Django REST API执行POST/PUT/DELETE后数据无法即时更新问题
问题描述
用Django、Redis和SQLite搭建REST API,目标是让Redis缓存GET请求的数据。但创建、更新或删除Category/Product实例后,数据库已同步变更(可在Django admin页面查看),后续GET请求却无法即时获取新数据,只有重启服务器后响应才会更新。不过创建同名分类会被拦截,说明API仍能正常读取数据库现有数据。
app/views.py
from django.shortcuts import render from django.core.cache import cache from rest_framework import viewsets, status from rest_framework.response import Response from rest_framework.decorators import action from .models import Category, Product from .serializers import CategorySerializer, ProductSerializer from django.conf import settings CACHE_TTL = getattr(settings, 'CACHE_TTL', 5) class CategoryViewSet(viewsets.ModelViewSet): queryset = Category.objects.all() serializer_class = CategorySerializer cache_key = 'store:categories' def list(self, request, *args, **kwargs): data = cache.get(self.cache_key) print("Cache GET for"+self.cache_key) if not data: # Serialize the queryset to JSON before caching data = list(self.queryset); cache.set(self.cache_key, data, CACHE_TTL) print(f"Cache set for"+self.cache_key) print('Cache retrieved') serializer = self.get_serializer(data, many=True) return Response(serializer.data) def create(self, request, *args, **kwargs): cache.delete('store:categories') print(f"Cache deleted for"+self.cache_key) return super().create(request, *args, **kwargs) def update(self, request, *args, **kwargs): cache.delete('store:categories') print(f"Cache deleted for"+self.cache_key) return super().update(request, *args, **kwargs) def destroy(self, request, *args, **kwargs): cache.delete('store:categories') print(f"Cache deleted for"+self.cache_key) return super().destroy(request, *args, **kwargs) class ProductViewSet(viewsets.ModelViewSet): queryset = Product.objects.all() serializer_class = ProductSerializer def list(self, request, *args, **kwargs): cache_key = 'store:products' data = cache.get(cache_key) if not data: data = list(self.queryset) cache.set(cache_key, data, CACHE_TTL) serializer = self.get_serializer(data, many=True) return Response(serializer.data) def create(self, request, *args, **kwargs): cache.delete('store:products') return super().create(request, *args, **kwargs) def update(self, request, *args, **kwargs): cache.delete('store:products') return super().update(request, *args, **kwargs) def destroy(self, request, *args, **kwargs): cache.delete('store:products') return super().destroy(request, *args, **kwargs)
app/serializers.py
from rest_framework import serializers from .models import Category, Product class CategorySerializer(serializers.ModelSerializer): class Meta: model = Category fields = '__all__' class ProductSerializer(serializers.ModelSerializer): class Meta: model = Product fields = '__all__'
调试输出
System check identified no issues (0 silenced). November 19, 2024 - 17:07:00 Django version 5.1.3, using settings 'storeapi.settings' Starting development server at http://127.0.0.1:8000/ Quit the server with CTRL-BREAK. Cache GET forstore:categories Cache set forstore:categories Cache retrieved [19/Nov/2024 17:07:03] "GET /api/categories/ HTTP/1.1" 200 296 Cache deleted forstore:categories [19/Nov/2024 17:07:16] "POST /api/categories/ HTTP/1.1" 201 42 Cache GET forstore:categories Cache set forstore:categories Cache retrieved [19/Nov/2024 17:07:20] "GET /api/categories/ HTTP/1.1" 200 296
问题原因
你缓存的是模型实例对象而非序列化后的原始数据。Django模型实例会通过pickle序列化存入缓存,但视图中定义的queryset = Category.objects.all()是惰性求值的,第一次调用list(self.queryset)时会将当时的实例加载到内存,后续即使数据库更新,内存中的旧实例不会自动同步,导致缓存刷新时仍用旧数据。
解决办法
方案1:缓存序列化后的数据(推荐)
修改list方法,直接缓存序列化后的字典数据,避免实例过期问题:
def list(self, request, *args, **kwargs): data = cache.get(self.cache_key) print("Cache GET for"+self.cache_key) if not data: # 重新查询最新数据并序列化 queryset = self.get_queryset() serializer = self.get_serializer(queryset, many=True) data = serializer.data cache.set(self.cache_key, data, CACHE_TTL) print(f"Cache set for"+self.cache_key) print('Cache retrieved') return Response(data)
方案2:每次缓存前重新查询
若要缓存实例,需用get_queryset()重新执行SQL查询,确保拿到最新数据:
def list(self, request, *args, **kwargs): data = cache.get(self.cache_key) print("Cache GET for"+self.cache_key) if not data: # 用get_queryset()重新获取最新查询集 data = list(self.get_queryset()) cache.set(self.cache_key, data, CACHE_TTL) print(f"Cache set for"+self.cache_key) print('Cache retrieved') serializer = self.get_serializer(data, many=True) return Response(serializer.data)
额外优化:用信号自动清除缓存
无需在每个视图方法手动删缓存,通过模型信号监听增删改事件自动清理:
- 在
app/models.py添加信号:
from django.db.models.signals import post_save, post_delete from django.dispatch import receiver from django.core.cache import cache from .models import Category, Product @receiver(post_save, sender=Category) @receiver(post_delete, sender=Category) def clear_category_cache(sender, **kwargs): cache.delete('store:categories') @receiver(post_save, sender=Product) @receiver(post_delete, sender=Product) def clear_product_cache(sender, **kwargs): cache.delete('store:products')
- 在
app/apps.py注册信号:
from django.apps import AppConfig class YourAppConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = 'app' def ready(self): import app.signals # 导入信号模块
内容的提问来源于stack exchange,提问作者dzakyadlh
相关产品推荐
相关产品推荐

