2 votes

Comment ajouter un modèle différent à ModelForm comme champ supplémentaire ?

J'essaie donc d'ajouter un champ supplémentaire à une classe ModelForm dans l'environnement de travail de l'entreprise. __init__ de sorte que, dans les vues de la classe Create/Update, je puisse saisir ce modèle et créer un nouveau modèle. Puisque le modèle n'a pas ce champ, comment puis-je l'ajouter dans la vue de la classe Create/Upate ? __init__ ou y a-t-il un autre moyen d'ajouter ce champ ?

J'ai essayé de remplacer __init__ pour inclure ce champ dans la classe ModelForm, mais je reçois toujours un message d'erreur indiquant que l'argument est inattendu.

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}"

J'aimerais savoir comment faire pour que cela fonctionne, afin que dans ma CreateView, je puisse commencer à créer un troisième modèle en fonction du tarif donné.

1voto

cagrias Points 113

Vous pouvez inclure un champ supplémentaire dans votre ModelForm et le définir dans votre vue, par exemple :

# 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)

1voto

Alasdair Points 36535

Vous pouvez utiliser un ModelChoiceField afin que vous puissiez sélectionner un tariff sur le formulaire.

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()) 

Alors dans votre form_valid() vous pouvez récupérer la méthode tariff de cleaned_data et créez les 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())

Vous devrez peut-être modifier le jeu de questions si vous devez filtrer la liste des tarifs disponibles. Dans votre question initiale, je ne pense pas qu'il soit judicieux d'avoir if instance dans le formulaire __init__ parce qu'une instance ne sera pas transmise au formulaire pour une méthode de type CreateView .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X