在 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 choice for ModelChoiceField or ModelMultipleChoiceField in a Django form 解决了每行查询的问题 但我对 str 方法仍然有问题。我尝试过以下方法:
1) 添加 prefetch_lated 到 ComingAdmin:
def get_queryset(self, request):
return super(ComingAdmin, self).get_queryset(request). \
prefetch_related('products__product')
2) 将 select_lated 添加到 ComingProductInline:
def get_queryset(self, request):
return super(ComingProductsInline, self).get_queryset(request). \
select_related('priced_product__product')
3)定义内联自定义表单并添加 select_lated 到字段查询集:
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) 定义自定义表单集:
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 没有帮助。我想念什么?
你会发现这种方法非常有用:
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_lated 字段,自定义
AdminWithSelectRelated
、AdminInlineWithSelectRelated
和 FilterWithSelectRelated
将它们用于变更列表、变更表单,甚至内联表单集。
就像魅力一样。
表单集解决方案确实对我有用,但方法略有不同:
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 对象,并且可能需要很长时间才能呈现。当它是过滤后的查询集时,它渲染得非常快。
受@helpse回答的启发,如果您只想覆盖单个管理内联的查询集,您还可以执行以下操作:
class ComingProductsInline(admin.TabularInline):
model = ComingProducts
def get_formset(self, request, obj=None, **kwargs):
formset = super(ComingProductsInline, self).get_formset(request, obj, **kwargs)
queryset = formset.form.base_fields["priced_product"].queryset
queryset = queryset.select_related("product")
formset.form.base_fields["priced_product"].queryset = queryset
return formset
对于大多数情况来说这可能就足够了。
我目前正在解决类似的问题。我发现的内容记录在这个线程中:管理中的可翻译多位字段生成许多查询
我所做的一个重要观察是,我的解决方案仅适用于 Django 1.7x,不适用于 1.8。完全相同的代码,对于 d1.7,我有 10^1 个查询的顺序,而在新安装的 d1.8 中,我有 10^4 个查询。
我和@juntatalor有同样的现象,
__str__()
正在一一进行查找。
我试图覆盖表单集的初始化,但是当添加表单模型的实例时,查询集是空的 - 它只会有 edit 视图的实例。
因此,根据@5parkp1ug的推荐,我使用了formfield_for_foreignkey(在Django 3.2中):
对于这种情况,我的代码相当于:
class ComingProductsInline(admin.TabularInline):
model = ComingProducts
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name = "priced_product" and db_field.column == "product_id":
kwargs["queryset"] = PricedProduct.objects.select_related("product")
return super().formfield_for_foreignkey(db_field, request, **kwargs)