0

I have a specific problem with my forms. I think it would be better to share my codes instead of explaining the problem in detail.

However, to explain in a nutshell; inside my model I have field OneToOneField and model of that field has inlineformset_factory form. My new model also has a form and I want to save both forms.

I get the following error when I want to save the offer update form:

TypeError at /ru/mytarget/offer-update/T2GTTT053E9/

AdminOfferUpdateView.form_invalid() missing 2 required positional arguments: 'request_form' and 'request_item_formset'

Models:

request.py

class RequestModel(models.Model):
    customer = models.ForeignKey(Customer, on_delete=models.CASCADE, related_name="customer_requests")
    id = ShortUUIDField(primary_key=True, length=10, max_length=10, prefix="T", alphabet="ARGET0123456789", unique=True, editable=False)
    status = models.BooleanField(default=True)
    request_title = models.CharField(max_length=300)
    ......
    updated_on = models.DateTimeField(auto_now=True)
    published_date = models.DateTimeField(default=timezone.now)

    def __str__(self):
        return str(self.request_title)

    class Meta:
        verbose_name_plural = "Requests"
        verbose_name = "Request"

    def get_absolute_url(self):
        return reverse('mytarget:customer_request_details', kwargs={'pk': self.id})


class RequestItem(models.Model):
    request_model = models.ForeignKey(RequestModel, on_delete=models.CASCADE, related_name="request_items")
    product_name = models.CharField(max_length=300)
    ......

offer.py

class OfferModel(models.Model):
    request_model_name = models.OneToOneField(RequestModel, on_delete=models.CASCADE, primary_key=True)
    status = models.BooleanField(default=True)
    offer_validity = models.CharField(max_length=50, blank=True)
    ......

    updated_on = models.DateTimeField(auto_now=True)
    published_date = models.DateTimeField(default=timezone.now)

    def __str__(self):
        return str(self.request_model_name)

    class Meta:
        verbose_name_plural = "Offers"
        verbose_name = "Offer"

    def get_absolute_url(self):
        return reverse('mytarget:admin_offer_update', kwargs={'pk': self.request_model_name})

Forms:

request_create_form.py

class CustomerRequestForm(forms.ModelForm):

    disabled_fields = ("customer",)

    class Meta:
        model = RequestModel
        fields = ("customer", "request_title", "delivery_time", "shipping_country", "shipping_address",
                  "preferred_currency", "shipping_term", "delivery_term")

        widgets = {
            'request_title': TextInput(attrs={'class': 'form-control tableFormInputs',
                                              'placeholder': _('Example: Printers, Toner, and Cartridges')}),
            ......
        }

    def __init__(self, *args, **kwargs):
        self.user = kwargs.pop('customer')
        super(CustomerRequestForm, self).__init__(*args, **kwargs)
        self.fields['preferred_currency'].queryset = self.fields['preferred_currency'].queryset.translated().order_by("translations__currency_name")
        self.fields['shipping_term'].queryset = self.fields['shipping_term'].queryset.translated().order_by("translations__shipping_term")

        for field in self.disabled_fields:
            self.fields[field].widget = forms.HiddenInput()
            self.fields[field].disabled = True


class CustomerRequestItemForm(forms.ModelForm):

    class Meta:
        model = RequestItem
        fields = ("product_name", "product_info", "target_price", "price", "quantity", "dimensions", "net_weight", "gross_weight",
                  "hs_or_tn_ved_code", "brand", "manufacturer", "origin_country", "manufacturer_address")
        exclude = ()

        widgets = {
            'product_name': TextInput(attrs={'class': 'form-control tableFormInputs'}),
            ......
        }


RequestItemInlineFormset = inlineformset_factory(RequestModel, RequestItem,
                                                 form=CustomerRequestItemForm,
                                                 extra=1,
                                                 can_delete=True
                                                 )

offer_update_form.py

class AdminOfferUpdateForm(forms.ModelForm):
    disabled_fields = ()
    hidden_fields = ("request_model_name",)

    request_title = forms.CharField(required=False, widget=TextInput(attrs={'class': 'form-control tableFormInputs', 'placeholder': _('Example: Printers, Toner, and Cartridges')}))
    ......

    class Meta:
        model = OfferModel
        fields = ("request_model_name", "offer_validity", ......
                  )

        widgets = {'offer_validity': TextInput(attrs={'class': 'form-control tableFormInputs'}),
                   ......
                   'is_detailed_offer': CheckboxInput(attrs={'class': 'form-check-input'}),
                   }

    def __init__(self, *args, **kwargs):
        super(AdminOfferUpdateForm, self).__init__(*args, **kwargs)
        self.fields["preferred_currency"].choices = [(c.id, c.currency_name) for c in Currency.objects.all()]
        self.fields["shipping_term"].choices = [(st.id, st.shipping_term) for st in ShippingTerm.objects.all()]
        self.fields["delivery_term"].choices = [(dt.id, dt.delivery_term) for dt in DeliveryTerms.objects.all()]
        self.fields["request_statuses"].choices = [(r.id, r.status) for r in RequestStatus.objects.all()]

        for field in self.disabled_fields:
            self.fields[field].disabled = True

        for field in self.hidden_fields:
            self.fields[field].widget = forms.HiddenInput()

Views:

offer_update_view.py

@method_decorator([login_required(login_url=reverse_lazy("accounts:signin")), user_is_superuser], name='dispatch')
class AdminOfferUpdateView(UpdateView):
    model = OfferModel
    form_class = AdminOfferUpdateForm
    template_name = "mytarget/admin_offer_update.html"

    def get_context_data(self, **kwargs):
        context = super(AdminOfferUpdateView, self).get_context_data(**kwargs)
        if self.request.POST:
            context['request_form'] = AdminOfferUpdateForm(self.request.POST, instance=self.object.request_model_name)
            context['request_item_formset'] = RequestItemInlineFormset(self.request.POST, instance=self.object.request_model_name)
        else:
            context['request_form'] = AdminOfferUpdateForm(instance=self.object.request_model_name)
            context['request_item_formset'] = RequestItemInlineFormset(instance=self.object.request_model_name)
        return context

    def form_valid(self, form):
        context = self.get_context_data()
        request_form = context['request_form']
        request_item_formset = context['request_item_formset']
        with transaction.atomic():
            self.object = form.save()
            if request_form.is_valid() and request_item_formset.is_valid():
                request_form.instance = self.object.request_model_name
                request_form.save()
                request_item_formset.instance = self.object.request_model_name
                request_item_formset.save(commit=False)
                for ri in request_item_formset:
                    ri.save(commit=False)
                    request_item_formset.save()
        return super(AdminOfferUpdateView, self).form_valid(form)

    def form_invalid(self, form, request_form, request_item_formset):
        return self.render_to_response(
            self.get_context_data(form=form, request_form=request_form, request_item_formset=request_item_formset)
        )

    def get_initial(self):
        self.object = self.get_object()
        if self.object:
            return {"request_model": self.object.request_model_name, "request_item_formset": self.object.request_model_name}
        return super().initial.copy()

    def get_success_url(self):
        return reverse('mytarget:admin_offer_update', kwargs={'pk': self.object.id})

andronova
  • 21
  • 8
  • I'm assuming you're overidding form_invalid(). "when I want to save the offer update form" you should try visiting UpdateView.save() or anything that's inside your parent class that tries to call form_invalid() – Alvi15 Dec 09 '22 at 01:50
  • I was able to fetch data from the other form into the form fields in the offer_update_view.py file. Ensuring the view is saved will fix the issue. I think there is a problem with my get_context_data and form_valid functions. Even if I remove the form_invalid function, the form does not save. So I’m getting the data from the inheritance form but I can’t save it in the new form. Do you have any ideas about this? – andronova Dec 09 '22 at 06:47
  • do you still have the same error message? Because my idea is still the same, someone tried to call `form_invalid()` but they probably use 2 arguments instead of 4 that you have – Alvi15 Dec 09 '22 at 06:49
  • if i remove form_invalid() function, there is no error but form still not save. – andronova Dec 09 '22 at 07:11
  • `self.object = form.save()` might be worth to check whether this is what you wanted to do. `.save()` returns no value. – Alvi15 Dec 09 '22 at 07:17

1 Answers1

0

I solved my problem. I created a button function that creates a new model with inheritance of other model fields. In this way, there is no need to edit the form of the other model inside the form of my current model.

andronova
  • 21
  • 8