Django:如何在基于类的视图中缓存get_object()中的对象?

时间:2015-04-10 20:54:55

标签: session caching django-class-based-views django-1.7

我一直在苦苦挣扎数小时:我无法找到一种正确的方法来缓存对象查询集结果( object = queryset.get())以避免重新使用 - 在每个视图请求上访问数据库。

这是我当前的(简化)代码,正如您所看到的,我重写了get_object()以添加一些额外的数据(不仅是今天的变量),检查对象是否在会话中并将对象添加到会话。

views.py

from myapp import MyModel
from django.core.cache.utils import make_template_fragment_key
from django.views.generic import DetailView

class myClassView(DetailView):
    model = MyModel

    def get_object(self,queryset=None):

        if queryset is None:
            queryset = self.get_queryset()
        pk = self.kwargs.get(self.pk_url_kwarg, None) 
        if pk is not None:
            queryset = queryset.filter(pk=pk) 
        else:
            raise AttributeError("My error message.")
        try:
            today = datetime.today().strftime('%Y%m%d')
            cache_key = make_template_fragment_key('some_name', [pk, today])

            if cache.has_key(cache_key):
                object = self.request.session[cache_key]
                return object
            else:
                object = queryset.get()
                object.id = my_id
                object.today = today

                # Add object to session
                self.request.session[cache_key] = object

        except queryset.model.DoesNotExist:
            raise Http404("Error 404")
        return object

以上只有在我添加以下内容时才有效:

settings.py

SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'

但是我不喜欢这个黑客,因为对于Django 1.6和更新的版本来说它并不安全,因为根据How To Use Sessions (Django 1.7 documents)

  

如果SECRET_KEY没有保密并且你正在使用PickleSerializer,这可能导致任意远程代码执行

如果我没有添加SESSIONS_SERIALIZER行,我会得到一个" django object is not JSON serializable"错误。但是,在其他地方,我的代码中断了,并且在尝试从会话中提取数据时出现KeyError错误。解决了这个问题,将我的字符串键转换为整数。在更改设置文件之前,当请求会话数据时,Django会自动将str键转换为整数。

因此,考虑到此会话序列化程序安全问题,我更喜欢其他选项。所以我阅读了herehere关于缓存get_object()的内容,但我还是不知道如何将其纳入我的get_object()位。我试过..

if cache.has_key(cache_key):
    self._object = super(myClassView,self).get_object(queryset=None) 
    return self._object

......但它失败了。这似乎是迄今为止最好的解决方案。但是我如何在我的代码中实现它?或者,有更好的主意吗?我全都耳朵。谢谢!

1 个答案:

答案 0 :(得分:1)

您应该退后一步并重新评估情况。你想要实现什么目标? get_object是在详细视图中调用的方法,用于从数据库访问一个特定对象。 如果在第一次对象在Queryset中失效并缓存时访问此方法。 为了缓存get_queryset方法,您需要一个良好的缓存后端,如Redis或Memcached,以便您可以执行简单的直写缓存操作:

if cache.has_key(cache_key):
   object = cache.get(cache_key)
   return object
else:
   object = queryset.get(pk=pk)
   cache.set(cache_key,object)
   return object

请注意,django对象在缓存后端中序列化,并在反序列化时作为对象检索。 这种方法只是一个起点。您在第一次未命中时缓存该对象。 您还可以添加post_save,post_update信号,以便在每次保存或更新模型时将对象保存在缓存中:

@receiver(post_save, sender=MyModel)
@receiver(post_delete, sender=MyModel)
def add_MyModel_to_cache(sender, **kwargs):
    object = kwargs['instance']
    cache.set(cache_key,object)

您必须仔细检查要缓存的内容以及何时容易误判请求