Django 管理员内联:select_related

问题描述

在带有模型的 Python 3.4.1 上使用 Django 1.8:

Using Django 1.8 on Python 3.4.1 with models:

class Product(models.Model):
    name = models.CharField(max_length=255)
    # some more fields here

    def __str__(self):
        return self.name


class PricedProduct(models.Model):
    product = models.ForeignKey(Product, related_name='prices')
    # some more fields here

    def __str__(self):
        return str(self.product)

class Coming(models.Model):
    # some unimportant fields here


class ComingProducts(models.Model):
    coming = models.ForeignKey(Coming)
    priced_product = models.ForeignKey(PricedProduct)
    # more unimportant fields

和下面的 admin.py:

and the following admin.py:

class ComingProductsInline(ForeignKeyCacheMixin, admin.TabularInline):
    model = ComingProducts


class ComingAdmin(admin.ModelAdmin):
    inlines = [ComingProductsInline]

当然,我在对数据库进行多次查询时遇到了问题:我对列表中的每个项目都有一个查询,对每一行都有一个查询.所以,有 100 个项目我得到 100 ^ 2 个查询.我已经用 缓存查询集解决了每一行的查询问题Django 表单中 ModelChoiceField 或 ModelMultipleChoiceField 的选择但我仍然对 str 方法有问题.我尝试了以下方法:

Of course, i have a problem with multiply queries to database: i have a query for each item in list and a query for each line. So, having 100 items i get 100 ^ 2 queries. I've solved the problem with queries for each line with Caching queryset choices for ModelChoiceField or ModelMultipleChoiceField in a Django form But i still having problem with str method. I've tried the following:

1) 将 prefetch_related 添加到 ComingAdmin:

1) adding prefetch_related to ComingAdmin:

def get_queryset(self, request):
    return super(ComingAdmin, self).get_queryset(request). 
    prefetch_related('products__product')

2) 将 select_related 添加到 ComingProductInline:

2) adding select_related to ComingProductInline:

def get_queryset(self, request):
    return super(ComingProductsInline, self).get_queryset(request). 
    select_related('priced_product__product')

3) 为内联定义自定义表单并将 select_related 添加到字段查询集:

3) Defining custom form for inline and adding select_related to field queryset:

 class ComingProductsInline(ForeignKeyCacheMixin, admin.TabularInline):
     model = ComingProducts
     form = ComingProductsAdminForm

 class ComingProductsAdminForm(ModelForm):
     def __init__(self, *args, **kwargs):
              super(ComingProductsAdminForm, self).__init__(args, kwargs)
              self.fields['priced_product'].queryset = PricedProduct.objects.all(). 
              select_related('product')

     class Meta:
         model = ComingProducts
         fields = '__all__'

4) 定义自定义表单集:

4) Defining a custom formset:

 class ComingProductsInline(ForeignKeyCacheMixin, admin.TabularInline):
     model = ComingProducts
     formset = MyInlineFormset

 class MyInlineFormset(BaseInlineFormSet):
     def __init__(self, data=None, files=None, instance=None,
             save_as_new=False, prefix=None, queryset=None, **kwargs):
        super(MyInlineFormset, self).__init__(data, files, instance,
                                          save_as_new, prefix, queryset, **kwargs)
        self.queryset = ComingProducts.objects.all(). 
        prefetch_related('priced_product__product')

5) 前 4 种方法的不同组合

5) Different combinations for previous 4 methods

没有任何帮助:每次调用 str 对 PricedProduct 都会使 Django 执行对 Product 表的查询.所有这些方法都在 stackoverflow 上提到过,但是它们处理了 ModelAdmin,并且对 Inline 没有帮助.我错过了什么?

And nothing helps: each call of str for PricedProduct makes Django to perform a query for Product table. All of these methods were mentioned on stackoverflow, but they treated ModelAdmin, and do not help with Inline. What do i miss?


解决方案

formset 解决方案确实对我有用,但方法略有不同:

The formset solution does work for me, but with a slightly different approach:

class MyInlineFormset(BaseInlineFormSet):
    def __init__(self, *args, **kwargs):
        super(MyInlineFormset, self).__init__(*args, **kwargs)
        self.queryset = self.queryset.prefetch_related('priced_product__product')

BaseInlineFormSet 类为您过滤查询集,您需要获取过滤后的查询集并添加预取.使用您的表单集实现(all() 查询集),您将获得不相关的 ComingProduct 对象,并且渲染时间可能太长.当它是过滤后的查询集时,它会非常快速地呈现.

The BaseInlineFormSet class filters the queryset for you, and you need to take that filtered queryset and add the prefetch. With your formset implementation (the all() queryset) you get unrelated ComingProduct objects and it probably takes much too long to render. When it's the filtered queryset it renders very quickly.

相关文章