首页 文章

Django admin inline:select_related

提问于
浏览
8

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

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:

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


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

当然,我遇到了对数据库进行多次查询的问题:我对列表中的每个项目进行查询,并对每一行进行查询 . 所以,有100个项目,我得到100 ^ 2个查询 . 我用Caching queryset choices for ModelChoiceField or ModelMultipleChoiceField in a Django form解决了每行查询的问题但是我仍然遇到 str 方法的问题 . 我尝试过以下方法:

1)将prefetch_related添加到ComingAdmin:

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

2)将select_related添加到ComingProductInline:

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

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

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)定义自定义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种方法的不同组合

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

3 回答

  • 0

    我目前正在处理类似的问题 . 我发现的内容记录在这个帖子中:Translatable Manytomany fields in admin generate many queries

    我做的一个重要观察是我的解决方案仅适用于Django 1.7x而不适用于1.8 . 完全相同的代码,与d1.7我有10 ^ 1的查询顺序,而新安装的d1.8我有10 ^ 4 .

  • 1

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

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

    BaseInlineFormSet类为您过滤查询集,您需要获取该过滤的查询集并添加预取 . 使用formset实现(all()查询集),您将获得无关的ComingProduct对象,渲染可能需要很长时间 . 当它是过滤后的查询集时,它会很快呈现 .

  • 6

    你会发现这种方法非常有用:

    project/admin.py

    from django.contrib import admin
    from django.contrib.admin.options import BaseModelAdmin
    from django.db.models.constants import LOOKUP_SEP
    
    
    class AdminBaseWithSelectRelated(BaseModelAdmin):
        """
        Admin Base using list_select_related for get_queryset related fields
        """
        list_select_related = []
    
        def get_queryset(self, request):
            return super(AdminBaseWithSelectRelated, self).get_queryset(request).select_related(*self.list_select_related)
    
        def form_apply_select_related(self, form):
            for related_field in self.list_select_related:
                splitted = related_field.split(LOOKUP_SEP)
    
                if len(splitted) > 1:
                    field = splitted[0]
                    related = LOOKUP_SEP.join(splitted[1:])
                    form.base_fields[field].queryset = form.base_fields[field].queryset.select_related(related)
    
    
    class AdminInlineWithSelectRelated(admin.TabularInline, AdminBaseWithSelectRelated):
        """
        Admin Inline using list_select_related for get_queryset and get_formset related fields
        """
    
        def get_formset(self, request, obj=None, **kwargs):
            formset = super(AdminInlineWithSelectRelated, self).get_formset(request, obj, **kwargs)
    
            self.form_apply_select_related(formset.form)
    
            return formset
    
    
    class AdminWithSelectRelated(admin.ModelAdmin, AdminBaseWithSelectRelated):
        """
        Admin using list_select_related for get_queryset and get_form related fields
        """
    
        def get_form(self, request, obj=None, **kwargs):
            form = super(AdminWithSelectRelated, self).get_form(request, obj, **kwargs)
    
            self.form_apply_select_related(form)
    
            return form
    
    
    class FilterWithSelectRelated(admin.RelatedFieldListFilter):
        list_select_related = []
    
        def field_choices(self, field, request, model_admin):
            return [
                (getattr(x, field.remote_field.get_related_field().attname), str(x))
                for x in self.get_queryset(field)
            ]
    
        def get_queryset(self, field):
            return field.remote_field.model._default_manager.select_related(*self.list_select_related)
    

    app/admin.py

    from django.contrib import admin
    
    from project.admin import AdminWithSelectRelated, AdminInlineWithSelectRelated, FilterWithSelectRelated
    from .models import FormaPago, Comprobante, ItemServicio, ItemBazar
    
    
    class ItemServicioInlineAdmin(AdminInlineWithSelectRelated):
        model = ItemServicio
    
        list_select_related = (
            'alumno_servicio__alumno__estudiante__profile',
            'alumno_servicio__servicio__grado',
            'comprobante__forma_pago',
        )
    
    
    class ItemBazarInlineAdmin(AdminInlineWithSelectRelated):
        model = ItemBazar
    
        list_select_related = (
            'alumno_item__alumno__estudiante__profile',
            'alumno_item__item__anio_lectivo',
            'comprobante__forma_pago',
        )
    
    
    class ComprobanteAdmin(AdminWithSelectRelated):
        list_display = ('__str__', 'total', 'estado', 'fecha_generado', 'forma_pago', 'tipo', )
        list_filter = ('estado', 'forma_pago', )
    
        list_select_related = ('forma_pago', )
        inlines = (ItemServicioInlineAdmin, ItemBazarInlineAdmin, )
    
    
    class AlumnoFilter(FilterWithSelectRelated):
        list_select_related = ('estudiante__profile', )
    
    
    class ItemServicioAdmin(AdminWithSelectRelated):
        list_display = ('nombre', 'alumno', 'monto_pagado', 'comprobante', )
        list_filter = (
            'alumno_servicio__alumno__seccion__grado',
            ('alumno_servicio__alumno', AlumnoFilter),
        )
    
        list_select_related = (
            'comprobante__forma_pago',
            'alumno_servicio__alumno__estudiante__profile',
            'alumno_servicio__alumno__seccion__grado',
            'alumno_servicio__servicio__grado',
        )
    
    
    class ItemBazarAdmin(AdminWithSelectRelated):
        list_display = ('nombre', 'alumno', 'monto_pagado', 'comprobante', )
        list_filter = (
            'alumno_item__alumno__seccion__grado',
            ('alumno_item__alumno', AlumnoFilter),
        )
    
        list_select_related = (
            'comprobante__forma_pago',
            'alumno_item__alumno__estudiante__profile',
            'alumno_item__alumno__seccion__grado',
            'alumno_item__item__anio_lectivo',
        )
    
    
    admin.site.register(FormaPago)
    admin.site.register(Comprobante, ComprobanteAdmin)
    admin.site.register(ItemServicio, ItemServicioAdmin)
    admin.site.register(ItemBazar, ItemBazarAdmin)
    

    我所要做的就是定义select_related字段,Custom AdminWithSelectRelatedAdminInlineWithSelectRelatedFilterWithSelectRelated 将它们用于Changelists,Changeforms甚至内联Formset .

    奇迹般有效 .

相关问题