24

这个问题可能看起来与这个问题相似但它不是......

我有一个模型结构,如:

class Customer(models.Model):
    ....

class CustomerCompany(models.Model):
    customer = models.ForeignKey(Customer)
    type = models.SmallIntegerField(....)

我正在使用InlineModels,并且有两种类型的CustomerCompany.type. 所以我为CustomerCompany和覆盖定义了两个不同的内联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,因为我无法覆盖类似的方法 :CustomerCompanyAdminFormsaveInlineModelAdmin

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发送它以及type必须是什么......

有没有办法让我type在保存之前设置字段?

4

4 回答 4

39

Alasdair 的回答没有错,但它有一些痛点可能会导致问题。form首先,通过使用变量名遍历表单集,您实际上覆盖了传递给 for 方法的值form。这不是什么大不了的事,但是由于您可以直接从表单集中进行保存而无需提交,因此最好这样做。其次,所有重要的事情都formset.save_m2m()被排除在答案之外。实际的Django 文档推荐以下内容:

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 具有两种“类型”并且所有字段都相同,那么您应该使用代理模型,并且您实际上可以覆盖每个的保存方法以自动设置适当的类型。

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)

然后,你不需要对你的内联做任何特别的事情。只需更改现有的内联管理类以使用其适当的代理模型,一切都会自行解决。

于 2011-11-28T15:58:09.267 回答
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()
于 2011-11-28T15:12:33.227 回答
4

在使用 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()
于 2017-09-12T11:58:34.380 回答
1

对于需要在注册表是新的情况下执行操作的情况,您需要在保存表单集之前执行此操作。

    def save_formset(self, request, form, formset, change):
        for form in formset:
            model = type(form.instance)
            if change 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.
        """

我希望我的挖掘可以帮助别人!^^

于 2021-07-14T11:02:47.680 回答