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.
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 :)
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
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'
Daniel Greenfelds django-uni-form solves this with a the Layout helper class. I'm trying it out right now and it looks pretty clean to me.
I originally picked Django-uni-form because it complies with section 508.
This was the code that I developed in order to understand custom tags (with links). I applied it to create a fieldset.
Disclaimer: I encourage the use of any of the above answers, this was just for the sake of learning.
templatetags/myextras.py
:
from django import template
from django.template import Context
register = template.Library()
class FieldsetNode(template.Node):
""" Fieldset renderer for 'fieldset' tag """
def __init__(self, nodelist, fieldset_name):
""" Initialize renderer class
https://docs.djangoproject.com/en/1.8/howto/custom-template-tags/#writing-the-renderer
:param nodelist: a list of the template nodes inside a block of 'fieldset'
:param fieldset_name: the name of the fieldset
:return: None
"""
self.nodelist = nodelist
self.fieldset_name = fieldset_name
def render(self, context):
""" Render the inside of a fieldset block based on template file
https://docs.djangoproject.com/en/1.8/howto/custom-template-tags/#auto-escaping-considerations
:param context: the previous template context
:return: HTML string
"""
t = context.template.engine.get_template('myapp/fieldset.html')
return t.render(Context({
'var': self.nodelist.render(context),
'name': self.fieldset_name,
}, autoescape=context.autoescape))
@register.tag
def fieldset(parser, token):
""" Compilation function for fieldset block tag
Render a form fieldset
https://docs.djangoproject.com/en/1.8/howto/custom-template-tags/#writing-the-compilation-function
https://docs.djangoproject.com/en/1.8/howto/custom-template-tags/#parsing-until-another-block-tag
:param parser: template parser
:param token: tag name and variables
:return: HTML string
"""
try:
tag_name, fieldset_name = token.split_contents()
except ValueError:
raise template.TemplateSyntaxError("%r tag requires a single argument" % token.contents.split()[0])
if not (fieldset_name[0] == fieldset_name[-1] and fieldset_name[0] in ('"', "'")):
raise template.TemplateSyntaxError("%r tag's argument should be in quotes" % tag_name)
nodelist = parser.parse(('endfieldset',))
parser.delete_first_token()
return FieldsetNode(nodelist, fieldset_name[1:-1])
templates/myapp/fieldset.html
:
<div class="fieldset panel panel-default">
<div class="panel-heading">{{ name }}</div>
<div class="panel-body">{{ var }}</div>
</div>
templates/myapp/myform.html
:
<form action="{% url 'myapp:myurl' %}" method="post">
{% csrf_token %}
{% fieldset 'General' %}
{{form.myfield1 }}
{% endfieldset %}
{# my submit button #}
</form>
来源:https://stackoverflow.com/questions/518966/django-and-fieldsets-on-modelform