django forms and where to add dynamic choices
July 2010
Sun Mon Tue Wed Thu Fri Sat
        1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
About
This site is an effort to share some of the base knowledge I have gathered through all this years working with Linux, FreeBSD, OpenBSD, Python or Zope, among others. So, take a look around and I hope you will find the contents useful.
Recent Entries
Recent Comments

collective.solr 1.0rc1 (Release candidate)
2010-07-30 plone.org releases

Products.cron4plone 1.1.5rc1 (Release candidate)
2010-07-30 plone.org releases

vs.dashboardmanager 0.2.6.1
2010-07-30 plone.org releases

Heads up! OpenBSD now supports multi-byte characters!
2010-07-30 OpenBSD Journal (undeadly.org)

Setting the Focus Distance on the Epson V700 Scanner
2010-07-29 betabug

Gnome Census Released (and Red Hat 16% vs Canonical 1% Flame)
2010-07-29 Ramble on

Cómo conectarse a bases de datos SQLite desde NetBeans
2010-07-29 vaites (dmnet)

Diferencias cambiando de Perl a Python
2010-07-28 blackshell

Monos y cacahuetes
2010-07-28 userlinux.net

No Gazoline
2010-07-28 betabug

[c2k10] (Part 5)
2010-07-28 OpenBSD Journal (undeadly.org)

Cómo evitar fbc_channel=1 con Facebook Fan/Like Box
2010-07-28 vaites (dmnet)

Copyright Nonsense
2010-07-28 Ramble on

New Plone Usergroup in Charlottesville, VA kicks off July 29th
2010-07-28 plone.org news

Redimensionar la ventana de Firefox sin extensiones
2010-07-27 vaites (dmnet)

ἀφορισμός XII: Silencio
2010-07-27 emereci

New committer: Baptiste Daroussin (ports)
2010-07-27 FreeBSD latest news

[c2k10] The Hackathon BBQ (Part 4) - June 25 - July 3, 2010, Edmonton, Alberta, Canada
2010-07-26 OpenBSD Journal (undeadly.org)

Setting up Bacula
2010-07-26 Evilcoder

Plone 4 upgrade coming to plone.org
2010-07-25 plone.org news

So I bought a Scanner
2010-07-24 betabug

FreeBSD 8.1 RELEASED
2010-07-24 Evilcoder

FreeBSD 8.1-RELEASE Available
2010-07-23 FreeBSD latest news

Announcing Tornado 1.0
2010-07-23 Ramble on

Sauna Sprint just around the corner
2010-07-22 plone.org news

April-June, 2010 Status Report
2010-07-22 FreeBSD latest news

O culeiro
2010-07-21 emereci

Limitando usuarios ssh en Mercurial
2010-07-21 userlinux.net

The Wire
2010-07-20 emereci

Comienza la mudanza, nos vamos a Reading
2010-07-18 blackshell

Recent Trackbacks
Categories
OpenBSD (8 items)
BSD (0 items)
FreeBSD (12 items)
Linux (2 items)
Security (3 items)
Python (18 items)
Zope (13 items)
Daily (120 items)
e-shell (8 items)
Hacks (7 items)
PostgreSQL (3 items)
OSX (7 items)
Nintendo DS (0 items)
enlightenment (0 items)
Apache (3 items)
Nintendo Wii (0 items)
Django (23 items)
Music (9 items)
Plone (7 items)
Varnish (0 items)
Lugo (1 items)
Sendmail (-1 items)
europython (7 items)
Archives

Syndicate this site (XML)

RSS/RDF 0.91

22 enero
2009

django forms and where to add dynamic choices

is it forms.py, is it views.py, where?

In django, you can use the so-called ModelForms to generate a form directly from a model definition. This is pretty useful, as you can have a models.py file like, for example:

class Project(models.Model):
    name = models.CharField('Name of the project', max_length=100)
    code = models.CharField('Code number of the project', max_length=10)
    creator = models.ForeignKey(User, related_name='created_projects')
    maintainer = models.ManyToManyField(User, related_name='maintained_projects')

In this case we are creating a Project model, that is, a model to define information we will handle for those projects (this is an example, of course it would have more fields). The first two fields are CharFields (text fields where we will save the name and a numeric code for the project). The next two fields are ForeignKey and ManyToManyField fields (links to another model, this time User, which is django.contrib.auth.models.User, that is, the default django user model).

I've added two different relations to the User model, one will be the creator of the project, the other one the maintainer (or maintainers) of the project.

Now, back to ModelForms, I can create an HTML form just adding something like that to the app forms.py:

from myproject.myapp.models import Project

class ProjectForm(forms.ModelForm):
    class Meta:
        model = Proyecto

Once you've defined the form, you can get an html form just doing:

from myproject.myapp.forms import ProjectForm

myform = ProjectForm()

and you can use the form as described in the forms documentation (I'll not step there in detail).

But there is one problem with that approach. Imagine I've created two different groups (using django.contrib.auth.models.Group), let's call them project creators and project maintainers. Only users in the project creators group should be available in the creator field and users in the project maintainers group should be available to select for the the maintainer field.

Well, that's easy, we only have to modify a bit our forms.py:

from myproject.myapp.models import Project
from django.contrib.auth.models import Group

class ProjectForm(forms.ModelForm):

    creator_choices = [(c.id, c.username) for c in Group.objects.get(name__icontains='creator').user_set.all()]
    maintainer_choices = [(m.id, m.username) for m in Group.objects.get(name__icontains='maintainer').user_set.all()]

    creator = forms.ChoiceField(required=True, label='Project creator', choices=creator_choices)
    maintainer = forms.MultipleChoiceField(required=True, label='Project maintainer(s)', choices=maintainer_choices)

    class Meta:
        model = Proyecto

Ok, what we've got here is that I've overwrote or replaced the default creator and maintainer fields automatically generated by django, setting two new fields. This allow me to set certain default options for the fields, like the list of choices available (which I've generated searching for users in the needed groups).

Now a user will be able to select only users in the project creators group in the creator field and users in the project maintainers group in the maintainer field.

BUT, there is a problem with that. Doing it that way, the list of choices for each field will be generated on startup time (when you launch the django development server or apache or lighttpd or nginx or whatever you are using). That means that if you add a new user to the maintainers group, it will not appear in the maintainer field until you restart your server!

To avoid that, we will need to add the current available choices before using the form, overwriting the default list of choices:

from myproject.myapp.forms import ProjectForm
from django.contrib.auth.models import Group

creator_choices = [(c.id, c.username) for c in Group.objects.get(name__icontains='creator').user_set.all()]
maintainer_choices = [(m.id, m.username) for m in Group.objects.get(name__icontains='maintainer').user_set.all()]

creator = forms.ChoiceField(required=True, label='Project creator', choices=creator_choices)
maintainer = forms.MultipleChoiceField(required=True, label='Project maintainer(s)', choices=maintainer_choices)

myform = ProjectForm()

myform.fields['creator'].choices = creator_choices
myform.fields['maintainer'].choices = maintainer_choices

And that's it! This way the auto-generated html form will show up-to-date information in the select combos.

Posted by wu at 14:24 | Comments (0) | Trackbacks (0)
<< I've got flu | Main | OpenBSD, PyCha and the Memory Error. >>
Comments
Re: django forms and where to add dynamic choices

This is awesome! Exactly what I was looking for! There is one oddity in the code though. Shouldn't the lines that read "model = Proyecto" be "model = Project" ?

Thank you much, this worked perfectly!

Posted by: Shane C. Mason at abril 27,2009 00:09
Re: django forms and where to add dynamic choices

Yes, you are right (my fault, as the code is a copy of current code within one of my projects, which is coded in spanish :))

Thnx for the correction.

Posted by: Wu at abril 27,2009 09:55
Re: django forms and where to add dynamic choices

Nice one Wu. Though, in your second example, you could've used the ``ModelChoiceField`` and ``ModelMultipleChoiceField`` like so:


class ProjectForm:
creator = forms.ModelChoiceField(queryset=User.objects.filter(groups__name__icontains='creator')
maintainer = forms.ModelMultipleChoiceField(queryset=User.objects.filter(groups__name__icontains='maintainer')

class Meta:
model = Project

Like you said, this'll evaluate the querysets when your form is created. For dynamic choices, I prefer to override the form's ``__init__`` method, and create them there:

class ProjectForm:
class Meta:
model = Project

def __init__(self, *args, **kwargs):
super(ProjectForm, self).__init__(*args, **kwargs)
self.fields['creator'].queryset = User.objects.filter(groups__name__icontains='creator')
self.fields['maintainer'] = User.objects.filter(groups__name__icontains='maintainer')

Cheers!

Posted by: Stan at mayo 07,2009 16:15
Trackbacks
Please send trackback to:http://blog.e-shell.org/130/tbping
There are no trackbacks.
Post a comment