【问题标题】:How to add different model to ModelForm as extra field?如何将不同的模型添加到 ModelForm 作为额外字段?
【发布时间】:2019-12-15 21:47:07
【问题描述】:

所以我试图在__init__ func 中的 ModelForm 类中添加一个额外的字段,以便在 Create/Update 类视图中获取该模型并创建一个新模型。由于模型没有这个字段,我怎么能在__init__ func 中添加它,或者有没有其他方法可以添加这个字段?

我已尝试覆盖 __init__ 方法以将此字段包含在 ModelForm 类中,但这仍然会引发参数意外的错误

class MeterInstallationForm(ModelForm):

    class Meta:
        model = MeterInstallation
        fields = METER_INSTALLATION_DEFAULT_FIELDS

    def __init__(self, *args, **kwargs):
        instance = kwargs.get("instance")
        super(MeterInstallationForm, self).__init__(*args, **kwargs)
        if instance:
            #  get tariff info in the form
            # self.fields["tariff"] = instance.tariff
            self.fields["tariff"] = TariffApplication.objects.filter(meter_installation__pk=instance.meter_installation.pk).values_list("tariff", flat=True)

class MeterInstallationCreateView(MeterInstallationsViewMixin, LoginRequiredMixin, CreateView):
    template_name = "meter_installations/meter_installation_create.html"
    fields = (
        "name",
        "meter_type",
        "parent",
        "meter",
        "building",
        "initial_reading",
        "final_reading",
        "active_after",
        "active_until",
        "comment",
    )
    form_class = MeterInstallationForm


meter_installation_create_view = MeterInstallationCreateView.as_view()

class MeterInstallation(ActiveAfterUntilModel, DateTrackedModel, MPTTModel, NamedModel):  # type: ignore
    meter_type = models.ForeignKey(
        MeterType,
        on_delete=models.PROTECT,
        null=False,
        blank=False,
        related_name="installations",
        verbose_name=_("Meter Installation type"),
    )
    parent = TreeForeignKey(
        "self", on_delete=models.CASCADE, null=True, blank=True, related_name="children", db_index=True
    )
    meter = models.ForeignKey(
        Meter, on_delete=models.PROTECT, related_name="installations", null=False, blank=False, verbose_name=_("Meter")
    )
    building = models.ForeignKey(
        Building,
        on_delete=models.PROTECT,
        related_name="meter_installations",
        null=True,
        blank=False,
        verbose_name=_("Building"),
    )
    places = models.ManyToManyField(Place, related_name="meter_installations", blank=False, verbose_name=_("Places"))
    initial_reading = models.DecimalField(
        decimal_places=4, max_digits=10, null=False, blank=False, default=0, verbose_name=_("Initial reading")
    )
    final_reading = models.DecimalField(
        decimal_places=4, max_digits=10, null=True, blank=True, default=0, verbose_name=_("Final reading")
    )

    class MPTTMeta:
        order_insertion_by = ["meter"]

    def get_absolute_url(self):
        return reverse("meter-installations:meter-installation-detail", kwargs={"pk": self.pk})

    def delete(self, *args, **kwargs):
        first_lvl_children = self.get_children().filter(level=1)
        for first_lvl_child in first_lvl_children:
            first_lvl_child.parent = None
            first_lvl_child.save()
            for leaf in first_lvl_child.get_children():
                leaf.parent = first_lvl_child
                leaf.save()
            tree_id = first_lvl_child.tree_id

            MeterInstallation.objects.partial_rebuild(tree_id)

        super(MeterInstallation, self).delete(*args, **kwargs)

    def __str__(self):
        return f"[{self.pk}] type: {self.meter_type_id}, meter: {self.meter_id}"

class Tariff(ActiveAfterUntilModel, NamedModel, DateTrackedModel):
    tariff_type = models.ForeignKey(
        MeterType,
        on_delete=models.PROTECT,
        null=False,
        blank=False,
        related_name="tariffs",
        verbose_name=_("Tariff type"),
    )
    building = models.ForeignKey(
        Building, on_delete=models.PROTECT, related_name="tariffs", null=True, blank=False, verbose_name=_("Building")
    )
    unit_name = models.CharField(max_length=100, null=False, blank=True, unique=False, verbose_name=_("Unit name"))
    unit_price = models.DecimalField(
        decimal_places=4, max_digits=10, null=False, blank=False, default=0.0, verbose_name=_("Unit price")
    )
    VAT = models.DecimalField(decimal_places=2, max_digits=10, null=True, blank=True, verbose_name=_("VAT"))

    class Meta:
        unique_together = ("name", "tariff_type", "active_after", "active_until")

    def get_absolute_url(self):
        return reverse("tariffs:tariff-detail", kwargs={"pk": self.pk})

    def __str__(self) -> str:
        return (
            f"[{self.pk}] "
            f"type: {self.tariff_type_id}, "
            f"building: {self.building_id}, "
            f"price: {self.unit_price}, "
            f"VAT: {self.VAT}, "
            f"active_until: {self.active_until}"
        )

class TariffApplication(ActiveAfterUntilModel, DateTrackedModel):  # type: ignore
    tariff = models.ForeignKey(
        Tariff,
        on_delete=models.PROTECT,
        null=False,
        blank=False,
        related_name="tariff_applications",
        verbose_name=_("Tariff Applications"),
    )
    meter_installation = models.ForeignKey(
        "MeterInstallation",
        on_delete=models.PROTECT,
        null=False,
        blank=False,
        related_name="tariff_applications",
        verbose_name=_("Meter Installation"),
    )

    def __str__(self) -> str:
        return f"[{self.pk}] tariff: {self.tariff_id}, meter installation: {self.meter_installation_id}"

我很想知道如何进行这项工作,以便在我的 CreateView 中按照给定的关税开始创建第三个模型

【问题讨论】:

  • 请展示您的模型。
  • 添加到问题中

标签: python django django-forms django-views


【解决方案1】:

您可以使用ModelChoiceField,以便在表单上选择tariff

class MeterInstallationForm(ModelForm):

    class Meta:
        model = MeterInstallation
        fields = METER_INSTALLATION_DEFAULT_FIELDS

    def __init__(self, *args, **kwargs):
        super(MeterInstallationForm, self).__init__(*args, **kwargs)
        self.fields["tariff"] = forms.ModelChoiceField(queryset=Tariff.objects.all()) 

然后在您的form_valid() 方法中,您可以从cleaned_data 中检索tariff 并创建相关的TariffApplication

def form_valid(self, form):
    instance = form.save()
    TariffApplication.objects.create(tariff=form.cleaned_data['tariff'], meter_installation=instance)
    return HttpResponseRedirect(self.get_success_url())

如果您需要过滤可用关税列表,您可能需要更改查询集。在您最初的问题中,我认为在表单的 __init__ 方法中使用 if instance 没有意义,因为不会将实例传递给 CreateView 的表单。

【讨论】:

  • 好吧,现在我的服务器至少正在运行,这很好,但我没有得到的一件事是,如果我用覆盖的 __init__ 初始化表单,我应该看到 @987654334 @ 表单中的字段,我可以从查询中选择关税,但现在我没有看到 + 我的 MeterInstallationMPTTAdmin 正在崩溃,因为它使用相同的表单 => TypeError: __init__() got multiple values for argument 'queryset'
  • 我的代码中有一个错字 - ModelChoiceField 的第一个参数是查询集。
  • 正如我在上面的答案中所说,CreateView 不会将实例传递给表单,因此如果您在 if instance: 中创建模型选择字段,那么该行将不会为您运行创建视图。
  • 我已经删除了那行,正如你所说,并从你的评论中留下了 __init__,但表单仍然没有呈现带有 tariff 字段的表单,我应该从中选择 @ 987654341@
  • 我不能同时使用fields和form_class,出错了,所以只要指定form_class就可以了,再次感谢你,好人!:)
【解决方案2】:

您可以在 ModelForm 中包含额外的字段并在您的视图中进行设置,例如:

# forms.py
class MeterInstallationForm(ModelForm):    
    class Meta:
        model = MeterInstallation
        fields = METER_INSTALLATION_DEFAULT_FIELDS + ('tariff',)
# views.py
class MeterInstallationCreateView(MeterInstallationsViewMixin, LoginRequiredMixin, CreateView):
    template_name = "meter_installations/meter_installation_create.html"
    fields = (
        "name",
        "meter_type",
        "parent",
        "meter",
        "building",
        "initial_reading",
        "final_reading",
        "active_after",
        "active_until",
        "comment",
    )
    form_class = MeterInstallationForm

    def form_valid(self, form):
        form.instance.tariff = forms.TariffApplication(form.data)
        return super().form_valid(form)

【讨论】:

  • 我有一个基于类的视图,所以如果我只是在 CreateView 中添加一个 template_class 这仍然会引发错误django.core.exceptions.FieldError: Unknown field(s) (tariff) specified for MeterInstallation
  • 在访问我的CreateView之前,我需要以某种方式包含此字段
  • 您也可以分享一下您的观点吗?您也可以在视图的 create 方法中添加此实现。
  • 请将其添加到您的问题中 :)
  • 这仍然会引发同样的错误,我需要在此 MeterInstallation CreateView 中获取 Tariff 以便我可以创建该 TariffApplication 模型并为其分配 Tariff 和 MeterInstallation
猜你喜欢
  • 2012-11-13
  • 2015-08-26
  • 2011-06-07
  • 2011-01-14
  • 2014-08-15
  • 1970-01-01
  • 2015-09-06
  • 2017-03-07
  • 1970-01-01
相关资源
最近更新 更多