Three-level inline formsets in the frontend

71 Views Asked by At

I'm trying to accomplish a three-level stacked inline form in Django. Suppose these models:

class Anuncio(models.Model):
    title = models.CharField(max_length=200)
    delivery = models.CharField(max_length=100)

class Product(models.Model):
    anuncio = models.ForeignKey(Anuncio, on_delete=models.CASCADE)
    name = models.CharField(max_length=200)
    quantity = models.PositiveIntegerField(default=1)
    price = models.PositiveIntegerField()

class Image(models.Model):
    product = models.ForeignKey(Product, on_delete=models.CASCADE)
    image = models.ImageField()

There is a relation Anuncio-Product and another relation Product-Image. With this Django package, I accomplished exactly what I want in the Django admin: when creating an Anuncio object, I can add as many Products as I want, and those products can have as many Images as I want. I'm trying to accomplish this in the front end.

I think the way to go is with Django formsets, but I'm facing some problems. All the resources I've been able to find online are only 'two-level' formsets or in 'three-level' cases all the foreign keys point to the same parent model.

With this forms.py file:

class ProductForm(ModelForm):
    class Meta: 
        model = Product 
        fields = ['name', 'quantity', 'price']

class ImageForm(ModelForm):
    class Meta: 
        model = Imagen 
        fields = ['image']

class AnuncioForm(ModelForm):
    class Meta: 
        model = Anuncio 
        fields = ['title', 'delivery']

And this views.py function:

def anunciocreateview(request):
 form = AnuncioForm(request.POST or None)
 ProductFormSet = inlineformset_factory(Anuncio, Product, form=ProductForm)
 ImageFormSet = inlineformset_factory(Product, Image, form=ImageForm)
 if all([form.is_valid(), ProductFormSet.is_valid(), ImageFormSet.is_valid()]):
     parent = form.save(commit=False)
     parent.anunciante = request.user
     parent.save()
     for form in ProductoFormSet:
         child = form.save(commit=False)
         child.anuncio = parent 
         child.save()
     for form in ImagenFormSet:
         imagen = form.save(commit=False)
         imagen.product = form.product
         imagen.save()
  context = {
     'form_1' : form,
     'form_2' : ProductFormSet,
     'form_3' : ImageFormSet,
    }

But I think I'm missing important points when it comes to add the proper relations between models. This set-up gives an AttributeError of: 'ProductForm' object has no attribute '__name__'

The, for example, 'add (extra) Product' that appears in AdminStackedInLine I guess it can be accomplished with JavaScript, playing with hidden forms and changing attributes on click events.

Anyone has experience doing something similar or can guide me through the correct direction? Also on how to manage the data and the relations of the submitted forms?

1

There are 1 best solutions below

0
Lucas Grugru On

I think your problem is you have tried to validate a class Form instead of instanciate your formset and validate them. Your code would be look like to something like that :

def anunciocreateview(request):
  ProductFormSet = inlineformset_factory(Anuncio, Product, form=ProductForm)
  ImageFormSet = inlineformset_factory(Product, Image, form=ImageForm)

  anuncio_form = AnuncioForm(request.POST or None)
  product_formset = ProductFormSet(request.POST or None)
  image_formset = ImageFormSet(request.POST or None)
  if all([form.is_valid(), product_formset.is_valid(), image_formset.is_valid()]):
    ...

The function inlineformset_factory just create a Form class, not a instance of form.

More information and example on the documentation : https://docs.djangoproject.com/fr/4.1/topics/forms/formsets/