108👍
From Django 1.9+
Django 1.9 adds a new Form
attribute, field_order
, allowing to order the field regardless their order of declaration in the class.
class MyForm(forms.Form):
summary = forms.CharField()
description = forms.CharField(widget=forms.TextArea)
author = forms.CharField()
notes = form.CharField()
field_order = ['author', 'summary']
Missing fields in field_order
keep their order in the class and are appended after the ones specified in the list. The example above will produce the fields in this order: ['author', 'summary', 'description', 'notes']
See the documentation: https://docs.djangoproject.com/en/stable/ref/forms/api/#notes-on-field-ordering
Up to Django 1.6
I had this same problem and I found another technique for reordering fields in the Django CookBook:
class EditForm(forms.Form):
summary = forms.CharField()
description = forms.CharField(widget=forms.TextArea)
class CreateForm(EditForm):
name = forms.CharField()
def __init__(self, *args, **kwargs):
super(CreateForm, self).__init__(*args, **kwargs)
self.fields.keyOrder = ['name', 'summary', 'description']
16👍
From Django 1.9: https://docs.djangoproject.com/en/1.10/ref/forms/api/#notes-on-field-ordering
Original answer: Django 1.9 will support this by default on the form with field_order
:
class MyForm(forms.Form):
...
field_order = ['field_1', 'field_2']
...
https://github.com/django/django/commit/28986da4ca167ae257abcaf7caea230eca2bcd80
- [Django]-Django manage.py runserver invalid syntax
- [Django]-Atomic increment of a counter in django
- [Django]-HTML – How to do a Confirmation popup to a Submit button and then send the request?
12👍
I used the solution posted by Selene but found that it removed all fields which weren’t assigned to keyOrder. The form that I’m subclassing has a lot of fields so this didn’t work very well for me. I coded up this function to solve the problem using akaihola’s answer, but if you want it to work like Selene’s all you need to do is set throw_away
to True
.
def order_fields(form, field_list, throw_away=False):
"""
Accepts a form and a list of dictionary keys which map to the
form's fields. After running the form's fields list will begin
with the fields in field_list. If throw_away is set to true only
the fields in the field_list will remain in the form.
example use:
field_list = ['first_name', 'last_name']
order_fields(self, field_list)
"""
if throw_away:
form.fields.keyOrder = field_list
else:
for field in field_list[::-1]:
form.fields.insert(0, field, form.fields.pop(field))
This is how I’m using it in my own code:
class NestableCommentForm(ExtendedCommentSecurityForm):
# TODO: Have min and max length be determined through settings.
comment = forms.CharField(widget=forms.Textarea, max_length=100)
parent_id = forms.IntegerField(widget=forms.HiddenInput, required=False)
def __init__(self, *args, **kwargs):
super(NestableCommentForm, self).__init__(*args, **kwargs)
order_fields(self, ['comment', 'captcha'])
- [Django]-What is the SQL ''LIKE" equivalent on Django ORM queries?
- [Django]-Is there a HAML implementation for use with Python and Django
- [Django]-What is a "django backend"?
10👍
It appears that at some point the underlying structure of field order was changed from a django specific SordedDict
to a python standard OrderedDict
Thus, in 1.7 I had to do the following:
from collections import OrderedDict
class MyForm(forms.Form):
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
original_fields = self.fields
new_order = OrderedDict()
for key in ['first', 'second', ... 'last']:
new_order[key] = original_fields[key]
self.fields = new_order
I’m sure someone could golf that into two or three lines, but for S.O. purposes I think clearly showing how it works is better than cleaver.
- [Django]-MySQL "incorrect string value" error when save unicode string in Django
- [Django]-Composite primary key in django
- [Django]-Django FileField with upload_to determined at runtime
7👍
You could also create a decorator to order fields (inspired by Joshua’s solution):
def order_fields(*field_list):
def decorator(form):
original_init = form.__init__
def init(self, *args, **kwargs):
original_init(self, *args, **kwargs)
for field in field_list[::-1]:
self.fields.insert(0, field, self.fields.pop(field))
form.__init__ = init
return form
return decorator
This will ensure that all the fields passed to the decorator come first.
You can use it like this:
@order_fields('name')
class CreateForm(EditForm):
name = forms.CharField()
- [Django]-Auth.User.groups: (fields.E304) Reverse accessor for 'User.groups' clashes with reverse accessor for 'UserManage.groups'
- [Django]-What is more efficient .objects.filter().exists() or get() wrapped on a try
- [Django]-How to expire Django session in 5minutes?
6👍
The accepted answer’s approach makes use of an internal Django forms API that was changed in Django 1.7. The project team’s opinion is that it should never have been used in the first place. I now use this function to reorder my forms. This code makes use of an OrderedDict
:
def reorder_fields(fields, order):
"""Reorder form fields by order, removing items not in order.
>>> reorder_fields(
... OrderedDict([('a', 1), ('b', 2), ('c', 3)]),
... ['b', 'c', 'a'])
OrderedDict([('b', 2), ('c', 3), ('a', 1)])
"""
for key, v in fields.items():
if key not in order:
del fields[key]
return OrderedDict(sorted(fields.items(), key=lambda k: order.index(k[0])))
Which I use in classes like this:
class ChangeOpenQuestionForm(ChangeMultipleChoiceForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
key_order = ['title',
'question',
'answer',
'correct_answer',
'incorrect_answer']
self.fields = reorder_fields(self.fields, key_order)
For recent versions of Django (>=1.9), see the other answers’ Form.field_order
- [Django]-Token Authentication for RESTful API: should the token be periodically changed?
- [Django]-Django model constraint for related objects
- [Django]-How can I create a deep clone of a DB object in Django?
3👍
See the notes in this SO question on the way Django’s internals keep track of field order; the answers include suggestions on how to “reorder” fields to your liking (in the end it boils down to messing with the .fields
attribute).
- [Django]-Django.contrib.gis.db.backends.postgis vs django.db.backends.postgresql_psycopg2
- [Django]-Django migration strategy for renaming a model and relationship fields
- [Django]-"Failed building wheel for psycopg2" – MacOSX using virtualenv and pip
2👍
Alternate methods for changing the field order:
Pop-and-insert:
self.fields.insert(0, 'name', self.fields.pop('name'))
Pop-and-append:
self.fields['summary'] = self.fields.pop('summary')
self.fields['description'] = self.fields.pop('description')
Pop-and-append-all:
for key in ('name', 'summary', 'description'):
self.fields[key] = self.fields.pop(key)
Ordered-copy:
self.fields = SortedDict( [ (key, self.fields[key])
for key in ('name', 'summary' ,'description') ] )
But Selene’s approach from the Django CookBook still feels clearest of all.
- [Django]-Django FileField: How to return filename only (in template)
- [Django]-Django model manager objects.create where is the documentation?
- [Django]-Data Mining in a Django/Postgres application
1👍
Based on an answer by @akaihola and updated to work with latest Django 1.5 as self.fields.insert
is being depreciated.
from easycontactus.forms import *
from django import forms
class CustomEasyContactUsForm(EasyContactUsForm):
### form settings and configuration
CAPTHCA_PHRASE = 'igolf'
### native methods
def __init__(self, *args, **kwargs):
super(CustomEasyContactUsForm, self).__init__(*args, **kwargs)
# re-order placement of added attachment field
self.fields.keyOrder.insert(self.fields.keyOrder.index('captcha'),
self.fields.keyOrder.pop(self.fields.keyOrder.index('attachment'))
)
### field defintitions
attachment = forms.FileField()
In the above we are extending an EasyContactUsForm base class as it is defined in django-easycontactus package.
- [Django]-How to make the foreign key field optional in Django model?
- [Django]-Deploying Django with gunicorn and nginx
- [Django]-How do I output HTML in a message in the new Django messages framework?
0👍
I built a form ‘ExRegistrationForm’ inherited from the ‘RegistrationForm’ from Django-Registration-Redux. I faced two issues, one of which was reordering the fields on the html output page once the new form had been created.
I solved them as follows:
1. ISSUE 1: Remove Username from the Registration Form: In my_app.forms.py
class ExRegistrationForm(RegistrationForm):
#Below 2 lines extend the RegistrationForm with 2 new fields firstname & lastname
first_name = forms.CharField(label=(u'First Name'))
last_name = forms.CharField(label=(u'Last Name'))
#Below 3 lines removes the username from the fields shown in the output of the this form
def __init__(self, *args, **kwargs):
super(ExRegistrationForm, self).__init__(*args, **kwargs)
self.fields.pop('username')
2. ISSUE 2: Make FirstName and LastName appear on top: In templates/registration/registration_form.html
You can individually display the fields in the order that you want. This would help in case the number of fields are less, but not if you have a large number of fields where it becomes practically impossible to actually write them in the form.
{% extends "base.html" %}
{% load i18n %}
{% block content %}
<form method="post" action=".">
{% csrf_token %}
#The Default html is: {{ form.as_p }} , which can be broken down into individual elements as below for re-ordering.
<p>First Name: {{ form.first_name }}</p>
<p>Last Name: {{ form.last_name }}</p>
<p>Email: {{ form.email }}</p>
<p>Password: {{ form.password1 }}</p>
<p>Confirm Password: {{ form.password2 }}</p>
<input type="submit" value="{% trans 'Submit' %}" />
</form>
{% endblock %}
- [Django]-Why does DEBUG=False setting make my django Static Files Access fail?
- [Django]-Django: remove a filter condition from a queryset
- [Django]-Django request get parameters
0👍
The above answers are right but incomplete. They only work if all the fields are defined as class variables. What about dynamic form fields which have to be defined in the intitialiser (__init__
)?
from django import forms
class MyForm(forms.Form):
field1 = ...
field2 = ...
field_order = ['val', 'field1', 'field2']
def __init__(self, val_list, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
vals = zip(val_list, val_list)
self.fields['val'] = forms.CharField(choices=vals)
The above will never work for val
but will work for field1
and field2
(if we reorder them). You might want to try defining field_order
in the initialiser:
class MyForm(forms.Form):
# other fields
def __init__(self, val_list, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
vals = zip(val_list, val_list)
self.fields['val'] = forms.CharField(choices=vals)
self.field_order = ['val', 'field1', 'field2']
but this will also fail because the field order is fixed before the call to super()
.
Therefore the only solution is the constructor (__new__
) and set field_order
to a class variable.
class MyForm(forms.Form):
# other fields
field_order = ['val', 'field1', 'field2']
def __new__(cls, val_list, *args, **kwargs):
form = super(MyForm, cls).__new__(cls)
vals = zip(val_list, val_list)
form.base_fields['val'] = forms.CharField(choices=vals)
return form
- [Django]-Unit testing with django-celery?
- [Django]-Django modifying the request object
- [Django]-How do you filter a nested serializer in Django Rest Framework?