For work recently I've been doing some Django-related tasks that involve talking to an external API with POSTed forms. Django forms objects are declared by creating a class that inherits from django.forms.Form, with the fields of the form declared by declaring attributes of that class. Which works well and is clean and easy to remember—unless the API you're working with requires a field with the same name as a Python keyword, such as return. You can't declare a field like this as an attribute; it will trigger a syntax error.

I spent some time scratching my head over this, and came up with this as a workaround after source-diving to find out how Form objects actually work:

from django import forms

class ExampleForm(forms.Form):
    def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
            initial=None, errorclass=ErrorList, label_suffix=':', empty_permitted=False, return_url=None):
        forms.Form.__init__(self, data, files, auto_id, prefix, initial,
            errorclass, label_suffix, empty_permitted)
        if return_url is not None:
            self.fields['return'] = forms.CharField(widget=forms.HiddenInput, initial=return_url)

It turns out that the attribute declaration is just syntactic sugar for creating a dictionary of key/value pairs, which is then stored in the fields attribute. So we can monkeypatch in extra values after the translation. Which is somewhat more awkward and ugly, but works in a pinch.

Note that I haven't extensively tested what interactions this may cause with other forms code, so use with some caution.

I guess if you wanted to do this often, it would make sense to create a metaclass to mangle the member names. Say strip one trailing underscore from all names that have (exactly) one, so you'd declare 'return_' and it would become 'return'. It can be done along the lines of the autoprop example from Since python 2.6, the same should possible with class decorator.
Comment by Fri 04 Jun 2010 05:49:14 AM UTC

A somewhat nicer way to would be to use the 'type' constructor:

ExampleForm = type('ExampleForm', (forms.Form,), {'return': forms.CharField()})

That way, it's guaranteed to hit the same codepaths as if you wrote the form out "longhand".

Comment by lamby Fri 04 Jun 2010 07:43:33 AM UTC