0

is there a way to validate that at least 1 of the many different formsets are not empty inside forms.py?

I have a working validation inside views.py but this validation don't apply to admin form. I want my code to be DRY and so my idea is to write the validation inside forms.py, so that both admin and HTML form share the same validation.

models.py, Person can have multiple PersonAddress and PersonEmail. In my actual models.py, I have many more related class, not just PersonAddress, PersonEmail, I just simplify it to 2 to make this example simpler.

class Person(models.Model):
    ...

class PersonAddress(models.Model):
    person = models.ForeignKey(
        Person, null=True, on_delete=models.SET_NULL, related_name="person_address",
    )
    ...

class PersonEmail(models.Model):
    person = models.ForeignKey(
        Person, null=True, on_delete=models.SET_NULL, related_name="person_email",
    )
    ...

forms.py, PersonAddressFormSet and PersonEmailFormSet will become inline formsets of PersonForm

class PersonForm(forms.ModelForm):
    ...

class PersonAddressForm(forms.ModelForm):
    ...

class PersonEmailForm(forms.ModelForm):
    ...

PersonAddressFormSet = forms.models.inlineformset_factory(
    Person, PersonAddress, form=PersonAddressForm, can_delete=False, extra=1,
)
PersonEmailFormSet = forms.models.inlineformset_factory(
    Person, PersonEmail, form=PersonEmailForm, can_delete=False, extra=1,
)

views.py, under form_valid method, if both address and email formset are not has_changed, I will return form error

class PersonCreateView(SuccessMessageMixin, MetadataMixin, LoginRequiredMixin, CreateView):
    model = Person
    form_class = PersonForm
    template_name = "person/person_form.html"

    def get_context_data(self, **kwargs):
        context = super(PersonCreateView, self).get_context_data(**kwargs)
        if self.request.POST:
            context["addresses"] = PersonAddressFormSet(self.request.POST, self.request.FILES)
            context["emails"] = PersonEmailFormSet(self.request.POST, self.request.FILES)
        else:
            context["addresses"] = PersonAddressFormSet()
            context["emails"] = PersonEmailFormSet()
        return context

    def form_valid(self, form):
        form.instance.created_by = self.request.user
        context = self.get_context_data()
        addresses = context["addresses"]
        emails = context["emails"]
        personfs = [addresses, emails]
        if (
                all(fs.is_valid() for fs in personfs)
        ):
            if not any(fs.has_changed() for fs in personfs):
                form.add_error(
                    None, ValidationError(
                        _("""
                        Please fill up either address / email
                        """)
                    )
                )
                return self.form_invalid(form)
            else:
                self.object = form.save()
                for fs in personfs:
                    fs.instance = self.object
                    fs.save()
        else:
            return self.form_invalid(form)
        return super(PersonCreateView, self).form_valid(form)

Now, I want the same validation for admin form, through forms.py. I thought of using PersonForm.clean

    def clean(self):
        cleaned_data = super().clean()
        print(f"PersonForm.clean.cleaned_data: {cleaned_data}")
        return cleaned_data

but PersonAddress and PersonEmail is not found under cleaned_data dict. So I am not sure if this is possible?

If my idea of putting validation inside forms.py is not possible. Is there a way I can write validation that works on both admin and HTML form?

Thanks!

shawnngtq
  • 687
  • 1
  • 7
  • 24
  • maybe this could help https://stackoverflow.com/questions/877723/inline-form-validation-in-django – taha maatof May 15 '21 at 03:28
  • @tahamaatof, no, that example is using a single type of formset `InvoiceOrder`, but I have multiple different formsets `PersonAddress` and `PersonEmail`. Also, that method is only for admin form, I want to write validation that I can use on both admin and HTML form :) – shawnngtq May 15 '21 at 03:32

0 Answers0