How does Django know the order for rendering form fields?

If I have a Django form such as:

class ContactForm(forms.Form): subject = forms.CharField(max_length=100) message = forms.CharField() sender = forms.EmailField() 

And I call the as_table () method of the instance of this form, Django will display the fields in the same order as above.

My question is, how does Django know the order in which class variables are defined?

(Also how to override this order, for example, when I want to add a field from the init method)

+84
python django class django-forms contacts
Dec 08 '08 at 20:37
source share
14 answers

I went ahead and answered my question. Here is the answer for future reference:

Django form.py uses some dark magic using the __new__ method to load class variables into self.fields in the order defined in the class. self.fields is an instance of Django SortedDict (defined in datastructures.py ).

So, to override this, say, in my example, you wanted the sender to come first, but he needed to add it to the init method, you would do:

 class ContactForm(forms.Form): subject = forms.CharField(max_length=100) message = forms.CharField() def __init__(self,*args,**kwargs): forms.Form.__init__(self,*args,**kwargs) #first argument, index is the position of the field you want it to come before self.fields.insert(0,'sender',forms.EmailField(initial=str(time.time()))) 
+38
Dec 08 '08 at 21:12
source share

[NOTE: this answer is currently completely out of date - see discussion below and more recent answers].

If f is a form, its fields are f.fields , i.e. django.utils.datastructures.SortedDict (it represents the elements in the order they were added). After building the form, f.fields has the keyOrder attribute, which is a list containing the names of the fields in the order in which they are presented. You can set this in the correct order (although you need to be careful not to skip items or add additional ones).

Here is an example that I just created in my current project:

 class PrivEdit(ModelForm): def __init__(self, *args, **kw): super(ModelForm, self).__init__(*args, **kw) self.fields.keyOrder = [ 'super_user', 'all_districts', 'multi_district', 'all_schools', 'manage_users', 'direct_login', 'student_detail', 'license'] class Meta: model = Privilege 
+89
Jul 28 '09 at 0:12
source share

New in Django 1.9 is Form.field_order and Form.order_fields () .

 # forms.Form example class SignupForm(forms.Form): password = ... email = ... username = ... field_order = ['username', 'email', 'password'] # forms.ModelForm example class UserAccount(forms.ModelForm): custom_field = models.CharField(max_length=254) def Meta: model = User fields = ('username', 'email') field_order = ['username', 'custom_field', 'password'] 
+46
Dec 28 '15 at 23:05
source share

Fields are listed in the order in which they are defined in ModelClass._meta.fields. But if you want to change the order in the Form, you can do this using the keyOrder function. For example:

 class ContestForm(ModelForm): class Meta: model = Contest exclude=('create_date', 'company') def __init__(self, *args, **kwargs): super(ContestForm, self).__init__(*args, **kwargs) self.fields.keyOrder = [ 'name', 'description', 'image', 'video_link', 'category'] 
+11
Apr 21 2018-11-21T00:
source share

With Django> = 1.7, you need to change ContactForm.base_fields , as shown below:

 from collections import OrderedDict ... class ContactForm(forms.Form): ... ContactForm.base_fields = OrderedDict( (k, ContactForm.base_fields[k]) for k in ['your', 'field', 'in', 'order'] ) 

This trick is used in Django Admin PasswordChangeForm : Source on Github

+6
Dec 22 '14 at 0:13
source share

Form fields have an attribute for the creation order called creation_counter . The .fields attribute is a dictionary, so just adding a dictionary and changing the creation_counter attributes in all fields should be enough to reflect the new order (although they didn’t try to).

+5
Dec 08 '08 at 20:52
source share

Use a counter in the Field class. Sort by this counter:

 import operator import itertools class Field(object): _counter = itertools.count() def __init__(self): self.count = Field._counter.next() self.name = '' def __repr__(self): return "Field(%r)" % self.name class MyForm(object): b = Field() a = Field() c = Field() def __init__(self): self.fields = [] for field_name in dir(self): field = getattr(self, field_name) if isinstance(field, Field): field.name = field_name self.fields.append(field) self.fields.sort(key=operator.attrgetter('count')) m = MyForm() print m.fields # in defined order 

Output:

 [Field('b'), Field('a'), Field('c')] 



+5
May 15 '09 at 10:03
source share

Django 1.7 forms use an OrderedDict, which does not support the append statement. So you need to rebuild the dictionary from scratch ...

 class ChecklistForm(forms.ModelForm): class Meta: model = Checklist fields = ['name', 'email', 'website'] def __init__(self, guide, *args, **kwargs): self.guide = guide super(ChecklistForm, self).__init__(*args, **kwargs) new_fields = OrderedDict() for tier, tasks in guide.tiers().items(): questions = [(t['task'], t['question']) for t in tasks if 'question' in t] new_fields[tier.lower()] = forms.MultipleChoiceField( label=tier, widget=forms.CheckboxSelectMultiple(), choices=questions, help_text='desired set of site features' ) new_fields['name'] = self.fields['name'] new_fields['email'] = self.fields['email'] new_fields['website'] = self.fields['website'] self.fields = new_fields 
+4
Mar 03 '15 at 10:18
source share

For future reference: everything has changed since the new forms. This is one way to reorder fields from base classes of classes that you do not control:

 def move_field_before(form, field, before_field): content = form.base_fields[field] del(form.base_fields[field]) insert_at = list(form.base_fields).index(before_field) form.base_fields.insert(insert_at, field, content) return form 

Also, there is some documentation about SortedDict that uses base_fields here: http://code.djangoproject.com/wiki/SortedDict

+3
Mar 31 '10 at 9:53
source share

If either fields = '__all__' :

 class AuthorForm(ModelForm): class Meta: model = Author fields = '__all__' 

or exclude :

 class PartialAuthorForm(ModelForm): class Meta: model = Author exclude = ['title'] 

Django then refers to the order of the fields as defined in the model . It just bit me, so I thought I mentioned it. It refers to ModelForm Docs :

If any of these are used, the order in which the fields appear on the form will be the order in which the fields in the model are defined, and ManyToManyField instances will appear last.

+2
Dec 05 '14 at 18:19
source share

The easiest way to arrange the fields in django 1.9 forms is to use field_order in your form. Form.field_order

Here is a small example

 class ContactForm(forms.Form): subject = forms.CharField(max_length=100) message = forms.CharField() sender = forms.EmailField() field_order = ['sender','message','subject'] 

This will show everything in the order specified in the field_order dict.

+2
Sep 09 '16 at 13:17
source share

Using fields in the Meta inner class is what worked for me on Django==1.6.5 :

 #!/usr/bin/env python # -*- coding: utf-8 -*- """ Example form declaration with custom field order. """ from django import forms from app.models import AppModel class ExampleModelForm(forms.ModelForm): """ An example model form for ``AppModel``. """ field1 = forms.CharField() field2 = forms.CharField() class Meta: model = AppModel fields = ['field2', 'field1'] 

The easiest way.

+1
Aug 22 '14 at 14:14
source share

This is due to the meta class used to define the form class. I think it stores an internal list of fields, and if you insert it in the middle of the list, this might work. It's been a while since I looked at this code.

0
Dec 08 '08 at 20:41
source share

I used this to move the fields:

 def move_field_before(frm, field_name, before_name): fld = frm.fields.pop(field_name) pos = frm.fields.keys().index(before_name) frm.fields.insert(pos, field_name, fld) 

This works in 1.5, and I'm sure it still works in later versions.

0
Dec 05 '14 at 12:05
source share



All Articles