Upload
shawn-rider
View
35.133
Download
3
Embed Size (px)
DESCRIPTION
This is a presentation for novice Django developers that reviews the basics of working with Django's Forms module.
Citation preview
Django FormsDjango FormsBest Practices, Tips and TricksBest Practices, Tips and Tricks
DjangoCon 2010
Shawn RiderPBS 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...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, For some developers, it seems natural to use it seems natural to use a "code factory" design a "code factory" design
pattern to solve this pattern to solve this problem.problem.
Please don't do that.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 = ['[email protected]'] 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/