与管理器相关的 Django 重复查询
Django duplicate queries with manager related
我正在优化我的 ORM 查询。我有两个应用程序,'app1' 和 'app2'。 'app2' 的一个 class 有一个指向 app1 的 class 的外键,如下所示:
#app1/models.py
class C1App1(WithDateAndOwner):
def get_c2_app2(self):
res = self.c2app2_set.all()
if res.count() > 0:
return res[0]
else:
return None
#app2/models.py
class C2App2(WithDateAndOwner):
c1app1 = models.ForeignKey("app1.C1App1")
is_ok = models.BooleanField(default=False)
现在我在管理页面中显示 C1App1 的所有实例的 C2App2:
#app1/admin.py
@admin.register(C1App1)
class C1App1Admin(admin.MyAdmin):
list_display = ("get_c2_app2")
list_select_related = ()
list_prefetch_related = ("c2app2_set",)
list_per_page = 10
prefetch_related
减少了这个查询:
SELECT ••• FROM `app2_c2app2` WHERE `app2_c2app2`.`c1app1_id` = 711
Duplicated 19 times.
至:
SELECT ••• FROM `app2_c2app2` WHERE `app2_c2app2`.`c1app1_id` IN (704, 705, 706, 707, 708, 709, 710, 711, 702, 703) ORDER BY `app2_c2app2`.`id` DESC
而且很好。现在,如果我想过滤 C2App2 属性 'is_ok':
的查询
#app1/models.py
class C1App1(WithDateAndOwner):
def get_c2_app2(self):
res = self.c2app2_set.filter(is_ok=False)
if res.count() > 0:
return res[0]
else:
return None
我还有这个预取查询:
SELECT ••• FROM `c2app2_set` WHERE `app2_c2app2`.`c1app1_id` IN (704, 705, 706, 707, 708, 709, 710, 711, 702, 703) ORDER BY `app2_c2app2`.`id` DESC
但是每个显示的 C1App1 实例都复制了那个 (10)。 :
SELECT ••• FROM `app2_c2app2` WHERE (`app2_c2app2`.`c1app1_id` = 711 AND `app2_c2app2`.`is_ok` = 1)
Duplicated 13 times.
实际上,对于显示的 10 个 ID 中的 3 个,查询也再次重复,这导致了那 13 个重复的查询。我该怎么做才能不重复这些查询?看来 prefetch_related 在这里不再有用了。
prefetch_related 仅在您使用 .all()
时有效。
如果您应用任何其他转换,如 .filter()
,将进行新的数据库查询。这是因为 prefetch_related
简单地缓存了列表中的所有相关实例,因此 Django 无法对列表执行 filter()
。要解决您的问题,您应该使用 Prefetch 对象。
您可以将 queryset
参数传递给它。因此,不要使用 list_prefetch_related,而是在您的管理 class.
中覆盖 get_queryset
方法
def get_queryset(*args, **kwargs):
qs = super().get_queryset(*args, **kwargs)
qs = qs.prefetch_related(Prefetch('c2app2_set', queryset=C2App2.objects.filter(is_ok=False)))
return qs
和
class C1App1(WithDateAndOwner):
def get_c2_app2(self):
res = self.c2app2_set.all()
if res.count() > 0:
return res[0]
else:
return None
我正在优化我的 ORM 查询。我有两个应用程序,'app1' 和 'app2'。 'app2' 的一个 class 有一个指向 app1 的 class 的外键,如下所示:
#app1/models.py
class C1App1(WithDateAndOwner):
def get_c2_app2(self):
res = self.c2app2_set.all()
if res.count() > 0:
return res[0]
else:
return None
#app2/models.py
class C2App2(WithDateAndOwner):
c1app1 = models.ForeignKey("app1.C1App1")
is_ok = models.BooleanField(default=False)
现在我在管理页面中显示 C1App1 的所有实例的 C2App2:
#app1/admin.py
@admin.register(C1App1)
class C1App1Admin(admin.MyAdmin):
list_display = ("get_c2_app2")
list_select_related = ()
list_prefetch_related = ("c2app2_set",)
list_per_page = 10
prefetch_related
减少了这个查询:
SELECT ••• FROM `app2_c2app2` WHERE `app2_c2app2`.`c1app1_id` = 711
Duplicated 19 times.
至:
SELECT ••• FROM `app2_c2app2` WHERE `app2_c2app2`.`c1app1_id` IN (704, 705, 706, 707, 708, 709, 710, 711, 702, 703) ORDER BY `app2_c2app2`.`id` DESC
而且很好。现在,如果我想过滤 C2App2 属性 'is_ok':
的查询#app1/models.py
class C1App1(WithDateAndOwner):
def get_c2_app2(self):
res = self.c2app2_set.filter(is_ok=False)
if res.count() > 0:
return res[0]
else:
return None
我还有这个预取查询:
SELECT ••• FROM `c2app2_set` WHERE `app2_c2app2`.`c1app1_id` IN (704, 705, 706, 707, 708, 709, 710, 711, 702, 703) ORDER BY `app2_c2app2`.`id` DESC
但是每个显示的 C1App1 实例都复制了那个 (10)。 :
SELECT ••• FROM `app2_c2app2` WHERE (`app2_c2app2`.`c1app1_id` = 711 AND `app2_c2app2`.`is_ok` = 1)
Duplicated 13 times.
实际上,对于显示的 10 个 ID 中的 3 个,查询也再次重复,这导致了那 13 个重复的查询。我该怎么做才能不重复这些查询?看来 prefetch_related 在这里不再有用了。
prefetch_related 仅在您使用 .all()
时有效。
如果您应用任何其他转换,如 .filter()
,将进行新的数据库查询。这是因为 prefetch_related
简单地缓存了列表中的所有相关实例,因此 Django 无法对列表执行 filter()
。要解决您的问题,您应该使用 Prefetch 对象。
您可以将 queryset
参数传递给它。因此,不要使用 list_prefetch_related,而是在您的管理 class.
get_queryset
方法
def get_queryset(*args, **kwargs):
qs = super().get_queryset(*args, **kwargs)
qs = qs.prefetch_related(Prefetch('c2app2_set', queryset=C2App2.objects.filter(is_ok=False)))
return qs
和
class C1App1(WithDateAndOwner):
def get_c2_app2(self):
res = self.c2app2_set.all()
if res.count() > 0:
return res[0]
else:
return None