该问题可能与this one类似,但不是......
我有一个模型结构,如:
class Customer(models.Model):
....
class CustomerCompany(models.Model):
customer = models.ForeignKey(Customer)
type = models.SmallIntegerField(....)
我正在使用InlineModels
,并且有两种类型的CustomerCampany.type
。所以我为CustomerCompany
和ov覆盖InlineModelAdmin.queryset
class CustomerAdmin(admin.ModelAdmin):
inlines=[CustomerCompanyType1Inline, CustomerCompanyType2Inline]
class CustomerCompanyType1Inline(admin.TabularInline):
model = CustomerCompany
def queryset(self, request):
return super(CustomerCompanyType1Inline, self).queryset(request).filter(type=1)
class CustomerCompanyType2Inline(admin.TabularInline):
model = CustomerCompany
def queryset(self, request):
return super(CustomerCompanyType2Inline, self).queryset(request).filter(type=2)
所有内容都很好,但是要为InlineModelAdmin
添加新记录,我仍然需要在type
上显示CustomerCompany
的{{1}}字段,因为我无法覆盖AdminForm
的{{1}}方法,如:
save
使用信号也不是解决方案,因为我的信号InlineModelAdmin
将是相同的class CustomerCompanyType2Inline(admin.TabularInline):
model = CustomerCompany
def queryset(self, request):
return super(CustomerCompanyType2Inline, self).queryset(request).filter(type=2)
#Following override do not work
def save_model(self, request, obj, form, change):
obj.type=2
obj.save()
,因此我无法检测哪个sender
发送它以及Model
一定是......
有没有办法让我在保存之前设置InlineModelAdmin
字段?
答案 0 :(得分:27)
Alasdair的回答并没有错,但它有一些可能导致问题的痛点。首先,通过使用form
作为变量名循环遍历formset,实际上覆盖了传递给form
方法的值。这不是什么大不了的事,但既然你可以直接从formset中进行保存,那么最好这样做。其次,所有重要的formset.save_m2m()
都被排除在答案之外。实际Django docs推荐以下内容:
def save_formset(self, request, form, formset, change):
instances = formset.save(commit=False)
for instance in instances:
# Do something with `instance`
instance.save()
formset.save_m2m()
您将遇到的问题是save_formset
方法必须在父ModelAdmin
上而不是内联,并且从那里开始,无法知道哪个内联实际上是利用。如果你有一个带有两个“类型”的obj并且所有字段都是相同的,那么你应该使用代理模型,你实际上可以覆盖每个的save方法来自动设置合适的类型。
class CustomerCompanyType1(CustomerCompany):
class Meta:
proxy = True
def save(self, *args, **kwargs):
self.type = 1
super(CustomerCompanyType1, self).save(*args, **kwargs)
class CustomerCompanyType2(CustomerCompany):
class Meta:
proxy = True
def save(self, *args, **kwargs):
self.type = 2
super(CustomerCompanyType2, self).save(*args, **kwargs)
然后,您不需要对内联执行任何特殊操作。只需更改现有的内联管理类以使用适当的代理模型,所有内容都将自行排序。
答案 1 :(得分:5)
您可以覆盖save_formset
方法。你必须弄清楚formset
以某种方式表示的内联。
def save_formset(self, request, form, formset, change):
instances = formset.save(commit=False)
for instance in instances:
# Do something with `instance`
instance.save()
formset.save_m2m()
答案 2 :(得分:1)
使用save_formset时,其他答案是正确的。他们错过了检查当前保存的模型的方法。要做到这一点,你可以:
if formset.model == CustomerCompany:
# actions for specific model
这将使save_formset函数看起来像:(假设您只想覆盖特定模型的保存)
def save_formset(self, request, form, formset, change):
# if it's not the model we want to change
# just call the default function
if formset.model != CustomerCompany:
return super(CustomerAdmin, self).save_formset(request, form, formset, change)
# if it is, do our custom stuff
instances = formset.save(commit=False)
for instance in instances:
instance.type = 2
instance.save()
formset.save_m2m()
答案 3 :(得分:0)
对于需要在注册表中执行操作的情况,您需要在保存表单集之前进行操作。
def save_formset(self, request, form, formset, change):
for form in formset:
model = type(form.instance)
if not form["id"].initial and hasattr(model, "created_by"):
# craeted_by will not appear in the form dictionary because
# is read_only, but we can anyway set it directly at the yet-
# to-be-saved instance.
form.instance.created_by = request.user
super().save_formset(request, form, formset, change)
在这种情况下,当模型包含“created_by”字段时,我也会应用它(因为这是用于我在许多地方使用的 mixin,而不是用于特定模型)。
如果不需要,只需删除 and hasattr(model, "created_by")
部分。
在处理表单集时您可能会感兴趣的其他一些属性:
"""
The interesting fields to play with are:
for form in formset:
print("Instance str representation:", form.instance)
print("Instance dict:", form.instance.__dict__)
print("Initial for ID field:", form["id"].initial)
print("Has changed:", form.has_changed())
form["id"].initial will be None if it's a new entry.
"""
我希望我的挖掘对其他人有帮助! ^^