0

I have an original form (which is the result of three ModelForms) and I'm creating additional forms for two of those in the front-end.

Everything is working fine except for when saving the data. The originally rendered form saves correctly all the instances in the database. However, all the 'additionally added forms' won't save. In the form I have this button that with HTMX adds a new form:

<button type="button" class="btn btn-tertiary" hx-get="{% url 'buyandsell:create-product' %}" hx-target="#productforms" hx-swap="beforeend">Add new product</button>

This 'create-product' partial form corresponds to this view:

def create_product(request):
    producto_form = ProductoForm()
    imagen_form = ImagenForm()
    context = {
        'producto_form' : producto_form,
        'imagen_form' : imagen_form
    }
    return render(request, 'buyandsell/partials/producto-form.html', context)

But I guess the view I need to change for saving the forms is the original main view. I tried iterating over 'producto_forms' but it doesn't let me (as I think that iteration refers to the fields of the form). This is the view function that is working well before adding additional products:

def anunciocreateview(request):
    if request.method == "POST":
        anuncio_form = AnuncioForm(request.POST or None)
        producto_form = ProductoForm(request.POST or None)
        imagen_form = ImagenForm(request.POST, request.FILES)
        if all([anuncio_form.is_valid(), producto_form.is_valid(), imagen_form.is_valid()]):
            anuncio = anuncio_form.save(commit=False)
            anuncio.anunciante = request.user
            anuncio.save()
            producto = producto_form.save(commit=False)
            producto.anuncio = anuncio
            producto.save()
            imagen = request.FILES.get('imagen')
            if imagen:
                Imagen.objects.create(producto=producto, imagen=imagen)
            return HttpResponse(status=204, headers={'HX-Trigger' : 'eventsListChanged'})
        else:
            print(anuncio_form.errors)
            print(producto_form.errors)
            print(imagen_form.errors)
    else:
        anuncio_form = AnuncioForm()
        producto_form = ProductoForm()
        imagen_form = ImagenForm()
    context = {
        'anuncio_form' : anuncio_form,
        'producto_form' : producto_form,
        'imagen_form' : imagen_form
    }
    return render(request, 'buyandsell/formulario.html', context)

How can I make this view also look for additionally created forms and save them as well?

rolandist_scim
  • 145
  • 1
  • 8

1 Answers1

1

I think you should use formsets. From the docs: "A formset is a layer of abstraction to work with multiple forms on the same page."

So instead of producto_form and imagen_form you would send a producto_formset and imagen_formset to the tempate. (E.g. You'd ceate a model formset with modelformset_factory, which can use your current form as its form option. Then you'd instantiate the formset in your view, rather than the form.)

You would set up your anunciocreateview to process the formsets using the same .is_valid() and .save() methods as used for typical model forms. Django would then iterate over each form inside the formset when processing the data.

Using formsets in combination with htmx is also possible. The following has details on that: The right way to dynamically add Django formset instances and POST usign HTMX?

Docs references:

Matt
  • 133
  • 1
  • 6
  • Thank you for your comment. I will go in depth to your explanation and try to make it work. I'm working with HTMX, do you have any specific advice for that? I'll go back to this post once I tried it. – rolandist_scim Jan 12 '23 at 14:04
  • 1
    In the other SO post I linked to I provided a fairly complete working example, which I can confirm works on my machine, at least. For the htmx bits from that example, the `hx-swap-oob` is important for updating hidden fields from the formset management form, which is easy to overlook. Good luck! – Matt Jan 12 '23 at 18:53