[Django]-How does Django Know the Order to Render Form Fields?

40πŸ‘

βœ…

I went ahead and answered my own question. Here’s the answer for future reference:

In Django form.py does some dark magic using the __new__ method to load your class variables ultimately into self.fields in the order defined in the class. self.fields is a Django SortedDict instance (defined in datastructures.py).

So to override this, say in my example you wanted sender to come first but needed to add it in an 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())))
πŸ‘€Greg

102πŸ‘

New to 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']
πŸ‘€Steve Tjoa

90πŸ‘

[NOTE: this answer is now pretty completely outdated – please see the discussion below it, and more recent answers].

If f is a form, its fields are f.fields, which is a django.utils.datastructures.SortedDict (it presents the items in the order they are added). After form construction f.fields has a keyOrder attribute, which is a list containing the field names in the order they should be presented. You can set this to the correct ordering (though you need to exercise care to ensure you don’t omit items or add extras).

Here’s an example 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
πŸ‘€holdenweb

11πŸ‘

Fields are listed in the order they are defined in ModelClass._meta.fields. But if you want to change order in Form, you can do by using 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']
πŸ‘€Hugh Saunders

6πŸ‘

With Django >= 1.7 your must modify ContactForm.base_fields as 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

πŸ‘€Zulu

5πŸ‘

Form fields have an attribute for creation order, called creation_counter. .fields attribute is a dictionary, so simple adding to dictionary and changing creation_counter attributes in all fields to reflect new ordering should suffice (never tried this, though).

πŸ‘€zgoda

5πŸ‘

Use a counter in the Field class. Sort by that 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')]

πŸ‘€nosklo

4πŸ‘

If either fields = '__all__':

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

or exclude are used:

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

Then Django references the order of fields as defined in the model. This just caught me out, so I thought I’d mention it. It’s referenced in the ModelForm docs:

If either of these are used, the order the fields appear in the form will be the order the fields are defined in the model, with ManyToManyField instances appearing last.

πŸ‘€Paul J

4πŸ‘

As of Django 1.7 forms use OrderedDict which does not support the append operator. So you have 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 
πŸ‘€Paul Kenjora

3πŸ‘

For future reference: things have changed a bit since newforms. This is one way of reordering fields from base formclasses you have no control over:

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’s a little bit of documentation about the SortedDict that base_fields uses here: http://code.djangoproject.com/wiki/SortedDict

2πŸ‘

The easiest way to order 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 you specified in field_order dict.

πŸ‘€Tahir Fazal

2πŸ‘

To add something, you can use this (Django 3+):

class ...(forms.ModelForm):
    field = ...

    class Meta:
        model = Xxxxxx
        fields = '__all__'
    
    field_order = ['field', '__all__']

__all__ works

πŸ‘€Filip Vasic

1πŸ‘

Using fields in inner Meta 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']

As simple as that.

πŸ‘€ariel17

1πŸ‘

I’ve used this to move fields about:

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 reasonably sure it still works in more recent versions.

πŸ‘€Ian Rolfe

1πŸ‘

None of these answers worked for me, Actually, you do not have to do anything custom, you can just order the fields in the order you want in your Model class. For eg … the below code

from django.db import models


class Student(models.Model):
    class Meta:
        verbose_name_plural = "categories"

    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=300)
    nick_name = models.CharField(max_length=300)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.name

Your admin interface for model Will display the fields exactly in the same order in which you have declared in this case it will be (id, name, nick_name )

πŸ‘€Aravind.HU

0πŸ‘

It has to do with the meta class that is used in defining the form class. I think it keeps an internal list of the fields and if you insert into the middle of the list it might work. It has been a while since I looked at that code.

πŸ‘€Sam Corder

0πŸ‘

The order of the fields in the form depends on the order of the enumeration in the View , tested in Django 4.0.5.

class Sec_CreateView(CreateView):
    model = Sec
    template_name = 'forms/sec_create.html'
    fields = ['rto', 'ssid', 'lic', 'IPv4', 'vlans']
πŸ‘€Valer

Leave a comment