我有一个包含3个ForeignKey字段的简单模型。
class Car(models.Model):
wheel = models.ForeignKey('Wheel', related_name='wheels')
created = models.DateTimeField(auto_now_add=True)
max_speed = models.PositiveSmallIntegerField(null=True)
dealer = models.ForeignKey('Dealer')
category = models.ForeignKey('Category')
对于django admin中的列表视图,我得到4个查询。其中一个是带有3个INNER JOINS的SELECT。那个查询是慢的方法。用STRAIGHT_JOIN替换INNER JOIN将解决问题。有没有办法在评估之前修补管理生成的查询?
答案 0 :(得分:4)
我已经为Django ORM实现了INNER JOIN的修复,如果使用INNER JOIN进行订购,它将使用STRAIGHT_JOIN。我和Django核心开发者谈过,我们决定暂时将其作为一个单独的后端。所以你可以在这里查看:https://pypi.python.org/pypi/django-mysql-fix
但是,还有另外一种解决方法。使用James的答案中的片段,但将select_related替换为:
qs = qs.select_related('').prefetch_related('wheel', 'dealer', 'category')
它将取消INNER JOIN并使用4个单独的查询:1用于获取汽车,另外3个用car_id
IN(...)。
<强>更新强> 我找到了另外一个解决方法。在ForeignKey字段中指定null = True后,Django将使用LEFT OUTER JOIN而不是INNER JOIN。在这种情况下,LEFT OUTER JOIN在没有性能问题的情况下工作,但您可能还面临其他我尚未发现的问题。
答案 1 :(得分:3)
您可以指定list_select_related = ()
以防止django使用内部联接:
class CarAdmin(admin.ModelAdmin):
list_select_related = ()
答案 2 :(得分:2)
你可以覆盖
def changelist_view(self, request, extra_context=None):
管理类中的方法继承自ModelAdmin
类
答案 3 :(得分:1)
好的,我找到了修补管理员生成的查询的方法。这很丑,但似乎有用:
class CarChangeList(ChangeList):
def get_results(self, request):
"""Override to patch ORM generated SQL"""
super(CarChangeList, self).get_results(request)
original_qs = self.result_list
sql = str(original_qs.query)
new_qs = Car.objects.raw(sql.replace('INNER JOIN', 'STRAIGHT_JOIN'))
def patch_len(self):
return original_qs.count()
new_qs.__class__.__len__ = patch_len
self.result_list = new_qs
class CarAdmin(admin.ModelAdmin):
list_display = ('wheel', 'max_speed', 'dealer', 'category', 'created')
def get_changelist(self, request, **kwargs):
"""Return custom Changelist"""
return CarChangeList
admin.site.register(Rank, RankAdmin)
答案 4 :(得分:1)
我在Django管理员(版本1.4.9)中遇到了同样的问题,当MySQL支持时,相当简单的管理员列表页面非常慢。
在我的情况下,如果ChangeList.get_query_set()
中的任何字段很多,则select_related()
方法会向查询集添加过于宽泛的全局list_display
一对一的关系。对于一个合适的数据库(咳嗽PostgreSQL咳嗽),这不会是一个问题,但是对于MySQL而言,只需要通过这种方式触发一些连接。
我发现最干净的解决方案是将全局select_related()
指令替换为更有针对性的指令,该指令仅连接真正必要的表。通过使用显式关系名称调用select_related()
,这很容易做到。
这种方法可能最终会为多个后续查询交换数据库内连接,但如果MySQL在大型查询中遇到窒息,那么很多小的可能会更快。
这就是我所做的,或多或少:
from django.contrib.admin.views.main import ChangeList
class CarChangeList(ChangeList):
def get_query_set(self, request):
"""
Replace a global select_related() directive added by Django in
ChangeList.get_query_set() with a more limited one.
"""
qs = super(CarChangeList, self).get_query_set(request)
qs = qs.select_related('wheel') # Don't join on dealer or category
return qs
class CarAdmin(admin.ModelAdmin):
def get_changelist(self, request, **kwargs):
return CarChangeList
答案 5 :(得分:1)
我对MySQL的管理查询速度很慢,发现最简单的解决方案是将STRAIGHT_JOIN添加到查询中。我想出了一种方法,可以将其添加到QuerySet
,而不是被迫转到.raw()
,后者无法与管理员合作,并将其作为{{3的一部分}}。然后你可以:
def get_queryset(self, request):
qs = super(MyAdmin, self).get_queryset(request)
return qs.straight_join()