首页 > 解决方案 > 无法在视图中编辑 django 表单 empty_form 的字段

问题描述

我正在使用 Django BaseFormset 中的 empty_forms 以便将内联表单动态附加到父级。适用于编辑/创建视图。

然而,现在我需要从本质上实现一个 DuplicateView。我基本上使用的是 CreateView,在 get_context_data() 中对其进行调整,以使用要复制的对象填充它。像这样的东西:

def get_context_data(self, **kwargs):
    ctx = super().get_context_data(**kwargs)
    self.object = self.model.objects.get(pk=self.kwargs["pk"])
    logger.info(f"ProduitDuplicateView.get_context_data - object id/code: {self.object.pk} {self.object.no_prod}")
    if self.request.POST:
        # code
    else:
        ctx["form"] = ProduitForm(instance=self.object)
        fourprodfrmset = FourProdFormSet(instance=self.object)
        casefrmset = CaseFormSet(instance=self.object)

我确实需要将实例设置为 self.object,因为我想填充现有的内联表单,以便使用相关数据复制基础对象。但是,该内联表单包含一个指向父对象的外键,默认设置为父对象的 pk 值。对于表单实例,我将其设置为 none,如下所示:

    for f in formset:
        try:
            f.initial.pop("fk_produit")
            f.fields["fk_produit"].initial = None
        except KeyError as ex:
            logger.info(f"There was no fk_produit in form.initial for this form of the formset")

这适用于表单实例。但是,我还需要对 empty_form 执行相同操作 - 否则,当我将 emtpy_form 附加到我的内联表单集时,它会设置为基础对象的 pk。这意味着以这种方式保存新的内联表单会导致错误(类似于“内联表单的 id 与父表单的 id 不同”)。

所以我尝试这样设置:

for formset in [fourprodfrmset, casefrmset]:
    try:
        formset.empty_form.fields["fk_produit"].initial = None
    except KeyError as ex:
        logger.info(f"Couldn't remove fk_produit initial value in formset empty_form")

但是,由于某种原因,它没有任何效果 - 如果我在方法结束时对其进行调试,formset 的 emtpy_form 字段“fk_produit”仍然具有其基础值(例如基础对象的值)。

那么.....如何修改empty_form字段的值?最好在视图中,因为那是我处理所有那些恶作剧的地方。

当然,我想我可以按照模板来做,但这需要更多的工作......我真的更热衷于在视图中处理它。有任何想法吗?

标签: djangoformsview

解决方案


事实证明,问题的关键在于这个问题有点被误导了——要实现复制,有更好的方法。有关详细信息,请参阅django 论坛上的这篇文章。

本质上,您的 DuplicateView 可以从您的 CreateView 继承(甚至可能使用相同的模板)。那么关键是这样的:

myformset.management_form.initial["INITIAL_FORMS"] = 0     # otherwise, django saves the loaded ones in-place in the DB instead of creating new fouprods for this one

基本上,django 对待 INITIAL_FORMS 的方式与您在保存它们时通过 ajax 动态添加的任何表单有所不同。它尝试使用初始对象更新数据库中的对象,同时为较新的对象创建一些对象。因此,我实质上是用基础对象中的表单填充第一个表单(例如复制内联表单),但随后我用这些行告诉 django 它们都是新表单。因此,它将它们保存为实际实例,引用新创建对象的 PK,因为它应该

至于狭隘的问题(以防有人真的需要这样做),没有好的 django 方法。empty_form 是一堆无法直接编辑的访问器和属性。如果您真的想这样做,我想您需要重新编写渲染器的部分内容(以便最终的 html 更改)。我不会碰那个。

更好的解决方案是在模板中 - 例如:

 {% if field.auto-id=="id_mymodel-__prefix__-fk_fieldname" %}
    <input type="hidden" name="mymodel-__prefix__-fk_fieldname" id="id_mymodel-__prefix__-fk_fieldname">
{% else %}     
    # regular code    
{% endif %}

所以本质上,找到这个foreign_key对应的字段,然后手动写html标签,省略value=""部分。


推荐阅读