我已经向django admin添加了预取,因为我注意到它正在为模型运行近1000个查询。
然而,预取似乎没有效果。据我所知,预取查询是否正确?
示例重复:
SELECT "sites_sitedata"."id", "sites_sitedata"."location", "sites_sitedata"."
...
FROM "sites_sitedata" WHERE "sites_sitedata"."id" = '7'
Duplicated 314 times.
0.09126367597049141%
0.24
Sel Expl
Connection: default
/itapp/itapp/circuits/models.py in __str__(88)
return '%s | %s | %s | %s ' % (self.site_data.location, \
电路供应商和电路类型也有重复的高级别
admin.py
class SiteSubnetsAdmin(admin.ModelAdmin):
search_fields = ['site_data','device_data','subnet','subnet_type','vlan_id','peer_desc']
list_display = ('site_data','device_data','subnet','subnet_type','vlan_id','peer_desc')
ordering = ('site_data','device_data',)
def get_queryset(self, request):
queryset = super(SiteSubnetsAdmin, self).get_queryset(request)
queryset = SiteSubnets.objects \
.prefetch_related(
Prefetch(
'circuit',
queryset=Circuits.objects.prefetch_related('site_data').prefetch_related('service_contacts').prefetch_related('circuit_type').prefetch_related('provider'),
),
) \
.prefetch_related('site_data') \
.prefetch_related('device_data') \
.prefetch_related('subnet_type')
return queryset
admin.site.register(SiteSubnets, SiteSubnetsAdmin)
subnets.models
class SiteSubnets(models.Model):
device_data = models.ForeignKey(DeviceData, verbose_name="Device", \
on_delete=models.PROTECT, blank=True, null=True)
site_data = models.ForeignKey(SiteData, verbose_name="Location", \
on_delete=models.PROTECT, blank=True, null=True)
subnet = models.GenericIPAddressField(protocol='IPv4', \
verbose_name="Subnet", blank=True, null=True)
subnet_type = models.ForeignKey(SubnetTypes, verbose_name="Subnet Type")
circuit = models.ForeignKey(Circuits, verbose_name="Link to circuit?", \
on_delete=models.PROTECT, blank=True, null=True)
vlan_id = models.IntegerField(verbose_name="Vlan ID", blank=True, null=True)
peer_desc = models.IntegerField(verbose_name="Peer description", blank=True, null=True)
class Meta:
verbose_name = "Site Subnets"
verbose_name_plural = "Site Subnets"
def __str__(self):
if self.device_data != None:
return '{0} - {1} - {2}'.format(self.site_data,self.device_data, self.subnet)
else:
return '{0} - {1}'.format(self.site_data, self.subnet)
circuits.models
class Circuits(models.Model):
site_data = models.ForeignKey(SiteData, verbose_name="Site", on_delete=models.PROTECT)
order_no = models.CharField(max_length=200, verbose_name="Order No")
expected_install_date = models.DateField()
install_date = models.DateField(blank=True, null=True)
circuit_type = models.ForeignKey(CircuitTypes, verbose_name="Circuit Type", on_delete=models.PROTECT)
circuit_preference = models.CharField(max_length=20, verbose_name="Circuit Preference", \
choices=settings.CIRCUIT_PREFERENCE, blank=True, null=True)
provider = models.ForeignKey(CircuitProviders, verbose_name="Circuit Provider", on_delete=models.PROTECT)
service_contacts = models.ForeignKey(ServiceContacts, on_delete=models.PROTECT)
subnet_mask = models.CharField(max_length=4, verbose_name="Subnet Mask", \
choices=settings.SUBNET_MASK_CHOICES, blank=True, null=True)
decommissioned = models.BooleanField(default=False, verbose_name="Decomission this circuit?")
active_link = models.BooleanField(default=False, verbose_name="Active Link?")
class Meta:
verbose_name = "Circuit Data"
verbose_name_plural = "Circuit Data"
permissions = (
("can_view_financial", "Can View Financial"),
("can_view_orders", "Can View Orders"),
)
def __str__(self):
return '%s | %s | %s | %s ' % (self.site_data.location, \
self.provider, self.circuit_type, self.ref_no)
修改 这是单独存储的方法吗?在相同的模型中还是需要在模型的外部?
def full_info(self):
return '{} | {} | {} | {}'.format(self.site_data.location, \
self.provider, self.circuit_type, self.ref_no)
def __str__(self):
return '{} | {} | {}'.format(self.provider, self.circuit_type, self.ref_no)
答案 0 :(得分:2)
您应该使用select_related
作为转发外键。 prefetch_related
预取反向外键和多对多关系:
# ...
queryset=Circuits.objects\
.select_related('site_data', 'service_contacts', 'circuit_type', 'provider'),
# ...
.select_related('site_data', 'device_data', 'subnet_type')
在__str__
中显示所有这些fk信息可能很麻烦,因为默认情况下在管理员的多个位置使用__str__
,例如更改列表,但也包括表单输入下拉列表。您可以显示相关对象的pk(如果信息足够),因为它存储在本地表中并且不会创建额外的查询:
def __str__(self):
return '%s | %s | %s | %s ' % (self.site_data_id, \
self.provider_id, self.circuit_type_id, self.ref_no)
答案 1 :(得分:1)
首先,我会尽量避免在__str__
方法中包含外键。它可能会导致大量的查询。保持__str__
方法尽可能简单,如果需要显示相关信息,则创建另一种方法并在必要时使用。
其次,我不确定Django是否支持在prefetch_related
对象中使用Prefetch
。在您的情况下,您甚至不需要使用prefetch_related
。对于外键,您可以使用select_related
,Django将进行连接以获取相关对象,而不是在单独的查询中预取它们。
queryset = SiteSubnets.objects.select_related(
'circuit', 'circuit__site_data', 'circuit__service_contacts', 'circuit__circuit_type', 'circuit__provider'
).select_related('site_data', 'device_data', subnet_type')
如果其他查询来自Django管理员中外键的选择字段,那么优化get_queryset
并不会有所帮助。您可以按照我的建议简化__str__
方法,深入研究Django管理员内部以尝试优化查询,使用ajax小部件来阻止加载所有选项,或者忍受大量查询。