Opened 9 years ago

Closed 9 years ago

#25488 closed Bug (duplicate)

BaseGenericInlineFormSet runs validation methods before linking form instances to their related object

Reported by: Ariel Pontes Owned by: nobody
Component: contrib.contenttypes Version: 1.8
Severity: Normal Keywords: BaseGenericInlineFormSet clean validation
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

I have a generic inline FormSet class generated like this:

ProductImageInlineFormset = generic_inlineformset_factory(
    Image, form=ProductImageForm, extra=1)

and initialized like this:

product = get_object_or_404(Product.objects.by_id(id))
        image_formset = ProductImageInlineFormset(
            request.POST, request.FILES, instance=product)

When the individual forms that compose the FormSet are initialized, the "product" object is not linked to their instances. This is only done in the BaseGenericInlineFormSet.save_new method:

def save_new(self, form, commit=True):
    setattr(form.instance, self.ct_field.get_attname(),
        ContentType.objects.get_for_model(self.instance).pk)
    setattr(form.instance, self.ct_fk_field.get_attname(),
        self.instance.pk)
    return form.save(commit=commit)

This causes problem when the Image class needs to do validation across fields:

class Image(models.Model):
    ...
    def clean(self):
        related_class = self.content_type.model_class()
        print(self.content_type.model_class())

The above code results in the following error when trying to save ProductImageInlineFormsets:

django.db.models.fields.related.RelatedObjectDoesNotExist: Image has no content_type.

The linking of Image to Product should be done in the form initialization instead. At first sight, after a quick experiment, overriding the _construct_form instead of the save_new seems to solve the problem:

 def _construct_form(self, i, **kwargs):
     form = super()._construct_form(i, **kwargs)
     setattr(form.instance, self.ct_field.get_attname(),
         ContentType.objects.get_for_model(self.instance).pk)
     setattr(form.instance, self.ct_fk_field.get_attname(),
         self.instance.pk)
     return form

Change History (3)

comment:1 by Tim Graham, 9 years ago

It might be a duplicate of #25431 -- could you check if the problem exists on the stable/1.8.x branch, which will be soon released as 1.8.5?

comment:2 by Ariel Pontes, 9 years ago

Replying to timgraham:

It might be a duplicate of #25431 -- could you check if the problem exists on the stable/1.8.x branch, which will be soon released as 1.8.5?

The problem described is indeed very similar, but the example given was of a simple ModelFormset, not Generic or Inline. I installed stable/1.8.x and the problem persists. In a way it makes sense, I honestly don't understand how that commit would have solved the problem. It must solve the problem for ModelFormSet only, not BaseGenericInlineFormSet.

Last edited 9 years ago by Ariel Pontes (previous) (diff)

comment:3 by Tim Graham, 9 years ago

Resolution: duplicate
Status: newclosed

Thanks for looking into it. Upon further investigation, I think this a duplicate of #19255. If you'd like to submit a patch with tests, that will help move the issue forward. Thanks!

Note: See TracTickets for help on using tickets.
Back to Top