How Django's form field ordering works

May 25, 2011

In Django, forms are defined in what seems to be the conventional approach; each form is a Python class, and fields are variables defined in the class definition (what would be class variables if this was anything like a conventional Python class). Although I don't think that the Django documentation ever states this explicitly, the natural order of form fields in the form (the order that is used if you just tell the form to render itself as HTML or iterate over every field) is the order that you defined the fields in. All of this is perfectly natural and intuitive at first glance.

If you know Python, though, you are probably very startled about right now. You see, in Python class elements have no particular ordering. For most classes the class namespace is literally a Python dictionary, and dictionaries have no particular set order of elements. Certainly you can't work out the order that entries were created in them, and thus for normal classes there's no way to know which order the fields were defined in.

If you know a bit of Python, you might expect that Django is doing something funny with a metaclass. But it turns out that it's not, because even a metaclass can't get this information; metaclasses take effect after the class namespace has been set up (more or less). What Django is doing is simpler and more clever than a metaclass; it's keeping count. Literally. The parent class for all form fields has a global counter, and every time you create a form field Django saves the current counter value in the new field instance and increments it. Then when it wants to know what order fields were created in, it just looks at all of their counter values.

(I have to admire this approach; it's a great triumph of brute force over trying too hard to be clever.)

This has important consequences if you define fields outside of specific forms, perhaps because you want to reuse the same complex field definition between several related forms. Unless you are doing something that specifically dictates field ordering, the global order that the fields were defined in will control their order in a specific form and all fields defined outside the form will be ordered before any fields defined just in it. If you dynamically create fields (and perhaps forms) on the fly, I suspect that things get even harder to keep track of.

This also means that you can artificially manipulate the field order if you absolutely have to. I'm not going to tell you what internal variable to change on field instances, because anyone who ought to be doing this can find the relevant variable in the Django source (and re-find it again if the Django people change its name or the implementation of all of this). The usual cautions apply.

(All of this is for Django 1.2.5. Yes, we're slightly behind the times.)

Sidebar: how this interacts with inherited form classes

Suppose you have one Form class that inherits from another Form class, and both classes define fields. Better yet, suppose that both Form classes contain fields defined globally, in some order. What order are the fields in?

As of Django 1.2.5, the answer appears to be that each separate Form class has its own internal order for its own fields. When you subclass a Form class, all of your fields (in your order) go before your parent's fields (in its order), which are before any parent it may have, and so on.

Don't ask how this interacts with multiple inheritance, because I don't know and I'm not going to do the experiments to find out.


Comments on this page:

From 98.223.163.40 at 2011-05-27 09:11:53:

So what this means for those of us that don't want (or don't have the Python skill) to experiment with internals, is that we can't really rely on field order over the long term. As parent classes change, or if Django itself has a modification under the hood, the order may change.

By cks at 2011-05-27 11:08:46:

If you ignore parent classes, I think that Django is going to keep the default field order; whether officially documented or not, it's something that people have come to count on. If they change it, it will be marked as a big explicit change. Exactly how they implement this in the Python code may change. If it changes, people who are playing around in the insides of the implementation (to change the default field order, for example) get into trouble. But people who just use the feature are fine.

People who use parent classes with fields, uhh. You have a point. If this was an issue for me, I would start trying to put together a Django change (and get it accepted by the core) to let you explicitly specify the field order in a particular Form class, so that you can control ordering but not hard-code the fields (all the fields) into your template.

Written on 25 May 2011.
« Proper SQL database design versus the real world
More not supporting random query parameters in URLs »

Page tools: View Source, View Normal.
Search:
Login: Password:

Last modified: Wed May 25 01:50:58 2011
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.