Django 非常大的反向外键查询

Django very large reverse ForeignKey query

我正在使用 Django admin 根据对象的反向 ForeignKey 对象字段值过滤对象。 App 有一个 ForeignKeyContact,我正在尝试通过 Django 管理中的 Apps 类别过滤 Contacts。问题是查询非常大,我收到超时错误。大约有 30 万个联系人对象和大约 100 万个应用程序。 Django 管理中每页有 500 个结果。添加了数据库索引和 prefetch_related。我还应该做些什么来优化 Django 管理?我正在使用 sqlite 数据库。

代码:

class App(models.Model):
    contact = models.ForeignKey('Contact', related_name='apps', null=True)
    category = models.TextField(blank=True, null=True, db_index=True)
    store = models.IntegerField(choices=STORE_TYPES, db_index=True)

    class Meta:
        index_together = [
            ['category', 'store'],
        ]

# admin:

class ContactAdmin(admin.ModelAdmin):
    list_filter = (
        AppCategory,
    )

    def queryset(self, request):
        return super(ContactAdmin, self).queryset(request).prefetch_related(
            'apps',
            'to_contact',
        )

# the main list_filter that is causing troubles:

class AppCategory(admin.SimpleListFilter):
    title = 'app category'
    parameter_name = 'app_category'

def lookups(self, request, modelAdmin):

    return [
        ('Action', 'Action'),
        ('Adventure', 'Adventure'),
        ('Arcade', 'Arcade'),
        ('Board', 'Board'),
        ('Books', 'Books'),
        ('Books & Reference', 'Books & Reference'),
        ('Business', 'Business'),
        ('Card', 'Card'),
        ('Casino', 'Casino',),
        ('Casual', 'Casual'),
        ('Catalogs', 'Catalogs'),
        ('Comics', 'Comics'),
        ('Communication', 'Communication'),
        ('Education', 'Education'),
        ('Educational', 'Educational'),
        ('Entertainment', 'Entertainment'),
        ('Family', 'Family'),
        ('Finance', 'Finance'),
        ('Food & Drink', 'Food & Drink'),
        ('Games', 'Games'),
        ('Health & Fitness', 'Health & Fitness'),
        ('Libraries & Demo', 'Libraries & Demo'),
        ('Lifestyle', 'Lifestyle'),
        ('Media & Video', 'Media & Video'),
        ('Medical', 'Medical'),
        ('Music', 'Music'),
        ('Music & Audio', 'Music & Audio'),
        ('Navigation', 'Navigation'),
        ('News', 'News'),
        ('News & Magazines', 'News & Magazines'),
        ('Personalization', 'Personalization'),
        ('Photo & Video', 'Photo & Video'),
        ('Photography', 'Photography'),
        ('Productivity', 'Productivity'),
        ('Puzzle', 'Puzzle'),
        ('Racing', 'Racing'),
        ('Reference', 'Reference'),
        ('Role Playing', 'Role Playing'),
        ('Shopping', 'Shopping'),
        ('Simulation', 'Simulation'),
        ('Social', 'Social'),
        ('Social Networking', 'Social Networking'),
        ('Sports', 'Sports'),
        ('Strategy', 'Strategy'),
        ('Tools', 'Tools'),
        ('Transportation', 'Transportation'),
        ('Travel', 'Travel'),
        ('Travel & Local', 'Travel & Local'),
        ('Trivia', 'Trivia'),
        ('Utilities', 'Utilities'),
        ('Weather', 'Weather'),
        ('Word', 'Word')
    ]

def queryset(self, request, queryset):
    if not self.value():
        return queryset
    else:
        qs = queryset.filter(
            apps__category=self.value()
        )
        return qs

我找到了答案。如果您过滤一个反向 ForeignKey 关系(在本例中为 apps__category),一切正常。但我忘了说,我在 Django 管理页面的右侧组合了两个过滤器——另一个也是反向 ForeignKey - apps__store。当您这样做时,会执行两个完全相同的 INNER JOIN,这会导致服务器超时。当我单独使用这些过滤器时,一切正常。

参见:https://code.djangoproject.com/ticket/16554