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

如何在Django中缓存数据库表?解决重复查询性能问题

解决Django中重复数据库查询的缓存实现方案

针对无外键关联导致大量重复查询的问题,以下是几种具体的实现方案:

一、手动批量预取关联数据(推荐优先使用)

这种方法从根源减少查询次数,将关联表数据一次性查询后存入字典,序列化器直接从字典取值,完全避免重复查询。

修改视图代码:

class FileDownloaderSerializer(APIView):
    def get(self, request, **kwargs):
        filename = "All-users.csv"
        # 1. 查询所有用户数据
        datas = Userstable.objects.using(dbname).all()
        
        # 2. 批量预取所有需要的关联表数据,转成ID映射字典
        # 预取section表数据
        section_map = {
            s.id: s.sectionname 
            for s in section.objects.using(dbname).all()
        }
        # 预取department表数据(修正原代码中表名混淆问题)
        dept_map = {
            d.id: d.deptname 
            for d in department.objects.using(dbname).all()
        }
        
        # 3. 将映射字典传入序列化器上下文
        serializer = UserSerializer(
            datas, 
            context={'sector': sector, 'section_map': section_map, 'dept_map': dept_map}, 
            many=True
        )
        df = serializer.data

        # 优化文件操作:使用with语句自动关闭文件
        with open(filename, 'w') as f:
            df.to_csv(f, index=False, header=False)
        
        wrapper = FileWrapper(open(filename))
        response = HttpResponse(wrapper, content_type='text/csv')
        response['Content-Length'] = os.path.getsize(filename)
        response['Content-Disposition'] = f"attachment; filename={filename}"

        return response

修改序列化器代码:

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = Userstable
        fields = '__all__'
    
    section = serializers.SerializerMethodField()
    department = serializers.SerializerMethodField()

    def get_section(self, obj):
        # 从上下文字典直接取值,无需查询数据库
        return self.context['section_map'].get(obj.sectionid, '')

    def get_department(self, obj):
        return self.context['dept_map'].get(obj.deptid, '')

效果:原本500次查询会减少到3次(用户表1次、section表1次、department表1次),彻底解决重复查询问题。

二、使用Django缓存框架缓存单条关联数据

如果关联表数据更新频率低,可以用Django内置缓存将单条关联数据缓存起来,避免重复查询。

修改序列化器代码:

from django.core.cache import cache

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = Userstable
        fields = '__all__'
    
    section = serializers.SerializerMethodField()
    department = serializers.SerializerMethodField()

    def get_section(self, obj):
        # 加入数据库标识避免多库缓存冲突
        cache_key = f"section_{dbname}_name_{obj.sectionid}"
        # 先从缓存取数据
        section_name = cache.get(cache_key)
        if not section_name:
            # 缓存不存在时查询数据库并写入缓存
            section_obj = section.objects.using(dbname).get(pk=obj.sectionid)
            section_name = section_obj.sectionname
            # 设置缓存过期时间(例如1小时,可根据数据更新频率调整)
            cache.set(cache_key, section_name, timeout=3600)
        return section_name

    def get_department(self, obj):
        cache_key = f"dept_{dbname}_name_{obj.deptid}"
        dept_name = cache.get(cache_key)
        if not dept_name:
            dept_obj = department.objects.using(dbname).get(pk=obj.deptid)
            dept_name = dept_obj.deptname
            cache.set(cache_key, dept_name, timeout=3600)
        return dept_name

注意:需要先配置Django缓存(如Redis、Memcached或本地内存缓存),如果数据频繁更新,需在数据修改时主动删除对应缓存键,保证缓存一致性。

三、视图层全局内存缓存(单次请求复用)

如果只需要在生成CSV的单次请求内复用数据,可在视图初始化时加载所有关联表数据,序列化器直接调用,适合多表复杂场景:

视图代码示例:

class FileDownloaderSerializer(APIView):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 初始化时加载所有关联表数据到内存
        self.section_map = {s.id: s.sectionname for s in section.objects.using(dbname).all()}
        self.dept_map = {d.id: d.deptname for d in department.objects.using(dbname).all()}
        # 其他关联表同理扩展
        self.role_map = {r.id: r.rolename for r in Role.objects.using(dbname).all()}

    def get(self, request, **kwargs):
        filename = "All-users.csv"
        datas = Userstable.objects.using(dbname).all()
        
        serializer = UserSerializer(
            datas, 
            context={
                'sector': sector, 
                'section_map': self.section_map, 
                'dept_map': self.dept_map,
                'role_map': self.role_map
            }, 
            many=True
        )
        df = serializer.data

        with open(filename, 'w') as f:
            df.to_csv(f, index=False, header=False)
        
        wrapper = FileWrapper(open(filename))
        response = HttpResponse(wrapper, content_type='text/csv')
        response['Content-Length'] = os.path.getsize(filename)
        response['Content-Disposition'] = f"attachment; filename={filename}"

        return response

优势:单次请求内所有序列化逻辑复用内存中的数据,无额外数据库查询,性能最优。

长远建议

如果条件允许,建议补设数据库外键,之后可以使用Django的select_related或prefetch_related自动优化查询,代码会更简洁易维护。

内容的提问来源于stack exchange,提问作者user18489452

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 04:30:46