2. What Are Forms For?
•They are a way to get data from the
user
3. django.forms
•Encapsulate forms into objects
•Know how to validate our data
•In the case of ModelForms, they
know how to save data
•Formsets: Multiple of the same
type of Form
•Understand part of the idea of the
HTTP request/response cycle
4. The 20 second recap
from django import forms
from myapp.models import MyModel
class MyForm(forms.Form):
my_field = forms.CharField()
other_field = forms.BooleanField(widget=forms.Select)
class MyModelForm(forms.ModelForm):
class Meta:
model = MyModel
5. Going beyond the basics
•What you just saw is how 90% of
people use django.forms
•That's using classes to define static
forms that don't change
•But that's not how most
applications are
•What are some common tasks that
aren't static, or otherwise handled
by this setup?
6. Problems to Solve
•Multiple forms of different types
•Altering a form's options
dynamically
•Building entire forms dynamically
7. Multiple Forms
•Formsets let us handle multiple
forms of one type, for example
letting a user enter multiple books
•We can manually pass around
multiple forms
user_form = UserForm()
profile_form = UserProfileForm()
return render_to_response({
'user_form': user_form,
'profile_form': profile_form,
})
8.
9. Doing a bit Better
•That's really ugly, doesn't scale well
•What would be easier?
•One class to hold all of our forms
•It should act like it's just a normal
form object where possible
•How do we build this?
•Normal python tools of the trade,
dynamically creating classes
10. def multipleform_factory(form_classes,
form_order=None):
if form_order:
form_classes = SortedDict([
(prefix, form_classes[prefix])
for prefix in form_order])
else:
form_classes =
SortedDict(form_classes)
return type('MultipleForm',
(MultipleFormBase,),
{'form_classes': form_classes})
11. How type() works
type(object) -> returns the class of the object,
type(1) == int
type([]) == list
type(name, bases, attrs) -> returns a new class named
{{ name }}, inherits from {{ bases}}, and has attributes of
{{ attrs}}
class MyForm(forms.Form):
my_field = forms.CharField()
type('MyForm', (forms.Form,),
{'my_field': forms.CharField()}
12. class MutlipleFormBase(object):
def __init__(self, data, files):
self.forms = [form_class(data, files,
prefix=prefix) for form_class, prefix in
self.form_classes.iteritems()]
def as_table(self):
return 'n'.join([form.as_table() for
form in self.forms])
def save(self, commit=True):
return tuple([form.save(commit) for
form in self.forms])
def is_valid(self):
return all(form.is_valid() for form
in
13. How it works
•Simple factory function creates a
new class that subclasses
MultipleFormBase, gives it an extra
attribute, a dict of form classes
•MultipleFormBase just emulates
the API of Form and ModelForm,
aggregating the methods as
appropriate
14. class UserForm(forms.ModelForm):
class Meta:
model = User
class ProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
UserProfileForm = multipleform_factory({
'user': UserForm,
'profile': ProfileForm,
}, ['user', 'profile'])
15. The Result
•Now we can use UserProfileForm
the same way we would a regular
form.
•We need to expand
MultipleFormBase to implement
the rest of the Form API, but these
additional methods are trivial.
16. A More Dynamic Form
•Not all users should see the same
form
•Simple example: a user should only
be able to select an option from a
dropdown if it belongs to them
•Tools of the trade: Subclassing
methods
17. class ObjectForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
super(ObjectForm,
self).__init__(*args, **kwargs)
self.fields['related'].queryset =
self.fields['related'].queryset.
filter(owner=user)
class Meta:
model = Object
18. Dead Simple
•Now when we instantiate our form
we just need to prive a user kwarg.
•Form and ModelForm are just
python. We overide methods to add
our own behavior, then call super().
•We can apply this same technique
anywhere in Python, to any method.
19. Building it Dynamically
•What if we don't just want to alter
our form per request, what if we
want to build the whole thing
dynamically?
•Common example: a survey
application where we keep the
survey questions in the database.
•Let's build it.
20. The Pieces
•What do we need to build this
survey application?
•A Model for a Survey and another
for Fields on each Form(we're
keeping it simple).
•A function to take a Survey object
and turn it into a Form subclass.
21. class Survey(models.Model):
name = models.CharField(max_length=100)
FIELD_TYPE_CHOICES = (
(0, 'charfield'),
(1, 'textfield'),
(2, 'boolean'),
)
class Question(models.Model):
survey = models.ForeignKey(Survey,
related_name='questions')
label = models.CharField(max_length=255)
field_type = models.IntegerField(choices=
FIELD_TYPE_CHOICES)
23. What do We Have
•2 very simple models for storing
our surveys and the questions for
these surveys
•a way to create actual forms.Form
classes for a survey
•now we can use these forms in our
views the same as normal
24. What Don't We have
•Handling for more complex field
types(such as choices)
•A way to create Surveys(other than
the admin, which would actually
work really well for this)
•A way to save the results of a
survey
•Consider these an excersise for the
reader
25. Recap
•Use standard Python to extend
what we have
•Don't be afraid to build your own
things on top of what we already
have
•It's just Python