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)
delivery_time = models.CharField(max_length=50)
shipping_country = models.CharField(max_length=50)
shipping_address = models.CharField(max_length=300)
preferred_currency = models.ForeignKey(Currency, on_delete=models.CASCADE)
shipping_term = models.ForeignKey(ShippingTerm, on_delete=models.CASCADE)
delivery_term = models.ForeignKey(DeliveryTerms, on_delete=models.CASCADE)
request_statuses = models.ForeignKey(RequestStatus, on_delete=models.CASCADE, blank=True, default=1)
is_accepted = models.BooleanField(default=False)
is_rejected = models.BooleanField(default=False)
is_offer_created = models.BooleanField(default=False)
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)
product_info = models.TextField(max_length=2000, blank=True)
target_price = models.DecimalField(max_digits=15, decimal_places=2, blank=True, null=True)
price = models.DecimalField(max_digits=15, decimal_places=2, blank=True, null=True)
quantity = models.DecimalField(max_digits=10, decimal_places=2)
dimensions = models.CharField(max_length=100, blank=True)
net_weight = models.CharField(max_length=20, blank=True)
gross_weight = models.CharField(max_length=20, blank=True)
hs_or_tn_ved_code = models.CharField(max_length=100, blank=True)
brand = models.CharField(max_length=100, blank=True)
manufacturer = models.CharField(max_length=100, blank=True)
origin_country = models.CharField(max_length=50, blank=True)
manufacturer_address = models.CharField(max_length=300, blank=True)
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)
terms_of_payment = models.CharField(max_length=100, blank=True, default=_("100% upon order"))
profit_rate = models.PositiveIntegerField(blank=True, default=0)
swift_payment_slip = models.FileField(upload_to='payment-slips/%Y/%m/%d/', validators=[max_file_size], blank=True)
shipping_costs = models.CharField(max_length=300, blank=True)
shipping_costs_quantity = models.PositiveIntegerField(blank=True, default=0)
shipping_costs_price = models.DecimalField(max_digits=15, decimal_places=2, blank=True, default=0)
additional_expenses = models.CharField(max_length=300, blank=True)
additional_expenses_quantity = models.PositiveIntegerField(blank=True, default=0)
additional_expenses_price = models.DecimalField(max_digits=15, decimal_places=2, blank=True, default=0)
upload_shipping_details = models.FileField(upload_to='offer-files/%Y/%m/%d/', blank=True, null=True)
upload_additional_documents = models.FileField(upload_to='offer-files/%Y/%m/%d/', blank=True, null=True)
product_photo = models.ImageField(upload_to='offer-files/%Y/%m/%d/', blank=True, null=True)
product_photo_thumbnail = ImageSpecField(source='product_photo',
processors=[Transpose(),
SmartResize(width=2000, height=1500, upscale=False)
],
format='WEBP',
options={'quality': 100})
product_shipment_photo = models.ImageField(upload_to='offer-files/%Y/%m/%d/', blank=True, null=True)
product_shipment_photo_thumbnail = ImageSpecField(source='product_shipment_photo',
processors=[Transpose(),
SmartResize(width=2000, height=1500, upscale=False)
],
format='WEBP',
options={'quality': 100})
shipping_tracking_code = models.CharField(max_length=500, blank=True)
is_offer_accepted = models.BooleanField(default=False)
is_offer_rejected = models.BooleanField(default=False)
is_offer_canceled = models.BooleanField(default=False)
send_proforma_invoice = models.BooleanField(default=False)
is_payment_made = models.BooleanField(default=False)
send_invoice = models.BooleanField(default=False)
send_offer = models.BooleanField(default=False)
is_detailed_offer = models.BooleanField(default=False)
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')}),
'delivery_time': TextInput(attrs={'class': 'form-control tableFormInputs'}),
'shipping_country': TextInput(attrs={'class': 'form-control tableFormInputs'}),
'shipping_address': TextInput(attrs={'class': 'form-control tableFormInputs'}),
'preferred_currency': Select(attrs={'class': 'form-select tableFormInputs',
'aria-label': _('Preferred Currency')}),
'shipping_term': Select(attrs={'class': 'form-select tableFormInputs',
'aria-label': _('Shipping Term')}),
'delivery_term': Select(attrs={'class': 'form-select tableFormInputs',
'aria-label': _('Delivery Terms')}),
}
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'}),
'product_info': Textarea(attrs={'class': 'form-control tableFormInputs', 'maxlength': 1000, 'rows': 3}),
'target_price': NumberInput(attrs={'class': 'form-control tableFormInputs'}),
'price': NumberInput(attrs={'class': 'form-control tableFormInputs'}),
'quantity': NumberInput(attrs={'class': 'form-control tableFormInputs'}),
'dimensions': TextInput(attrs={'class': 'form-control tableFormInputs'}),
'net_weight': TextInput(attrs={'class': 'form-control tableFormInputs'}),
'gross_weight': TextInput(attrs={'class': 'form-control tableFormInputs'}),
'hs_or_tn_ved_code': TextInput(attrs={'class': 'form-control tableFormInputs'}),
'brand': TextInput(attrs={'class': 'form-control tableFormInputs'}),
'manufacturer': TextInput(attrs={'class': 'form-control tableFormInputs'}),
'origin_country': TextInput(attrs={'class': 'form-control tableFormInputs'}),
'manufacturer_address': 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')}))
delivery_time = forms.CharField(required=False, widget=TextInput(attrs={'class': 'form-control tableFormInputs'}))
shipping_country = forms.CharField(required=False, widget=TextInput(attrs={'class': 'form-control tableFormInputs'}))
shipping_address = forms.CharField(required=False, widget=Textarea(attrs={'class': 'form-control tableFormInputs', 'rows': 3}))
preferred_currency = forms.ChoiceField(required=False, widget=Select(attrs={'class': 'form-select tableFormInputs', 'aria-label': _('Preferred Currency')}))
shipping_term = forms.ChoiceField(required=False, widget=Select(attrs={'class': 'form-select tableFormInputs', 'aria-label': _('Shipping Term')}))
delivery_term = forms.ChoiceField(required=False, widget=Select(attrs={'class': 'form-select tableFormInputs', 'aria-label': _('Delivery Terms')}))
request_statuses = forms.ChoiceField(required=False, widget=Select(attrs={'class': 'form-select tableFormInputs', 'aria-label': _('Delivery Terms')}))
class Meta:
model = OfferModel
fields = ("request_model_name", "offer_validity", "terms_of_payment", "profit_rate", "is_offer_accepted",
"is_offer_rejected", "send_proforma_invoice", "is_payment_made", "send_invoice", "send_offer",
"is_detailed_offer", "is_offer_canceled", "shipping_costs", "shipping_costs_quantity", "shipping_costs_price", "additional_expenses",
"additional_expenses_quantity", "additional_expenses_price", "upload_shipping_details",
"upload_additional_documents", "product_photo", "product_shipment_photo", "shipping_tracking_code",
"request_title", "delivery_time", "shipping_country", "shipping_address", "preferred_currency",
"shipping_term", "delivery_term", "request_statuses",
)
widgets = {'offer_validity': TextInput(attrs={'class': 'form-control tableFormInputs'}),
'terms_of_payment': TextInput(attrs={'class': 'form-control tableFormInputs'}),
'profit_rate': NumberInput(attrs={'class': 'form-control tableFormInputs'}),
'shipping_costs': TextInput(attrs={'class': 'form-control tableFormInputs'}),
'shipping_costs_quantity': NumberInput(attrs={'class': 'form-control tableFormInputs'}),
'shipping_costs_price': NumberInput(attrs={'class': 'form-control tableFormInputs'}),
'additional_expenses': TextInput(attrs={'class': 'form-control tableFormInputs'}),
'additional_expenses_quantity': NumberInput(attrs={'class': 'form-control tableFormInputs'}),
'additional_expenses_price': NumberInput(attrs={'class': 'form-control tableFormInputs'}),
'upload_shipping_details': ClearableFileInput(attrs={'class': 'form-control w-auto tableFormInputs'}),
'upload_additional_documents': ClearableFileInput(attrs={'class': 'form-control w-auto tableFormInputs'}),
'product_photo': ClearableFileInput(attrs={'class': 'form-control w-auto tableFormInputs'}),
'product_shipment_photo': ClearableFileInput(attrs={'class': 'form-control w-auto tableFormInputs'}),
'shipping_tracking_code': TextInput(attrs={'class': 'form-control tableFormInputs'}),
'is_offer_accepted': CheckboxInput(attrs={'class': 'form-check-input'}),
'is_offer_rejected': CheckboxInput(attrs={'class': 'form-check-input'}),
'is_offer_canceled': CheckboxInput(attrs={'class': 'form-check-input'}),
'send_proforma_invoice': CheckboxInput(attrs={'class': 'form-check-input'}),
'is_payment_made': CheckboxInput(attrs={'class': 'form-check-input'}),
'send_invoice': CheckboxInput(attrs={'class': 'form-check-input'}),
'send_offer': CheckboxInput(attrs={'class': 'form-check-input'}),
'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})