Wandering Thoughts archives

2019-10-16

Some magical weirdness in Django's HTML form classes

In Django, HTML 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. This looks like this, to crib a skeleton from our Django web application :

class RequestForm(forms.Form):
  login = forms.CharField([...])
  name  = forms.CharField([...])
  email = forms.EmailField([...])
  aup_agree = forms.BooleanField([...])

You instantiate an instance of RequestForm either with initial data (when you're displaying the form for the first time) or with data from the POST or GET form, and then call various standard Django API functions on it and so on. It's all covered in the Django documentation.

What I didn't remember until I started looking just now is that this Python class definition we wrote out is kind of a lie. Django model classes mostly work look like they look, so for example the model version of a Request has a Request.login attribute just as its class definition does. Forms are significantly different. Although we set up what looks like class attributes here, our actual RequestForm class and class instances do not have, say, a RequestForm.login attribute. All of the form fields we seemed to define here get swept up and put in Form.fields.

At one level this is documented and probably the safest option, given that the data ultimately comes from an untrusted source (ie, a HTTP request). It also means that you mostly can't accidentally use a form instance as a model instance (for example, by passing the wrong thing to some function); if you try, it will blow up with attribute errors.

(The 'mostly' is because you can create a login attribute on a RequestForm instance if you write to it, so if a function that writes to fields of a model instance is handed a form instance by accident, it may at least half work.)

At another level, this is another way that Django's classes are non-Python magic. What looks like class attributes aren't even properties; they've just vanished. Conventional Python knowledge is not as much use for dealing with Django as it looks, and you have to know the API (or look it up) even for things that look like they should be basic and straightforward.

(I don't have an opinion any more about whether the tradeoffs here are worth it. Our Django app just works, which is what we really care about.)

python/DjangoFormClassMagic written at 21:54:06; Add Comment


Page tools: See As Normal.
Search:
Login: Password:
Atom Syndication: Recent Pages, Recent Comments.

This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.