【问题标题】:Django: How do I cache object from get_object() in a class-based view?Django:如何在基于类的视图中缓存来自 get_object() 的对象?
【发布时间】:2015-04-10 20:54:55
【问题描述】:

我已经为此苦苦挣扎了好几个小时:我只是想不出一个合适的方法来缓存对象查询集结果 (object = queryset.get()) 以避免重新命中每个视图请求的数据库。

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

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'

但我不喜欢这种 hack,因为它对 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

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

【问题讨论】:

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


    【解决方案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)
    

    你必须仔细检查你想要缓存的内容和时间,因为很容易误判请求

    【讨论】:

    • 谢谢,法尼斯!目前我正在安装和学习基本的 Redis。我会尽快回复您,给您反馈并接受您的回答。
    猜你喜欢
    • 2011-10-10
    • 1970-01-01
    • 2011-12-12
    • 2020-01-22
    • 1970-01-01
    • 1970-01-01
    • 2012-09-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多