Django and fieldsets on ModelForm

Viewed 48047

I know you can specify fieldsets in django for Admin helpers. However, I cannot find anything useful for ModelForms. Just some patches which I cannot use. Am I missing something? Is there a way I could achieve something like fieldsets without manually writing out each field on my template in the appropriate tag.

I would ideally like to iterate through a set of BoundFields. However, doing something like this at the end of my ModelForm:

    fieldsets = []
    fieldsets.append(('Personal Information',
                      [username,password,password2,first_name,last_name,email]),) # add a 2 element tuple of string and list of fields
    fieldsets.append(('Terms & Conditions',
                      [acceptterms,acceptprivacy]),) # add a 2 element tuple of string and list of fields

fails as the items contained in my data structure are the raw fields, not the BoundFields. t looks like BoundFields are generated on the fly... this makes me sad. Could I create my own subclass of forms.Form which contains a concept of fieldsets (even a rough one that is not backward compatible... this is just for my own project) and if so, can you give any pointer? I do not want to mess with the django code.

6 Answers

I think this snippet does exactly what you want. It gives you a Form subclass that allows you to declaratively subdivide your form into fieldsets and iterate through them in your template.

Update: that snippet has since become part of django-form-utils

Fieldsets in modelforms are still in "design" stage. There's a ticket in Django trac with low activity.

It's something I've been interested in researching myself in the near future, but since I haven't done it yet the best I can offer are these snippets:

Edit: I just noticed this question again and I realize it needs an edit to point out Carl's project django-form-utils which contains a BetterForm class which can contain fieldsets. If you like this project give him a +1 for his answer below :)

One thing you can do is break your logical fieldsets into separate model form classes.

class PersonalInfoForm (forms.ModelForm):
    class Meta:
        model=MyModel
        fields=('field1', 'field2', ...)

class TermsForm (forms.ModelForm):
    class Meta:
        model=MyModel
        fields=('fieldX', 'fieldY', ...)

Pass them to your template in different variables and break up the formsets:

<form ...>
   <fieldset><legend>Personal Information</legend>
       {{ personal_info_form }}
   </fieldset>
   <fieldset><legend>Terms and Conditions</legend>
       {{ terms_form }}
   </fieldset>
</form>

In that sense each of your form classes is just a fragment of the actual HTML form.

It introduces a touch of complexity when you call save on the form. You'll probably want to pass commit=False and then merge the resultant objects. Or just avoid using ModelForm.save altogether and populate your model object by hand with 'cleaned_data'

You can use this package: https://pypi.org/project/django-forms-fieldset/

pip install django-forms-fieldset

Add forms_fieldset to your INSTALLED_APPS setting like this:

INSTALLED_APPS = [
    ...
    'forms_fieldset',
]

Add fieldsets in your form

from django.forms import ModelForm

from .models import Student

class StudentForm(ModelForm):
    fieldsets = [
        ("Student Information", {'fields': [
            ('first_name', 'last_name'),
            ('email', 'adress'),
        ]}),
        ("Parent Information", {'fields': [
            'mother_name',
            'father_name',
        ]}),
    ]
    class Meta:
        model = Student
        fields = '__all__'

In your views

def home(request):
    form = StudentForm()
    if request.method == 'POST':
        form = Form(request.POST, request.FILES)
        #save...
    context = {
        'form': form,
    }
    return render(request, 'home.html', context)

in your template

{% load forms_fieldset static %}
<link rel="stylesheet" type="text/css" href="{% static 'forms_fieldset/css/main.css' %}">

<form>
    {{ form|fieldset:'#42945c' }}
</form>
Related