Django - Form for Editing Objects with a One-To-Many Relationship

33 views Asked by At

I have a model which contains a variable length list of objects. I want to be able to edit this model in a form, and in the same form edit each of the associated objects in the list.

I have looked at formsets which may be a way to handle the form for the multiple objects in the list, but from django documentation and searching online it is not obvious how I would then integrate this into the form of the parent object. What would be the best practice approach to this in Django?

2

There are 2 answers

2
SerhiiL On

For this you can use TabularInline or StackedInline.

#admin.py

from django.contrib import admin

class PostInlines(admin.TabularInline):
    model = Post
    fields = ["title"]
    extra = 1



@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
    ...
    inlines = [PostInlines]

After this category (from relationship one to many), you will have access to children's objects, including updating them.

0
Rusich90 On

Formsets exactly what you need

forms.py

from django.forms import inlineformset_factory
    
    
YourFormset = inlineformset_factory(
    ParentModel, 
    ChildrenModel, 
    fields="__all__", 
    extra=1,
)

views.py

class ParentModelUpdateView(UpdateView):
    model = ParentModel
    form_class = ParentForm
    success_url = reverse_lazy('{url}')
    template_name = '{template}'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['formset'] = YourFormset(instance=self.object)


    def post(self, request, *args, **kwargs):
        self.object = self.get_object()
        form_class = self.get_form_class()
        form = self.get_form(form_class)

        formset = YourFormset(self.request.POST, instance=self.object)
        if form.is_valid() and formset.is_valid():
            return self.form_valid(form, formset)
        else:
            return self.form_invalid(form, formset)

    def form_valid(self, form, formset):
        self.object = form.save()
        formset.instance = self.object
        formset.save()
        return HttpResponseRedirect(self.get_success_url())

    def form_invalid(self, form, formset):
        return render(
            self.request,
            self.template_name,
            super().get_context_data(
                form=form,
                formset=formset,
            ), status=400
        )

somewhere in html

...
{{ formset.management_form }}
{% for form in formset.forms %}
    {% for hidden in form.hidden_fields %}
        {{ hidden }}
    {% endfor %}
    ...
{% endfor %}