1. 问题背景
在 Django 管理界面中,用户可以使用内联模型来管理一对多的关系。但是,当一对多关系是多对多时,Django 提供的默认内联模型可能并不适合。例如,如果存在一个产品模型和一个发票模型,并且产品和发票之间是多对多的关系,那么在发票的管理界面中,Django 会显示一个表格,其中包含所有产品及其对应的复选框。这种形式的内联模型对于管理少量产品还可以接受,但是如果产品数量很多,那么这种内联模型就会非常不美观和难以使用。
2. 解决方案
为了解决这个问题,我们可以自定义多对多内联模型的显示方式。具体步骤如下:
-
创建一个新的内联模型类。这个类继承自
admin.TabularInline
或admin.StackedInline
。 -
在新的内联模型类中,重写
get_formset()
方法。这个方法负责返回一个表单集,表单集中的每个表单对应于内联模型中的一个对象。 -
在
get_formset()
方法中,使用formset_factory()
函数创建表单集。在formset_factory()
函数中,指定model
参数为内联模型的模型类,并指定fields
参数为内联模型中需要显示的字段。 -
在新的内联模型类中,重写
has_add_permission()
和has_change_permission()
方法。这两个方法分别负责判断用户是否有添加和修改内联模型对象 -
将新的内联模型类添加到
ModelAdmin
类中。在ModelAdmin
类的inlines
属性中,添加新的内联模型类。
下面是一个示例代码,演示了如何自定义多对多内联模型的显示方式:
python
from django.contrib import admin
from django.contrib.admin.utils import NestedObjects
class Product(models.Model):
name = models.TextField()
price = models.DecimalField(max_digits=10, decimal_places=2)
class Invoice(models.Model):
company = models.ForeignKey(Company)
customer = models.ForeignKey(Customer)
products = models.ManyToManyField(Product)
class InvoiceAdmin(admin.ModelAdmin):
# 使用 formset_factory() 函数创建表单集
def get_formset(self, request, obj=None, **kwargs):
formset_class = super().get_formset(request, obj, **kwargs)
formset_class = formset_factory(InvoiceProductFormset, extra=0,
fields=('product',))
return formset_class
# 重写 has_add_permission() 和 has_change_permission() 方法
def has_add_permission(self, request, obj=None):
return False
def has_change_permission(self, request, obj=None):
return False
class InvoiceProductFormset(admin.BaseInlineFormSet):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
NestedObjects(self)
# 重写 get_queryset() 方法
def get_queryset(self):
qs = super().get_queryset()
# 过滤掉已经被删除的对象
return qs.filter(is_deleted=False)
# 将自定义的内联模型类添加到 ModelAdmin 类中
admin.site.register(Invoice, InvoiceAdmin)
在上面的代码中,我们首先创建了一个新的内联模型类 InvoiceProductFormset
。这个类继承自 admin.BaseInlineFormSet
。然后,我们在 InvoiceProductFormset
类中重写了 get_formset()
方法、has_add_permission()
方法和 has_change_permission()
方法。最后,我们将 InvoiceProductFormset
类添加到 InvoiceAdmin
类中。