覆盖Django InlineModelAdmin上的保存

时间:2011-11-28 11:05:13

标签: django django-modeladmin

该问题可能与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字段?

4 个答案:

答案 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.
        """

我希望我的挖掘对其他人有帮助! ^^