Django如何自动延迟主模型及关联模型的TextField与本地化字段
在Django中自动延迟主模型及关联模型的大文本字段
要实现自动排除主模型和关联模型中description、description_xx、text、text_xx这类大文本字段(仅在需要时加载),可以通过自定义QuerySet和Manager统一处理,无需每次查询手动调用defer()。
1. 编写通用字段匹配函数
先实现一个函数,自动识别模型中符合命名规则的TextField:
import re from django.db import models def get_deferred_fields(model): deferred_fields = [] # 匹配规则:匹配description/text开头,可选跟双字符语言后缀(如_uk、_ru) field_pattern = re.compile(r'^(description|text)(_\w{2})?$') for field in model._meta.get_fields(): # 只筛选TextField且字段名符合规则的字段 if isinstance(field, models.TextField) and field_pattern.match(field.name): deferred_fields.append(field.name) return deferred_fields
2. 自定义QuerySet实现自动延迟
创建自定义QuerySet,重写查询逻辑,自动对主模型和关联模型应用字段延迟:
class DeferredTextQuerySet(models.QuerySet): def defer_text_fields(self): # 延迟主模型的目标字段 queryset = self.defer(*get_deferred_fields(self.model)) # 自动处理一级关联模型(外键/一对一关联) for rel in self.model._meta.related_objects: rel_model = rel.related_model rel_deferred = get_deferred_fields(rel_model) # 转换为跨模型字段格式,如product__description queryset = queryset.defer(*[f"{rel.name}__{field}" for field in rel_deferred]) # 处理二级关联模型(如Product关联的Manufacturer) for sub_rel in rel_model._meta.related_objects: sub_rel_model = sub_rel.related_model sub_deferred = get_deferred_fields(sub_rel_model) queryset = queryset.defer(*[f"{rel.name}__{sub_rel.name}__{field}" for field in sub_deferred]) return queryset # 让默认的all()方法自动应用延迟规则 def all(self): return self.defer_text_fields()
3. 给模型绑定自定义Manager
将自定义QuerySet绑定到模型的Manager上,让模型的默认查询自动生效:
class DeferredTextManager(models.Manager): def get_queryset(self): return DeferredTextQuerySet(self.model, using=self._db) # 给你的模型绑定Manager class StoreItem(models.Model): store_id = models.IntegerField() product = models.ForeignKey('Product', on_delete=models.CASCADE) price_in = models.DecimalField(max_digits=10, decimal_places=2) price_out = models.DecimalField(max_digits=10, decimal_places=2) count = models.IntegerField() objects = DeferredTextManager() # 同样给Product和ProductManufacturer绑定Manager class Product(models.Model): name = models.CharField(max_length=255) name_uk = models.CharField(max_length=255) description = models.TextField() description_uk = models.TextField() manufacturer = models.ForeignKey('ProductManufacturer', on_delete=models.SET_NULL, null=True) objects = DeferredTextManager() class ProductManufacturer(models.Model): name = models.CharField(max_length=255) description = models.TextField() text = models.TextField() objects = DeferredTextManager()
4. 处理预取关联(prefetch_related)
如果使用prefetch_related查询多对多或反向关联,需要给预取的QuerySet也应用延迟规则:
# 示例:预取Product和Manufacturer时自动延迟大文本字段 StoreItem.objects.prefetch_related( models.Prefetch('product', queryset=Product.objects.defer_text_fields()), models.Prefetch('product__manufacturer', queryset=ProductManufacturer.objects.defer_text_fields()) ).all()
效果验证
执行如下查询:
StoreItem.objects.select_related('product', 'product__manufacturer').all()
生成的SQL会自动排除所有符合规则的大文本字段,与你期望的查询结构一致。
内容的提问来源于stack exchange,提问作者Igor Kremin
相关产品推荐
相关产品推荐

