You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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)

额外优化:用信号自动清除缓存

无需在每个视图方法手动删缓存,通过模型信号监听增删改事件自动清理:

  1. 在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')
  1. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 04:17:28