Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Forms, Getting Your Money's Worth

These are the slides from my talk on advanced forms topics given at EuroDjangocon 2009.

Related Books

Free with a 30 day trial from Scribd

See all

Related Audiobooks

Free with a 30 day trial from Scribd

See all
  • Be the first to comment

Forms, Getting Your Money's Worth

  1. 1. Forms, Getting Your Money's Worth Alex Gaynor
  2. 2. What Are Forms For? •They are a way to get data from the user
  3. 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. 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. 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. 6. Problems to Solve •Multiple forms of different types •Altering a form's options dynamically •Building entire forms dynamically
  7. 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. 8. 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
  9. 9. 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})
  10. 10. 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()}
  11. 11. 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
  12. 12. 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
  13. 13. 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'])
  14. 14. 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.
  15. 15. 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
  16. 16. 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
  17. 17. 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.
  18. 18. 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.
  19. 19. 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.
  20. 20. 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)
  21. 21. FIELD_TYPES = { 0: forms.CharField, 1: curry(forms.CharField, widget=forms.Textarea) 2: forms.BooleanField } def form_form_survey(survey): fields = {} for question in survey.questions.all(): fields[question.label] = FIELD_TYPES[question.field_type]( label = question.label ) return type('%sForm' % survey.name, (forms.Form,), fields)
  22. 22. 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
  23. 23. 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
  24. 24. 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
  25. 25. Questions? P.S.: If you're someone who needs multiple datbase support in Django come talk to me

    Be the first to comment

    Login to see the comments

  • nperriault

    May. 15, 2010
  • Pharar

    Nov. 29, 2010
  • davidpaccoud

    Feb. 23, 2011
  • zygm0nt

    Mar. 10, 2011
  • czhang

    May. 26, 2011
  • hhsihsa

    Oct. 5, 2011
  • risent

    Oct. 26, 2011
  • golodhrim

    Nov. 8, 2011
  • botulotoxin

    Nov. 23, 2011
  • flubbers

    May. 10, 2012
  • virhilo

    May. 17, 2012
  • scwoodal

    Jul. 31, 2012
  • aysekuck

    Sep. 11, 2012
  • geobourazanas

    Nov. 6, 2012
  • BalamuruganRamar

    Mar. 17, 2013
  • thomaspreuss353

    May. 24, 2013
  • n9986

    Jun. 4, 2013
  • adeelnafis

    Dec. 1, 2013
  • VuToai

    Dec. 23, 2013
  • barseghyanartur

    Jul. 3, 2017

These are the slides from my talk on advanced forms topics given at EuroDjangocon 2009.

Views

Total views

18,095

On Slideshare

0

From embeds

0

Number of embeds

3,533

Actions

Downloads

300

Shares

0

Comments

0

Likes

35

×