POST请求是否缓存在Django中?

时间:2019-12-17 11:36:07

标签: django graphql django-cache

我想根据发送给它的POST数据来缓存一些视图。

django.views.decorators.cache.cache_page装饰器会自动执行此操作,还是需要以某种方式对其进行调整?在后一种情况下,我该怎么办?

我正在尝试缓存GraphQL POST请求。

2 个答案:

答案 0 :(得分:3)

否,永远不会缓存POST响应:

    if request.method not in ('GET', 'HEAD'):
        request._cache_update_cache = False
        return None  # Don't bother checking the cache.

(来自django.middleware.cache中的FetchFromCacheMiddleware)。

您必须使用low-level cache API自己实现一些功能。缓存对POST请求的响应是最不寻常的,因为POST请求是要更改数据库中的内容,并且结果对于特定请求而言总是始终唯一。您必须考虑到底要缓存什么。

答案 1 :(得分:1)

我最终创建了一个自定义装饰器,该装饰器根据请求路径,查询参数和发布的数据来缓存响应:

# myproject/apps/core/caching.py

import hashlib
import base64
from functools import wraps

from django.core.cache import cache
from django.conf import settings


def make_hash_sha256(o):
    hasher = hashlib.sha256()
    hasher.update(repr(make_hashable(o)).encode())
    return base64.b64encode(hasher.digest()).decode()


def make_hashable(o):
    if isinstance(o, (tuple, list)):
        return tuple((make_hashable(e) for e in o))

    if isinstance(o, dict):
        return tuple(sorted((k,make_hashable(v)) for k,v in o.items()))

    if isinstance(o, (set, frozenset)):
        return tuple(sorted(make_hashable(e) for e in o))

    return o


def cache_get_and_post_requests(duration=600):
    def view_decorator(view):
        @wraps(view)
        def view_wrapper(request, *args, **kwargs):
            # TODO: make the key also dependable on the user or cookies if necessary
            cache_key = "-".join((
                settings.CACHE_MIDDLEWARE_KEY_PREFIX,
                make_hash_sha256((
                    request.path,
                    list(request.GET.items()),
                    list(request.POST.items()),
                    request.body,
                )),
            ))
            cached_response = cache.get(cache_key)
            if cached_response:
                return cached_response
            response = view(request, *args, **kwargs)
            cache.set(cache_key, response, duration)
            return response
        return view_wrapper
    return view_decorator

然后我可以像这样在URL配置中使用它:

# myproject/urls.py

from django.urls import path
from django.conf.urls.i18n import i18n_patterns
from graphene_django.views import GraphQLView
from myproject.apps.core.caching import cache_get_and_post_requests

urlpatterns = i18n_patterns(
    # …
    path("graphql/", cache_get_and_post_requests(60*5)(GraphQLView.as_view(graphiql=True))),
)