Doing things with Django forms

by shabda on January 14, 2010

Forms are one of the best features of Django. (After models, admin, url routing etc :) ). Here is a quick tutorial describing how to do things with Django forms.

  1. Basic form

Prob. You want to show a form, validate it and display it.

Ans. Create a simple form.

class UserForm(forms.Form):
    username = forms.CharField()
    joined_on = forms.DateField()

This wil take care that the form is displayed with two text field, and a value for them are filled in, and the second field has correct formatting for a date.

2.

Prob. Create a form which has values populated depending upon a runtime value, eg you might want to show only the values corresponding to the current subdomain.

Ans. Create a form with an custom __init__.

class UserForm(forms.Form):
    username = forms.CharField()
    plan = forms.ModelChoiceField(queryset = Plan.objects.none())

    def __init__(self, subdomain, *args, **kwargs):
        self.default_username = default_username
        super(UserForm, self).__init__(*args, **kwargs)
        self.fields['plan'].queryset = Plan.objects.filter(subdomain = subdomain)

Here in the __init__ we are overriding the default queryset on field plan. Any of the attributes can similarly be overridden.

However the self.fields is populated only after super(UserForm, self).__init__(*args, **kwargs) is called.

3.

Prob. The same form is used in multiple views, and handles the cleaned_data similarly.

Ans. Create a form with a custom .save()

class UserForm(forms.Form):
    username = forms.CharField()

    def save(self):
        data = self.cleaned_data
        user = User.objects.create(username = data['username'])
        #create a profile
        UserProfile.objects.create(user = user, ...some more data...)

You could have called this method anything, but this is generally called save, to maintain similarity with ModelForm

4.

Prob. You need to create a form with fields which have custom validations.

Ans. Create a form with custom clean_fieldname

class UserForm(forms.Form):
    username = forms.CharField()

    def clean_username(self):
        data = self.cleaned_data
        try:
            User.objects.get(username = data['username'])
        except User.DoesNotExist:
            return data['username']
        raise forms.ValidationError('This username is already taken.')

Here we can validate that the usernames are not repeated.

5.

Prob. You want to create a field which has cross field validations.

Ans. Create a field with a .clean

class UserForm(forms.Form):
    username = forms.CharField()

    password1 = forms.PasswordField()
    password2 = forms.PasswordField()

    def clean(self):
        data = self.cleaned_data
        if "password1" in data and "password2" in data and data["password1"] != data["password2"]:
            raise forms.ValudationError("Passwords must be same")

6.

Problem.

You need a form the fields of which depends on some value in the database. Eg. The field to be shown are customisable per user.

Create a form dynamically

def get_user_form_for_user(user):
        class UserForm(forms.Form):
            username = forms.CharField()
            fields = user.get_profile().all_field()
            #Use field to find what to show.

This post provides much more details

7.

Prob. You need to create a Html form which writes to multiple Django models.

Ans. Django forms are not a one to one mapping to Html forms.

#in forms.py
class UserForm(forms.ModelForm):
    class Meta:
        model = User
        fields = ["username", "email"]

class UserProfileForm(forms.ModelForm):
    class Meta:
        model = UserProfile

#in views.py
def add_user(request):
    ...
    if request.method == "POST":
        uform = UserForm(data = request.POST)
        pform = UserProfileForm(data = request.POST)
        if uform.is_valid() and pform.is_valid():
            user = uform.save()
            profile = pform.save(commit = False)
            profile.user = user
            profile.save()
            ....
    ...

#in template
<form method="post">
    {{ uform.as_p }}
    {{ pform.as_p }}
    <input type="submit" ...>
</form>

8.

Prob. You want to use multiple forms of same type on one page.

Ans.

a. If you want a datagrid style ui, use formset.

from django.forms.formsets import formset_factory
forms = formset_factory(UserForm, extra = 4)
#

Formsets are described much more comprehensively here

b. If you do not need a datagrid style ui, use prefix argument to forms.

Eg. you have a survey app, and you want a page with all questions from that survey displayed.

#IN views.py
def survey(request, survey_slug)
    ...
    questions = survey.questions.all()
    question_forms = []
    for question in questions:
        qform = QuestionForm(question=question, prefix = question.slug)
        question_forms.append(qform)
        ...
    if request.method == "POST":
        for question in questions:
            qform = QuestionForm(question=question, prefix = question.slug, data = request.POST)
        #Validate and do save action
        ...
    ...

Do you subscribe to our feed? We recently made a full text feed available, so if you are using the old feed, you should change it. Subscribe now.

Related posts:

  1. Dynamic forms with Django
  2. Five Things I Hate About Django.
  3. Develop Twitter API application in django and deploy on Google App Engine
  4. Aren’t django signals a little like comefrom?
  5. Doing things with Django models – aka – Django models tutorial

7 Comments 24 Tweets 4 Comments

{ 1 trackback }

Weekly Digest for January 16th | William Stearns
January 16, 2010 at 11:11 pm

{ 40 comments… read them below or add one }

1 kevin January 14, 2010 at 10:19 am

Nice round up of use cases and solutions. This is a good reference for noobs and experienced Django devs. Thanks.

2 Dave January 14, 2010 at 11:39 am

Cool post. Thanks. Suggestion for future post: How to use static values in a formset. For example display a list view of items, but with an enable/disable check box on each item. I tried using a formset containing a BooleanField, but could not figure out how to add static fields to it. And for extra credit, update check box value via AJAX.

3 Zach January 14, 2010 at 12:42 pm

Thanks for this post. Very helpful!

4 shabda January 14, 2010 at 2:55 pm

Dave:

I dont think you can use formset for that, you should use the second method, and set prefixes manually, eg

for question in survey.questions.all(): qform = SurveyForm(survey, prefix = question.slug) if questions.is_optional: qform.set_optional()

Etc

5 nix January 16, 2010 at 12:55 pm

I like Django! Thanks for info about this

6 geo January 16, 2010 at 6:32 pm

Hey, at the code snippet at number 4, shouldn’t it be “except User.DoesNotExist” instead of “User.DoesNotExist:” ?

7 shabda January 17, 2010 at 2:11 am

@geo: Thanks, updated the posts.

8 Andy January 19, 2010 at 11:51 pm

Foe no. 6, the definition of the class UserForm changes depending on which employee is involved. if I just create an new instance of UserForm, how does Python know which definition to use?

9 Nicola Culli April 9, 2010 at 5:45 pm

Hi, i tried that example and i didn’t get one part working from the beginning, how come it doesn’t work for me?

13 JimH10 January 14, 2010 at 9:32 am

Good link; thanks. There are a lot of things in here that took me quite a while to dope out.

This comment was originally posted on Reddit

18 paddie January 14, 2010 at 3:16 pm

Agreed, forwarded this to a couple of my friends who have just started developing for Django.. very nice examples

This comment was originally posted on Reddit

21 recurser January 14, 2010 at 12:29 pm

Nice summary.

This comment was originally posted on Hacker News

22 old-gregg January 14, 2010 at 12:53 pm

Forms are the biggest difference between Rails itself and Rails-inspired Python frameworks (I’m talking about Pylons’s default helpers, Toscawidgets and Django here)After working with Rails for 3 years I am absolutely don’t get the need for "form" classes. And indeed, judging by open sourced projects and posted code examples, submit handlers in Django and Pylons always look insanely complex as compared to Rails.

This comment was originally posted on Hacker News

24 kgrin January 14, 2010 at 1:52 pm

Django isn’t Rails-inspired – they were developed at around the same time, with no real awareness of each other (at first).As to the merits of "form" classes and post handlers, I think the complexity is only there if you need it, if you’re doing something more interesting (read: complex) than CRUD-type inserts/updates and basic validation (if that’s all you’re doing, ModelForms are pretty close to magical and light on syntax).

This comment was originally posted on Hacker News

25 ubernostrum January 14, 2010 at 6:56 pm

Shameless self-promotion: * [Intro to the forms library, part 1](http://www.b-list.org/weblog/2007/nov/22/newforms/) (from not long after it was added, back when it was `django.newforms` instead of just `django.forms`) * [Part 2](http://www.b-list.org/weblog/2007/nov/23/newforms/) * [Dynamic forms](http://www.b-list.org/weblog/2008/nov/09/dynamic-forms/)

This comment was originally posted on Reddit

26 JimH10 January 14, 2010 at 8:15 pm

I have those bookmarked; they were a big help also. Thank you for those.

This comment was originally posted on Reddit

28 paddie January 14, 2010 at 9:12 pm

Dude, where was this 1 month ago when I really needed it?! Shit, bookmarked!

This comment was originally posted on Reddit

29 cjmaynar January 14, 2010 at 10:03 pm

I’ve got a question for you. I’ve got a dynamic form set up with a foreign key pointing to another model. I’ve been unable to find an easy way of including an add button into the form so that the user can add an entry to the other model and have it immediately reflected on the form that they were editing before. The way i’m envisioning it, it would work similar to the way that kind of thing is handled in the admin section. Could you perhaps point me in the right direction?

This comment was originally posted on Reddit

33 kaveri January 15, 2010 at 2:02 am

Django forms (and their non-Django counterpart, WTForms) are very easy to use, with a declarative syntax.The advantage is that validation is decoupled from models. Often you need to handle validation which doesn’t directly involve a single model – for example sending an email, or handling updates to a number of models. This saves you having to litter your controller/views with validation code.

This comment was originally posted on Hacker News

34 scorpion032 January 15, 2010 at 7:39 am

Perhaps, a side block next to articles, linking to other articles in the same tag, and some random articles might be helpful?

This comment was originally posted on Reddit

Leave a Comment

Additional comments powered by BackType

Previous post:

Next post: