Skip to content
Advertisement

form for simultaneously editing two django models with foreign key relationship

I am trying to find a simple way to create a form that allows the editing of two models with foreign key relationship simultaneously.

After some research, it seems that Inline formsets come very close to what I want to do.

The django documentation offers this example:

from django.db import models

class Author(models.Model):
    name = models.CharField(max_length=100)

class Book(models.Model):
    author = models.ForeignKey(Author, on_delete=models.CASCADE)
    title = models.CharField(max_length=100)

And then,

>>> from django.forms import inlineformset_factory
>>> BookFormSet = inlineformset_factory(Author, Book, fields=('title',))
>>> author = Author.objects.get(name='Mike Royko')
>>> formset = BookFormSet(instance=author)

Let’s suppose Author has a second field, city. Can I use the fields argument to add a city to the form?

If inline formsets are not the way to go, is there another way that generates this joint form?


After some more research, I found django model Form. Include fields from related models from 2009 which hints that inline form sets might not be the way to go.

I would be very much interested if there’s a default solution with a different factory.

Advertisement

Answer

Well, this is a bit different from the linked post because there the relationship is a OneToOne and not a ForeignKey.

There is no django factory (at least that I know of) to do what you want automatically. You can try the following instead:

  • Create a ModelForm for the depended table (Book in this case):

    class BookForm(forms.ModelForm):
        class Meta:
            model = Book
            fields = ['name', 'city', 'other_field', ...]
    
  • Create an inline_formset for the depended table:

    BookFormSet = inlineformset_factory(Author, Book, form=BookForm)
    
  • Use the formset in your view:

    def my_view(request):
        if request.method == 'POST':
            formset = BookFormSet(request.POST, instance=request.user)
            if formset.is_valid():
                ...
                formset.save()
        else:
            formset = BookFormSet(instance=request.user)
        return render_to_response("template.html", {"formset": formset})
    

    OR in a class based view: django class-based views with inline model-form or formset

  • Finally in the template (this part needs a bit of fumbling to get it right, but this is a general idea):

    <form action="." method="post">
        {% csrf_token %}
        {{ formset }}
        {{ formset.management_form }}
        <input type="submit" value="Submit">
    </form>
    
User contributions licensed under: CC BY-SA
5 People found this is helpful
Advertisement