【发布时间】:2020-04-09 21:49:42
【问题描述】:
我想根据发送给它的 POST 数据缓存一些视图。
django.views.decorators.cache.cache_page 装饰器会自动执行此操作,还是我需要以某种方式对其进行调整?在后一种情况下,我该怎么做?
我正在尝试缓存 GraphQL POST 请求。
【问题讨论】:
标签: django graphql django-cache
我想根据发送给它的 POST 数据缓存一些视图。
django.views.decorators.cache.cache_page 装饰器会自动执行此操作,还是我需要以某种方式对其进行调整?在后一种情况下,我该怎么做?
我正在尝试缓存 GraphQL POST 请求。
【问题讨论】:
标签: django graphql django-cache
不,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 请求旨在更改数据库中的内容,并且结果对于特定请求始终是唯一的。您必须考虑到底要缓存什么。
【讨论】:
django.utils.cache.get_cache_key 中构建缓存键。这将使您了解 url 如何与标头结合以生成请求的唯一缓存键。您必须向其中添加帖子数据。
我最终创建了一个自定义装饰器,它根据请求路径、查询参数和发布的数据缓存响应:
# 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))),
)
【讨论】: