如何将不同的模型添加到ModelForm作为额外字段?

时间:2019-08-08 08:24:07

标签: python django django-forms django-views

因此,我试图在__init__函数的ModelForm类中添加一个额外的字段,以便在Create / Update类视图中可以获取此模型并创建一个新模型。由于“模型”没有此字段,我如何将其添加到__init__函数中?还是可以通过其他方法添加此字段?

我尝试重写__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中可以根据给定的价目表开始创建第三个模型

2 个答案:

答案 0 :(得分:1)

您可以在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)

答案 1 :(得分: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()方法中,您可以从tariff中检索cleaned_data并创建相关的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())

如果需要过滤可用关税列表,则可能需要更改查询集。在您最初提出的问题中,我认为在表单的if instance方法中使用__init__是没有道理的,因为不会为CreateView将实例传递到表单。 / p>