Documents
Resources
Learning Center
Upload
Plans & pricing Sign in
Sign Out

Django Forms - Best Practices_ Tips_ Tricks

VIEWS: 37 PAGES: 18

Great presentations from experts about various web technologies and programming languages.

More Info
									Django Forms
Best Practices, Tips and Tricks




       DjangoCon 2010

         Shawn Rider
        PBS Education
Basic Forms
  from django import forms
  class ContactForm(forms.Form):
     subject = forms.CharField(max_length=100)
     message = forms.CharField()
     sender = forms.EmailField()
     cc_myself = forms.BooleanField(required=False)

    def save(self):
      data = self.cleaned_data
      contact = Contact()
      contact.subject = data.get('subject', '')
      contact.message = data.get('message', '')
      contact.sender = data.get('sender', '')
      contact.cc_myself = data.get('cc_myself', False)
      contact.save()
      return contact
But that's so much to write...
Model Forms

 • Easily create forms with just a few lines of code.

 • Customize and modify fields available for editing on the
   form.

 • Override default methods to support complex business
   logic, etc.

 • Customized Model Forms can easily be used in Django
   Admin.

 • Model Forms can be used with Model Formsets to edit
   multiple forms in a single view.
Model Forms
   from django.forms import ModelForm

   ### ... Form Definition ... ###
   class ArticleForm(ModelForm):
     class Meta:
        model = Article

   ### ... Inside the View ... ###
   article = Article.objects.get(pk=article_id)
   if request.method == 'POST':
       form = ArticleForm(request.POST, instance=article)
       if form.is_valid():
           article = form.save()
           return HttpResponseRedirect(redirect_url)
   else:
       form = ArticleForm(instance=article)
Model Forms
A Model Form with listed editable fields and explicitly defined widgets:

class ArticleForm(ModelForm):
  class Meta:
    model = Article
    fields = ('title', 'content', 'blurb')
    widgets = {
       'content': Textarea(attrs:{'cols':80, 'rows':20})
    }

A Model Form with a custom save method:

class ArticleForm(ModelForm):
  class Meta:
    model = Article
  def save(self, *args, **kwargs):
    data = self.cleaned_data
    ### Insert complex, custom save logic here. ###
    return article
Formsets

• Formsets allow you to produce multiple forms for a page
  (bulk editing).

• Can be customized in many ways.

• Handle basic metadata to keep forms and data properly
  aligned.

• Formsets are used with basic Django forms. Model
  Formsets are used with Django Model Forms.

• Allow for validation of entire set of forms as well as
  individual forms within the formset.
Formsets
   ### View code ###
   def manage_articles(request):
     ArticleFormSet = formset_factory(ArticleForm)
     if request.method == 'POST':
         formset = ArticleFormSet(request.POST, request.FILES)
         if formset.is_valid():
             for form in formset:
                form.save()
             return HttpResponseRedirect(REDIRECT_URL)
     else:
          formset = ArticleFormSet()
     return render_to_response('manage_articles.html', {
          'formset': formset
     })

   <!-- Template Code -->
   <form method="post" action="">
      <table> {{ formset }} </table>
   </form>
Dynamic Forms

A Dynamic Form modulates the fields and/or choices available
in the form

Why would I ever need a Dynamic Form?

• Enforcing restricted permissions for different levels of user

• Providing choices based on user or system data (custom
  contact lists, for example)

• Enforcing other complex business logic
 For some developers,
it seems natural to use
a "code factory" design
   pattern to solve this
         problem.

   Please don't do that.
Override __init__ function
class ContactForm(forms.Form):
  def __init__(self, user, *args, **kwargs):
    super(ContactForm, self).__init__(*args, **kwargs)
    if not user.is_authenticated():
        self.fields['captcha'] = CaptchaField()
  name = forms.CharField(max_length=50)
  email = forms.Emailfield()
  message = forms.CharField(widget=forms.Textarea)
Code Factory
def make_contact_form(user):
  # The basic form
  class _ContactForm(forms.Form):
    name = forms.CharField(max_length=50)
    email = forms.EmailField()
    message = forms.CharField(widget=forms.Textarea)
   if user.is_authenticated():
      return _ContactForm
   class _CaptchaContactForm(_ContactForm):
     captcha = CaptchaField()
   return _CaptchaContactForm

(Taken from James Bennett's http://www.b-list.org/weblog/2008/nov/09/dynamic-forms/)
Dynamic Forms

class MemberSearchForm(forms.Form):
   def __init__(self, data=None, account=None, *args, **kwargs):
     self.account = account
     super(MemberSearchForm, self).__init__(data, *args, **kwargs)

  terms = forms.CharField(required=False)
  role = forms.ChoiceField(choices=SEARCH_ROLE_CHOICES, required=False)
  status = forms.ChoiceField(choices=SEARCH_STATUS_CHOICES, required=False)
Tip:
Always Redirect After Modifying Data


    article = Article.objects.get(pk=article_id)
    if request.method == 'POST':
        form = ArticleForm(request.POST, instance=article)
        if form.is_valid():
            article = form.save()
            return HttpResponseRedirect(redirect_url)
    else:
        form = ArticleForm(instance=article)
The Worst Example in the Django docs?

def contact(request):
  if request.method == 'POST': # If the form has been submitted...
    form = ContactForm(request.POST) # A form bound to the POST data
    if form.is_valid(): # All validation rules pass
      subject = form.cleaned_data['subject']
      message = form.cleaned_data['message']
      sender = form.cleaned_data['sender']
      cc_myself = form.cleaned_data['cc_myself']
      recipients = ['info@example.com']
      if cc_myself:
         recipients.append(sender)
         from django.core.mail import send_mail
         send_mail(subject, message, sender, recipients)
      return HttpResponseRedirect('/thanks/') # Redirect after POST
  else:
      form = ContactForm() # An unbound form
  return render_to_response('contact.html', { 'form': form, })
Tip:
Use Forms to Isolate Logic
class MemberSearchForm(forms.Form):
   def __init__(self, data=None, account=None, *args, **kwargs):
     self.account = account
     super(MemberSearchForm, self).__init__(data, *args, **kwargs)

  terms = forms.CharField(required=False)
  role = forms.ChoiceField(
            choices=SEARCH_ROLE_CHOICES, required=False)
  status = forms.ChoiceField(
            choices=SEARCH_STATUS_CHOICES, required=False)

  def search(self):
    data = self.cleaned_data
    ### Complex Search Logic Here ###
    return results
Tip:
Use django-uni-form
Tip:
Roll Your Own Fields and Form Classes

• Isolate logic at any cost

• Sometimes objects or features in your project warrant a
  completely custom Form Field

• Custom Form Classes can perform custom templatized
  output, eliminating repetitive HTML in your templates

Sub Tip:

• Extend existing classes to save yourself headaches
Thanks and Further Reading

James Bennett's B-List:
  http://www.b-list.org/weblog/2008/nov/09/dynamic-forms/

Danny Greenfield’s Django-Uniform:
  http://github.com/pydanny/django-uni-form/

								
To top