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

如何通过预取或缓存无关表优化Django的@property方法

优化方案

方案1:将ReferenceZscoreData加载到全局内存字典

由于该表每年仅更新一次,可将所有数据预加载到内存字典中,彻底避免重复查询数据库。

  • 实现步骤:
    1. 在项目工具模块(比如utils/zscore.py)中编写缓存加载函数:
    from .models import ReferenceZscoreData
    
    _ZSCORE_DATA_CACHE = None
    
    def get_zscore_data_cache():
        global _ZSCORE_DATA_CACHE
        if _ZSCORE_DATA_CACHE is None:
            # 一次性拉取所有数据,构建(sex, age)为键的映射字典
            _ZSCORE_DATA_CACHE = {
                (item.sex, item.age): item
                for item in ReferenceZscoreData.objects.all()
            }
        return _ZSCORE_DATA_CACHE
    
    def clear_zscore_data_cache():
        global _ZSCORE_DATA_CACHE
        _ZSCORE_DATA_CACHE = None
    
    1. 修改ChildBmiCheck的zscore属性:
    from utils.zscore import get_zscore_data_cache
    
    @property
    def zscore(self):
        if self.child_was_present is True:
            age = self.child.age * 12  # 转换为月单位
            gender = self.child.gender
            zscore_cache = get_zscore_data_cache()
            matching_data = zscore_cache.get((gender, age))
            return self.calculate_z_score(matching_data) if matching_data else None
        else:
            return None
    
    1. 当ReferenceZscoreData更新后,调用clear_zscore_data_cache()清空缓存,确保新数据生效(可在更新数据的管理命令或后台视图中触发)。

方案2:使用Django缓存框架缓存单条数据

利用Django自带的缓存系统,为每个(sex, age)组合设置长过期时间(比如1年),首次查询后直接从缓存读取。

  • 实现步骤:
    修改ChildBmiCheck的zscore属性:
    from django.core.cache import cache
    
    @property
    def zscore(self):
        if self.child_was_present is True:
            age = self.child.age * 12  # 转换为月单位
            gender = self.child.gender
            cache_key = f"zscore_ref_{gender}_{age}"
            matching_data = cache.get(cache_key)
            
            if not matching_data:
                try:
                    matching_data = ReferenceZscoreData.objects.filter(sex=gender).get(age=age)
                    # 设置缓存有效期为1年(31536000秒)
                    cache.set(cache_key, matching_data, 31536000)
                except ReferenceZscoreData.DoesNotExist:
                    return None
            
            return self.calculate_z_score(matching_data)
        else:
            return None
    
    注意:若使用数据库/文件缓存,需确保ReferenceZscoreData对象可序列化;使用Redis等内存缓存则无需额外处理。数据更新时,可批量删除相关缓存键。

方案3:视图层批量预取所需数据

在DRF视图中,先收集当前请求所有需要的(sex, age)组合,批量查询ReferenceZscoreData后手动映射到每个ChildBmiCheck实例,彻底消除N+1查询问题。

  • 实现步骤:
    1. 自定义序列化器和视图:
    from rest_framework import serializers, viewsets
    from .models import ChildBmiCheck, ReferenceZscoreData
    
    class ChildBmiCheckSerializer(serializers.ModelSerializer):
        zscore = serializers.SerializerMethodField()
    
        def get_zscore(self, obj):
            if not obj.child_was_present:
                return None
            # 从上下文获取预取的映射数据
            zscore_data_map = self.context.get('zscore_data_map', {})
            key = (obj.child.gender, obj.child.age * 12)
            matching_data = zscore_data_map.get(key)
            return obj.calculate_z_score(matching_data) if matching_data else None
    
    class ChildBmiCheckViewSet(viewsets.ModelViewSet):
        serializer_class = ChildBmiCheckSerializer
    
        def get_queryset(self):
            # 先关联查询Child表,避免额外查询
            queryset = ChildBmiCheck.objects.select_related('child').all()
            # 收集所有需要的(sex, age)组合
            required_keys = set()
            for bmi_check in queryset:
                if bmi_check.child_was_present:
                    age = bmi_check.child.age * 12
                    required_keys.add((bmi_check.child.gender, age))
            # 批量查询ReferenceZscoreData
            zscore_data = ReferenceZscoreData.objects.filter(
                sex__in=[k[0] for k in required_keys],
                age__in=[k[1] for k in required_keys]
            )
            # 构建映射字典
            zscore_data_map = {(item.sex, item.age): item for item in zscore_data}
            # 将映射字典传入序列化器上下文
            self.serializer_context['zscore_data_map'] = zscore_data_map
            return queryset
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 15:40:04