Dynamic forms with Django

by shabda on October 10, 2008

Newforms, (or forms now) are without doubt one of the coolest features of Django. (Of course after Admin, Localflavor, and many others). Here is some sample code.

class EmployeeForm(forms.Form):
    name = forms.CharField()
    age = forms.IntegerField()
    resume = forms.FileField()

Just this code gives you

  1. A form which knows how to render itself as Html.
  2. A form which knows how to validate data on the server side.
  3. A form which knows how to show the relevant errors.

However think of this scenario,

You need to customise your form depending on values in the Database.

What you want to do is

class EmployeeForm(forms.Form):
    name = forms.CharField()
    "Show more fields depending on the Values in DB for a specific employees."
    #....
    #....
    #....

You can do this without resorting to any black magic. Here is the code to do so,

#in models.py

type_mapping = {'CharField':forms.CharField(max_length = 100), 'TextField': forms.CharField(widget = forms.Textarea),
        'BooleanField':forms.BooleanField(required = False),
        'URLField': forms.URLField(), 'EmailField': forms.EmailField()
        }

class EmployeeFieldModel(models.Model):
    "Model for employee form fields for a specific Job board."
    employee = models.ForeignKey(Employee)
    name = models.CharField(max_length = 100)
    type = models.CharField(max_length = 100)
    order = models.IntegerField()

#in forms.py

def get_employee_form(employee):
    """Return the form for a specific Board."""
    employee_fields = EmployeeFieldModel.objects.filter(employee = employee).order_by('order')
    class EmployeeForm(forms.Form):
    def __init__(self, *args, **kwargs):
        forms.Form.__init__(self, *args, **kwargs)
        self.employee = employee
    def save(self):
        "Do the save"
    for field in employee_fields:
    setattr(EmployeeForm, field.name, copy(type_mapping[field.type]))
    return type('EmployeeForm', (forms.Form, ), dict(EmployeeForm.__dict__))

#in views.py

employee = Employee.objects.get(employee_slug)
get_employee_form(employee)

PS. A similar techniques works for Dynamic models.
PPS. Yes I have worked with Oracle. Yes, all my data models start with Employee and Departments.


Want to build an Web Application. Talk to Usware

Related posts:

  1. Doing things with Django forms
  2. Doing things with Django models – aka – Django models tutorial
  3. Django aggregation tutorial
  4. Django quiz

{ 2 trackbacks }

Doing things with Django forms — The Usware Blog - Django Web Development
January 14, 2010 at 7:26 am
links for 2010-05-10 « Object neo = neo Object
May 10, 2010 at 11:31 pm

{ 10 comments… read them below or add one }

1 Brian Beck October 10, 2008 at 10:17 am

Cool. Just a note, ‘dynamic form’ is a bit general, I think an accurate description of what you’re doing is generating a form for a “vertical” model.

2 Brian Beck October 10, 2008 at 10:23 am

On second thought, you’re not storing the values vertically so it really is just a dynamic form description.

3 Andrew Davison October 23, 2008 at 7:01 am

Many thanks. This saved me a lot of digging through the Django source code.

4 Adam Smith June 16, 2009 at 3:11 am

Cool. Looks pretty good and interesting.

5 chris June 25, 2009 at 12:18 am

Do you think you could give a demo of what it looks like so I can see it?

You have “do the save”. Does it need an override and you just haven’t written it? What does your views.py look like if so?

Also, are the fields that are put in dynamically in the “type_mapping” variable?

6 shabda July 7, 2009 at 3:00 am

Chris:

Chris: Cant give a demo, its a intranet only app.

You have “do the save”. Does it need an override and you just haven’t written it? What does your views.py look like if so?

Yes, override saving the form data to the db.

Also, are the fields that are put in dynamically in the “type_mapping” variable?

Db has type(bad name btw, type is a builtin, mea culpa), which decides what form filed to show using type_mapping

type_mapping = {‘CharField’:forms.CharField(max_length = 100), ‘TextField’: forms.CharField(widget = forms.Textarea), ‘BooleanField’:forms.BooleanField(required = False), ‘URLField’: forms.URLField(), ‘EmailField’: forms.EmailField() }

7 aexl July 30, 2009 at 6:56 pm

in the drupal world there is a powerful pattern, where one app (they call module) can (1) extend the form of another app before rendering, and (2) catch the form values from the post. this might be an essential part for doing this in django. if i understand it right to do the same we just would need the form to send “before render” and “before save” signals. right?

8 Mert Nuhoglu September 1, 2009 at 2:44 am

Thanks. This was what I was looking for exactly.

I have one small addition: James Bennett suggests to use forms.BaseForm instead of forms.Form as base class for dynamically generated Form classes due to some technical reasons.

9 brainiac September 17, 2009 at 7:47 am

Thats a really useful trick, thanks for that! Just out of curiosity, is it necessary to use copy on the field that is looked up in type_mapping?

10 shabda September 18, 2009 at 6:40 am

Yes. Django sets ordering on fields, when they are instantaited. If you do not copy, the fileds will be out of order.

Leave a Comment

Additional comments powered by BackType

Previous post:

Next post: