django forms and where to add dynamic choices
March 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

Notifica 1.0.1
2010-03-11 plone.org releases

Created using the AutoMotivator (made with secret alien...
2010-03-10 Ramble on

Finance Fields 1.0
2010-03-10 plone.org releases

Upfront Contacts 0.6
2010-03-10 plone.org releases

Your lovers won't kiss
2010-03-10 emereci

Ejecución directa de “comandos stream” via SSH
2010-03-10 vaites (dmnet)

Self-motivator: Programming You might not know him. You should....
2010-03-09 Ramble on

La Copy & Paste Web
2010-03-09 blackshell

Branco
2010-03-09 emereci

Melodía
2010-03-09 emereci

Speed test between django_mongokit and postgresql_psycopg2
2010-03-09 peterbe.com

In July
2010-03-09 Ramble on

Dark Yellow Morning Sky
2010-03-09 betabug

Vende enlaces con backlinks.com
2010-03-09 userlinux.net

Marc Espie on portability
2010-03-09 OpenBSD Journal (undeadly.org)

OpenSSH 5.4 released
2010-03-09 OpenBSD Journal (undeadly.org)

Mercurial en Fedora Core 4 y CentOS 5
2010-03-08 userlinux.net

How and why to use django-mongokit (aka. Django to MongoDB)
2010-03-08 peterbe.com

Planting Trees
2010-03-07 betabug

Returning committer: Niels Heinen (ports)
2010-03-07 FreeBSD latest news

pjsua: The Geek Out SIP Client
2010-03-06 betabug

Ubuntu Cola or Ubuntu Linux
2010-03-06 peterbe.com

DbWrench Database Design & Synchronization v1.6.3
2010-03-05 PostgreSQL latest news

High performance Grails with memcached
2010-03-04 Oliver's place (django)

Notificador para Spotify en Linux sobre Wine
2010-03-04 vaites (dmnet)

FreeBSD 7.3-RC2 Available
2010-03-04 FreeBSD latest news

Global hotkeys para Spotify en Linux sobre Wine
2010-03-03 vaites (dmnet)

"[…] when researchers extract a single food from a diet of proven value, it usually fails to..."
2010-03-03 Saâd Kadhi / The Web self()

New committer: Neel Natu (src)
2010-03-03 FreeBSD latest news

DynDNS, ddclient y mundo-r
2010-03-02 userlinux.net

Recent Trackbacks
Categories
OpenBSD (8 items)
BSD (0 items)
FreeBSD (11 items)
Linux (1 items)
Security (3 items)
Python (18 items)
Zope (12 items)
Daily (104 items)
e-shell (7 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)
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