How does Django Know the Order to Render Form Fields? How does Django Know the Order to Render Form Fields? python python

How does Django Know the Order to Render Form Fields?


[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


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

# forms.Form exampleclass SignupForm(forms.Form):    password = ...    email = ...    username = ...    field_order = ['username', 'email', 'password']# forms.ModelForm exampleclass UserAccount(forms.ModelForm):    custom_field = models.CharField(max_length=254)    def Meta:        model = User        fields = ('username', 'email')    field_order = ['username', 'custom_field', 'password']


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())))