Advanced Django  Form Usageby Daniel Greenfeld and Miguel Araujo
Daniel Greenfeld                                                  •   pydanny                                             ...
Miguel Araujo    •   maraujop    •   Freelance Python developer    •   Co-lead of django-uni-form    •   Does Muay Thai   ...
Advanced Django Form Usage                                 @pydanny / @maraujopTons of technical content                  ...
Tons of technical content•   We probably won’t have time for questions                                                Adva...
Tons of technical content•   We probably won’t have time for questions•   Slides will be posted right after the talk      ...
Tons of technical content•   We probably won’t have time for questions•   Slides will be posted right after the talk•   To...
Tons of technical content•   We probably won’t have time for questions•   Slides will be posted right after the talk•   To...
Tons of technical content•   We probably won’t have time for questions•   Slides will be posted right after the talk•   To...
Tons of technical content•   We probably won’t have time for questions•   Slides will be posted right after the talk•   To...
Tons of technical content•   We probably won’t have time for questions•   Slides will be posted right after the talk•   To...
Advanced Django Form Usage                          @pydanny / @maraujopGood Form Patterns Fundamentals of                ...
Fundamentals of   Good Form Patterns• Zen of Python still applies                                Advanced Django Form Usag...
Fundamentals of   Good Form Patterns• Zen of Python still applies   • import this                                Advanced ...
Fundamentals of   Good Form Patterns• Zen of Python still applies   • import this• Spartan programming als0 is important  ...
Spartan Programming•   Horizontal complexity•   Vertical complexity (line count)•   Token count•   Character count•   Vari...
Spartan Programming•   Horizontal complexity•   Vertical complexity (line count)•   Token count•   Character count•   Vari...
Spartan Programming•   Horizontal complexity•   Vertical complexity (line count)•   Token count•   Character count•   Vari...
Advanced Django Form Usage                                  @pydanny / @maraujopCalling forms the easy way                ...
Calling forms the easy way• Smaller, cleaner code makes our lives better                                                 A...
Calling forms the easy way• Smaller, cleaner code makes our lives better• Line 5 of the Zen of Python                     ...
Calling forms the easy way• Smaller, cleaner code makes our lives better• Line 5 of the Zen of Python   • Flat is better t...
Calling forms the easy way• Smaller, cleaner code makes our lives better• Line 5 of the Zen of Python   • Flat is better t...
Calling forms the easy way• Smaller, cleaner code makes our lives better• Line 5 of the Zen of Python   • Flat is better t...
Enough theory
A Basic Django Formclass MyForm(forms.Form):   name = forms.CharField(_(Name), required=True)                             ...
Standard views.pydef my_view(request, template_name=myapp/my_form.html):    if request.method == POST:        form = MyFor...
Standard views.pydef my_view(request, template_name=myapp/my_form.html):    if request.method == POST:                   F...
Standard views.pydef my_view(request, template_name=myapp/my_form.html):    if request.method == POST:                   F...
Standard views.pydef my_view(request, template_name=myapp/my_form.html):    if request.method == POST:                   F...
Standard views.pydef my_view(request, template_name=myapp/my_form.html):    if request.method == POST:                   F...
Standard views.pydef my_view(request, template_name=myapp/my_form.html):    if request.method == POST:                   F...
Easy views.pydef my_view(request, template_name=myapp/my_form.html):   # sticks in a POST or renders empty form   form = M...
Single form              Easy views.pydef my_view(request, template_name=myapp/my_form.html):    # sticks in a POST or ren...
Single form              Easy views.pydef my_view(request, template_name=myapp/my_form.html):    # sticks in a POST or ren...
Easy views.pydef my_view(request, template_name=myapp/my_form.html):   # sticks in a POST or renders empty form   form = M...
Easy views.pydef my_view(request, template_name=myapp/my_form.html):   # sticks in a POST or renders empty form   form = M...
Easy views.pydef my_view(request, template_name=myapp/my_form.html):   # sticks in a POST or renders empty form   form = M...
Easy views.pydef my_view(request, template_name=myapp/my_form.html):   # sticks in a POST or renders empty form   form = M...
Easy views.pydef my_view(request, template_name=myapp/my_form.html):   # sticks in a POST or renders empty form   form = M...
What aboutfile uploads?
Easy views.py + filesdef my_view(request, template_name=myapp/my_form.html):   form = MyForm(request.POST or None, request....
Easy views.py + filesdef my_view(request, template_name=myapp/my_form.html):   form = MyForm(request.POST or None, request....
Easy views.py + filesdef my_view(request, template_name=myapp/my_form.html):   form = MyForm(request.POST or None, request....
Easy views.py + filesdef my_view(request, template_name=myapp/my_form.html):   form = MyForm(request.POST or None, request....
Easy views.py + filesdef my_view(request, template_name=myapp/my_form.html):   form = MyForm(request.POST or None, request....
What aboutModelForms?
Advanced Django Form Usage                      @pydanny / @maraujoppydanny made up statistics                            ...
pydanny made          up statistics• 91% of all Django projects use ModelForms                                            ...
pydanny made          up statistics• 91% of all Django projects use ModelForms• 80% ModelForms require trivial logic      ...
pydanny made          up statistics• 91% of all Django projects use ModelForms• 80% ModelForms require trivial logic• 20% ...
pydanny made          up statistics• 91% of all Django projects use ModelForms• 80% ModelForms require trivial logic• 20% ...
A Basic ModelFormclass MyModelForm(forms.Form):    class Meta:        model = MyModel        fields = [name]              ...
Classic views.py for ModelFormdef my_model_edit(request, slug=slug, template_name=myapp/my_model_form.html):   # I wouldnt...
Classic views.py for ModelFormdef my_model_edit(request, slug=slug, template_name=myapp/my_model_form.html):   # I wouldnt...
Classic views.py for ModelFormdef my_model_edit(request, slug=slug, template_name=myapp/my_model_form.html):   # I wouldnt...
Classic views.py for ModelForm def my_model_edit(request, slug=slug, template_name=myapp/my_model_form.html):    # I would...
Classic views.py for ModelForm def my_model_edit(request, slug=slug, template_name=myapp/my_model_form.html):    # I would...
Classic views.py for ModelForm def my_model_edit(request, slug=slug, template_name=myapp/my_model_form.html):    # I would...
Classic views.py for ModelFormdef my_model_edit(request, slug=slug, template_name=myapp/my_model_form.html):   # I wouldnt...
Classic views.py for ModelFormdef my_model_edit(request, slug=slug, template_name=myapp/my_model_form.html):   # I wouldnt...
Classic views.py for ModelFormdef my_model_edit(request, slug=slug, template_name=myapp/my_model_form.html):   # I wouldnt...
Classic views.py for ModelFormdef my_model_edit(request, slug=slug, template_name=myapp/my_model_form.html):   # I wouldnt...
Classic views.py for ModelFormdef my_model_edit(request, slug=slug, template_name=myapp/my_model_form.html):   # I wouldnt...
easy views.py + ModelFormdef my_model_edit(request, slug=slug, template_name=myapp/my_model_form.html):   mymodel = get_ob...
easy views.py + ModelFormdef my_model_edit(request, slug=slug, template_name=myapp/my_model_form.html):   mymodel = get_ob...
easy views.py + ModelFormdef my_model_edit(request, slug=slug, template_name=myapp/my_model_form.html):   mymodel = get_ob...
easy views.py + ModelFormdef my_model_edit(request, slug=slug, template_name=myapp/my_model_form.html):   mymodel = get_ob...
easy views.py + ModelFormdef my_model_edit(request, slug=slug, template_name=myapp/my_model_form.html):   mymodel = get_ob...
easy views.py + ModelFormdef my_model_edit(request, slug=slug, template_name=myapp/my_model_form.html):   mymodel = get_ob...
easy views.py + ModelFormdef my_model_edit(request, slug=slug, template_name=myapp/my_model_form.html):   mymodel = get_ob...
easy views.py + ModelFormdef my_model_edit(request, slug=slug, template_name=myapp/my_model_form.html):   mymodel = get_ob...
What about newmodel instances?
add views.py + ModelFormdef my_model_add(request, template_name=myapp/my_model_form.html):   form = MyModelForm(request.PO...
add views.py + ModelFormdef my_model_add(request, template_name=myapp/my_model_form.html):   form = MyModelForm(request.PO...
I can make it smaller!def my_model_tiny_add(request,template_name=myapp/my_model_form.html):   form = MyModelForm(request....
How do you test  this stuff?
Advanced Django Form Usage                             @pydanny / @maraujopPlease don’t manually   test your forms        ...
Please don’t manually      test your forms• This isn’t a testing talk but...                                     Advanced ...
Please don’t manually      test your forms• This isn’t a testing talk but...• Forms are the number one thing to test      ...
Please don’t manually      test your forms• This isn’t a testing talk but...• Forms are the number one thing to test• Don’...
Please don’t manually      test your forms• This isn’t a testing talk but...• Forms are the number one thing to test• Don’...
Django unit tests!def test_add_package_view(self):    url = reverse(my-url)    response = self.client.get(url)   self.asse...
Django unit tests!def test_add_package_view(self):    url = reverse(my-url)    response = self.client.get(url)      POSTin...
Django unit tests!def test_add_package_view(self):    url = reverse(my-url)    response = self.client.get(url)      POSTin...
Django unit tests!def test_add_package_view(self):    url = reverse(my-url)    response = self.client.get(url)      POSTin...
Can wechange non-required    to required?
non-required to required• Your model fields are non-required• but you want the form fields to be required                   ...
A basic Django models.pyclass MyModel(models.Model):    name = models.CharField(_(Name), max_length=50, blank=True, null=T...
Classic forms.py overloadclass MyModelTooMuchTypingForm(forms.ModelForm):    """ Ive done this and it sucks    hard to deb...
Classic forms.py overloadclass MyModelTooMuchTypingForm(forms.ModelForm):    """ Ive done this and it sucks    hard to deb...
Classic forms.py overloadclass MyModelTooMuchTypingForm(forms.ModelForm):    """ Ive done this and it sucks    hard to deb...
Classic forms.py overloadclass MyModelTooMuchTypingForm(forms.ModelForm):    """ Ive done this and it sucks    hard to deb...
Better forms.py overloadclass MyModelForm(forms.ModelForm):    """ Much better and you are extending, not copy/pasting """...
Try it with inheritance!class BaseEmailForm(forms.Form):   email = forms.EmailField(_(Email))   confirm_email = forms.Emai...
Add dynamic fields    to a form
Dynamically adding fields                to a formdef my_view(request, template_name=myapp/my_model_form.html):   form = My...
Dynamically adding fields                to a formdef my_view(request, template_name=myapp/my_model_form.html):     Form di...
Dynamically adding fields                to a formdef my_view(request, template_name=myapp/my_model_form.html):     Form di...
Can’t we just pass in alist of fields into a form       from a view?
Constructor overrides forms.pyclass MyModelForm(forms.ModelForm):    def __init__(self, *args, **kwargs):        extra = k...
Constructor overrides views.py def my_view(request, template_name=myapp/my_model_form.html):    form = MyModelForm(request...
I need a formset
formsets.pyclass ItemFormSet(BaseFormSet):    def __init__(self, numberItems, *args, **kwargs):        super(ItemFormSet, ...
formsets.pyclass ItemFormSet(BaseFormSet):    def __init__(self, numberItems, *args, **kwargs):        super(ItemFormSet, ...
views.pyfrom django.forms.models import formset_factorydef wsgi_form_view(request):   WsgiFormset = formset_factory(Exampl...
Lets doprogrammatic layout     of forms
Things I want python   to describe in forms• Different fieldsets within the same form• Various buttons  • submit  • reset   ...
Advanced Django Form Usage                       @pydanny / @maraujopdjango-uni-form                                      ...
Programmatic layoutsclass ExampleForm(forms.Form):    def __init__(self, *args, **kwargs):        self.helper = FormHelper...
Programmatic layouts  from uni_form.helpers import FormHelper, Submit, Reset  from uni_form.helpers import Fieldset, Butto...
Programmatic layouts  from uni_form.helpers import FormHelper, Submit, Reset  from uni_form.helpers import Fieldset, Butto...
Programmatic layouts  from uni_form.helpers import FormHelper, Submit, Reset  from uni_form.helpers import Fieldset, Butto...
Programmatic layouts  from uni_form.helpers import FormHelper, Submit, Reset  from uni_form.helpers import Fieldset, Butto...
Programmatic layouts  from uni_form.helpers import FormHelper, Submit, Reset  from uni_form.helpers import Fieldset, Butto...
Programmatic layouts  from uni_form.helpers import FormHelper, Submit, Reset  from uni_form.helpers import Fieldset, Butto...
renders as divs{% load uni_form_tags %}{% uni_form my_form my_form.helper %}      Renders the HTML form     with buttons a...
Sample output<form action="#" class="uniForm">  <fieldset class="inlineLabels">    <div class="ctrlHolder">      <label fo...
django-uni-form• Programmatic layout• Div based forms• Section 508 compliant• Fully customizable templates                ...
What aboutHTML5 Fields?
django-floppyforms• by Bruno Renie @brutasse• HTML5 Widgets• Fully customizable templates• Plays nice with django-uni-form ...
Easy to use!       import floppyforms as forms       class ExampleForm(forms.Form):           username = forms.CharField(  ...
Customizable widgetsimport floppyforms as formsclass OtherEmailInput(forms.EmailInput):    template_name = path/to/other_e...
Can you combinedjango-uni-form anddjango-floppyforms?
django-uni-form +django-floppyforms       =AWESOME POWER
They get along wellclass ListFriendsForm(forms.Form):    username = forms.CharField(        widget = forms.TextInput(     ...
Forms refactor  Django 1.4
Advanced Django Form Usage                                  @pydanny / @maraujopWhat we are getting in 1.4                ...
What we are getting in 1.4• Form rendering will use templates instead  of HTML in Python                                  ...
What we are getting in 1.4• Form rendering will use templates instead  of HTML in Python   • Template based widgets       ...
What we are getting in 1.4• Form rendering will use templates instead  of HTML in Python   • Template based widgets   • Te...
Advanced Django Form Usage                          @pydanny / @maraujopforms refactor and django-uni-form                ...
forms refactor and    django-uni-form• forms refactor layout “lives” in templates                                         ...
forms refactor and    django-uni-form• forms refactor layout “lives” in templates• django-uni-form layout “lives” in pytho...
forms refactor and    django-uni-form• forms refactor layout “lives” in templates• django-uni-form layout “lives” in pytho...
Give me a way to create custom   form fields
The docs on custom fields“Its only requirements are that it implementa clean() method and that its __init__()method accept ...
The docs on custom fields“Its only requirements are that it implementa clean() method and that its __init__()method accept ...
How it really worksYou don’t need to implement “clean”       You do need to implement:•   required•   widget              ...
How the heck then do I actually add a custom     form field?!?
Advanced Django Form Usage                        @pydanny / @maraujopvalidation work?How does form                       ...
fields.pyclass AMarkField(forms.Field):    widget = TextInput    default_error_messages = {        not_an_a: _(uyou can onl...
Not DRYclass MyModelForm(forms.ModelForm):    mark = CharField()    def clean_mark(self):        mark_value = self.cleaned...
I want a custom field that validates JSON
The JSON fieldclass JSONField(forms.Field):    default_error_messages = {        invalid: This is not valid JSON string    ...
I want a customwidget that handles multiple widgets
widgets.pyfrom django.forms.extras.widgets import MultiWidgetclass AddressWidget(MultiWidget):    def __init__(self, attrs...
widgets.pyfrom django.forms.extras.widgets import MultiWidgetclass AddressWidget(MultiWidget):            Two TextInput   ...
widgets.pyfrom django.forms.extras.widgets import MultiWidgetclass AddressWidget(MultiWidget):             Two TextInput  ...
fields.py         class AddressField(forms.Field):             self.widget = AddressWidget             def to_python(self, ...
fields.py           class AddressField(forms.Field):               self.widget = AddressWidget               def to_python(...
Give me a MultiValue, MultiWidget field
MultiValue, MultiWidget Field class AlternativeAddressField(forms.MultiValueField):     widget = AddressWidget     def __i...
MultiValue, MultiWidget Field class AlternativeAddressField(forms.MultiValueField):     widget = AddressWidget     def __i...
MultiValue, MultiWidget Field class AlternativeAddressField(forms.MultiValueField):     widget = AddressWidget     def __i...
MultiValue, MultiWidget Field class AlternativeAddressField(forms.MultiValueField):     widget = AddressWidget     def __i...
MultiValue, MultiWidget Field class AlternativeAddressField(forms.MultiValueField):     widget = AddressWidget     def __i...
Validators with Multi-Fieldclass AlternativeAddressField(forms.MultiValueField):    widget = AddressWidget    def __init__...
How about custom widget output?
Advanced Django Form Usage                                       @pydanny / @maraujopCustom Widget Output                 ...
def render(self, name, value, attrs=None): Custom Widget Output    # HTML to be added to the output    widget_labels = [  ...
def render(self, name, value, attrs=None): Custom Widget Output    # HTML to be added to the output    widget_labels = [  ...
Can’t you make iteasier to understand?
Advanced Django Forms Usage
Advanced Django Forms Usage
Advanced Django Forms Usage
Advanced Django Forms Usage
Advanced Django Forms Usage
Advanced Django Forms Usage
Advanced Django Forms Usage
Advanced Django Forms Usage
Advanced Django Forms Usage
Advanced Django Forms Usage
Advanced Django Forms Usage
Advanced Django Forms Usage
Advanced Django Forms Usage
Advanced Django Forms Usage
Advanced Django Forms Usage
Advanced Django Forms Usage
Advanced Django Forms Usage
Advanced Django Forms Usage
Advanced Django Forms Usage
Upcoming SlideShare
Loading in...5
×

Advanced Django Forms Usage

65,525

Published on

An advanced forms presentation given with Miguel Araujo (marajop) at DjangoCon 2011. The transcript and slides is aimed at getting into Django Core, and Jacob Kaplan-Moss has stated this is his plan.

Published in: Business, Technology
5 Comments
120 Likes
Statistics
Notes
No Downloads
Views
Total Views
65,525
On Slideshare
0
From Embeds
0
Number of Embeds
25
Actions
Shares
0
Downloads
1,307
Comments
5
Likes
120
Embeds 0
No embeds

No notes for slide
  • D\n
  • M\n
  • D\n
  • D\n
  • D\n
  • D\n
  • D\n
  • D\n
  • D\n
  • D\n
  • D\n
  • D\n
  • D\n
  • D\n
  • D\nIf you can reduce your line count from 10 to 2 and make it more obvious, that is good\nThis is why so many people prefer things like Python over Java\n
  • D\nYou want to have shallow code with as little if statements as possible\nOtherwise you create lots of hard to test edge cases\n
  • D\n
  • D\n
  • D\n
  • D\n
  • D\n
  • M\nShow me the code!\n
  • D\n
  • D\nassuming we have from forms import MyForm\nIn real life this can get big, complex, and nasty\n
  • D\nassuming we have from forms import MyForm\nIn real life this can get big, complex, and nasty\n
  • D\nassuming we have from forms import MyForm\nIn real life this can get big, complex, and nasty\n
  • D\nassuming we have from forms import MyForm\nIn real life this can get big, complex, and nasty\n
  • D\nassuming we have from forms import MyForm\nIn real life this can get big, complex, and nasty\n
  • D\n
  • D\n
  • D\n
  • D\n
  • D\n
  • D\n
  • D \nMy fiancee, Audrey Roy, said my example needs to handle file uploads. How do we do that?\n
  • M\n
  • M\n
  • M\n
  • M\n
  • M\nHey Danny, don&amp;#x2019;t 91% of real Django projects use ModelForms?\n
  • D\n
  • D\n
  • D\n
  • D\n
  • D\n
  • D\n
  • D\n
  • D\n
  • D\n
  • D\n
  • D\n
  • D\n
  • D\n
  • D\n
  • D\n
  • D\n
  • D\n
  • D\n
  • D\n
  • D\n
  • D\nHey Miguel, how do I make this work when adding data?\n
  • M\n
  • M\n
  • M\n
  • D\n
  • D\n
  • D\n
  • D\n
  • D\n
  • D\n
  • D\n
  • M\nAny other tricks?\n
  • D\n
  • D\n
  • D\n
  • D\n
  • D\n
  • D Fields are in a dictionary! We should remember this fact!\n
  • D\n
  • D\n\n
  • M\nDidn&amp;#x2019;t you just show that fields are stored in a dict in the form? Why not just add another field that way?\n
  • M\nDidn&amp;#x2019;t you just show that fields are stored in a dict in the form? Why not just add another field that way?\n
  • D\n\n
  • M - Let&apos;s override form constructor to wrap the logic of adding fields on the go\n\n
  • M - Let&apos;s override form constructor to wrap the logic of adding fields on the go\n\n
  • D\n\n
  • M\n
  • M\n
  • M\n\n
  • D\n
  • D\n
  • D\n
  • D\n
  • D\n
  • D\n
  • D\n
  • D\n
  • D\n
  • D\n
  • D\n
  • D\n\n
  • M\n
  • M\n
  • M\n
  • D\n\n
  • M\n\n
  • M\nBecause both DUF and DFF do things in a clean, non-magical way - they just work together. \nNamespaces and clean code beat smart hackery any day.\n
  • M\nTell me what is coming down the road\n
  • D\n
  • D\n
  • D\n
  • D\nIt&amp;#x2019;s okay that there are different objects\n
  • D\nIt&amp;#x2019;s okay that there are different objects\n
  • D\nIt&amp;#x2019;s okay that there are different objects\n
  • M\n\n
  • D\n\n
  • D\n
  • D\n\n
  • M\n
  • M Of course you could do the same with a regular CharField using form validationclean_&lt;fieldname&gt; method, but...\n
  • M - Duplicated code. So do a custom form field instead of duplicating code everywhere\nYes, you could solve this with form inheritance, but what if the inheritance chain gets too long?\n
  • M\n\n
  • D This field returns an already JSON validated Python list when accessing cleaned_data\n
  • D\n\n
  • M We are using MultiWidget.\n
  • M We are using MultiWidget.\n
  • M The difference is that you can get fancier with validation on the list than the string\n
  • D\n\n
  • M You need to implement compress\nIt would be nice that fields were also an attribute in the parent class.\n
  • M You need to implement compress\nIt would be nice that fields were also an attribute in the parent class.\n
  • M You need to implement compress\nIt would be nice that fields were also an attribute in the parent class.\n
  • M You need to implement compress\nIt would be nice that fields were also an attribute in the parent class.\n
  • M\n
  • M\n\n
  • D - You are going to love how easy this is! Ready?\n
  • D - You are going to love how easy this is! Ready?\n
  • D\n\n
  • M\n
  • M\n\n
  • M\n
  • M\n
  • M\n
  • M * You can raise a ValidationError with a code. * You can raise a ValidationError with a list of messages. * You could raise a ValidationError on the first email that fails, but error will be less helpful. * Hard to customize, no error_messages, Do this in an upper layer, rewrite run_validators\n
  • M\n
  • D\n
  • D\n
  • D\n
  • D\n
  • M\n
  • M\n
  • M\n
  • M\n
  • D\n
  • \n
  • Advanced Django Forms Usage

    1. 1. Advanced Django Form Usageby Daniel Greenfeld and Miguel Araujo
    2. 2. Daniel Greenfeld • pydanny • Python & Django developer for Cartwheel Web / RevSys • Founded django-uni-form • Does Capoeira • Lives in Los Angeles with his Advanced Django Form Usagehttp://www.flickr.com/photos/pydanny/4442245488/ Fiancé, Audrey Roy (audreyr) @pydanny / @maraujop 2
    3. 3. Miguel Araujo • maraujop • Freelance Python developer • Co-lead of django-uni-form • Does Muay Thai • Lives in Madrid with his amazing girlfiend who does Advanced Django Form Usage aerospace for a living @pydanny / @maraujop • http://maraujop.github.com/ 3
    4. 4. Advanced Django Form Usage @pydanny / @maraujopTons of technical content 4
    5. 5. Tons of technical content• We probably won’t have time for questions Advanced Django Form Usage @pydanny / @maraujop 4
    6. 6. Tons of technical content• We probably won’t have time for questions• Slides will be posted right after the talk Advanced Django Form Usage @pydanny / @maraujop 4
    7. 7. Tons of technical content• We probably won’t have time for questions• Slides will be posted right after the talk• Too much content in the abstract Advanced Django Form Usage @pydanny / @maraujop 4
    8. 8. Tons of technical content• We probably won’t have time for questions• Slides will be posted right after the talk• Too much content in the abstract• Special thanks to: Advanced Django Form Usage @pydanny / @maraujop 4
    9. 9. Tons of technical content• We probably won’t have time for questions• Slides will be posted right after the talk• Too much content in the abstract• Special thanks to: • Brian Rosner Advanced Django Form Usage @pydanny / @maraujop 4
    10. 10. Tons of technical content• We probably won’t have time for questions• Slides will be posted right after the talk• Too much content in the abstract• Special thanks to: • Brian Rosner Advanced Django Form Usage • James Tauber @pydanny / @maraujop 4
    11. 11. Tons of technical content• We probably won’t have time for questions• Slides will be posted right after the talk• Too much content in the abstract• Special thanks to: • Brian Rosner Advanced Django Form Usage • James Tauber @pydanny / @maraujop • Frank Wiles 4
    12. 12. Advanced Django Form Usage @pydanny / @maraujopGood Form Patterns Fundamentals of 5
    13. 13. Fundamentals of Good Form Patterns• Zen of Python still applies Advanced Django Form Usage @pydanny / @maraujop 5
    14. 14. Fundamentals of Good Form Patterns• Zen of Python still applies • import this Advanced Django Form Usage @pydanny / @maraujop 5
    15. 15. Fundamentals of Good Form Patterns• Zen of Python still applies • import this• Spartan programming als0 is important Advanced Django Form Usage @pydanny / @maraujop 5
    16. 16. Spartan Programming• Horizontal complexity• Vertical complexity (line count)• Token count• Character count• Variables• Advanced Django Form Usage Loops @pydanny / @maraujop• Conditionals http://www.codinghorror.com/blog/2008/07/spartan-programming.html 6
    17. 17. Spartan Programming• Horizontal complexity• Vertical complexity (line count)• Token count• Character count• Variables• Advanced Django Form Usage Loops @pydanny / @maraujop• Conditionals http://www.codinghorror.com/blog/2008/07/spartan-programming.html 7
    18. 18. Spartan Programming• Horizontal complexity• Vertical complexity (line count)• Token count• Character count• Variables• Advanced Django Form Usage Loops @pydanny / @maraujop• Conditionals http://www.codinghorror.com/blog/2008/07/spartan-programming.html 8
    19. 19. Advanced Django Form Usage @pydanny / @maraujopCalling forms the easy way 9
    20. 20. Calling forms the easy way• Smaller, cleaner code makes our lives better Advanced Django Form Usage @pydanny / @maraujop 9
    21. 21. Calling forms the easy way• Smaller, cleaner code makes our lives better• Line 5 of the Zen of Python Advanced Django Form Usage @pydanny / @maraujop 9
    22. 22. Calling forms the easy way• Smaller, cleaner code makes our lives better• Line 5 of the Zen of Python • Flat is better than nested. Advanced Django Form Usage @pydanny / @maraujop 9
    23. 23. Calling forms the easy way• Smaller, cleaner code makes our lives better• Line 5 of the Zen of Python • Flat is better than nested.• Aim for minimal boilerplate Advanced Django Form Usage @pydanny / @maraujop 9
    24. 24. Calling forms the easy way• Smaller, cleaner code makes our lives better• Line 5 of the Zen of Python • Flat is better than nested.• Aim for minimal boilerplate Advanced Django Form Usage• So you can focus on your business logic @pydanny / @maraujop 9
    25. 25. Enough theory
    26. 26. A Basic Django Formclass MyForm(forms.Form): name = forms.CharField(_(Name), required=True) Advanced Django Form Usage @pydanny / @maraujop 11
    27. 27. Standard views.pydef my_view(request, template_name=myapp/my_form.html): if request.method == POST: form = MyForm(request.POST) # Form #1! if form.is_valid(): # nested if! do_x() return redirect(/) else: form = MyForm() # Form #2! return render(request, template_name, {form: form}) Advanced Django Form Usage @pydanny / @maraujop 12
    28. 28. Standard views.pydef my_view(request, template_name=myapp/my_form.html): if request.method == POST: Form #1 form = MyForm(request.POST) # Form #1! if form.is_valid(): # nested if! do_x() return redirect(/) else: form = MyForm() # Form #2! return render(request, template_name, {form: form}) Advanced Django Form Usage @pydanny / @maraujop 12
    29. 29. Standard views.pydef my_view(request, template_name=myapp/my_form.html): if request.method == POST: Form #1 form = MyForm(request.POST) # Form #1! if form.is_valid(): # nested if! do_x() return redirect(/) Form #2 else: form = MyForm() # Form #2! return render(request, template_name, {form: form}) Advanced Django Form Usage @pydanny / @maraujop 12
    30. 30. Standard views.pydef my_view(request, template_name=myapp/my_form.html): if request.method == POST: Form #1 form = MyForm(request.POST) # Form #1! if form.is_valid(): # nested if! do_x() return redirect(/) Form #2 else: form = MyForm() # Form #2! return render(request, template_name, {form: form}) Advanced Django Form Usage Only 1 nested if, but real code @pydanny / @maraujop gets much more complex 12
    31. 31. Standard views.pydef my_view(request, template_name=myapp/my_form.html): if request.method == POST: Form #1 form = MyForm(request.POST) # Form #1! if form.is_valid(): # nested if! do_x() return redirect(/) Form #2 else: form = MyForm() # Form #2! return render(request, template_name, {form: form}) Advanced Django Form Usage Only 1 nested if, but real code @pydanny / @maraujop gets much more complex Custom business goes here 12
    32. 32. Standard views.pydef my_view(request, template_name=myapp/my_form.html): if request.method == POST: Form #1 form = MyForm(request.POST) # Form #1! if form.is_valid(): # nested if! do_x() return redirect(/) Form #2 else: form = MyForm() # Form #2! return render(request, template_name, {form: form}) Advanced Django Form Usage Only 1 nested if, but real code @pydanny / @maraujop gets much more complex Custom business goes here 12
    33. 33. Easy views.pydef my_view(request, template_name=myapp/my_form.html): # sticks in a POST or renders empty form form = MyForm(request.POST or None) if form.is_valid(): do_x() return redirect(home) return render(request, template_name, {form: form}) Advanced Django Form Usage @pydanny / @maraujopCustom business goes here 13
    34. 34. Single form Easy views.pydef my_view(request, template_name=myapp/my_form.html): # sticks in a POST or renders empty form form = MyForm(request.POST or None) if form.is_valid(): do_x() return redirect(home) return render(request, template_name, {form: form}) Advanced Django Form Usage @pydanny / @maraujopCustom business goes here 13
    35. 35. Single form Easy views.pydef my_view(request, template_name=myapp/my_form.html): # sticks in a POST or renders empty form form = MyForm(request.POST or None) if form.is_valid(): do_x() return redirect(home) return render(request, template_name, {form: form}) Advanced Django Form Usage If no request.POST, @pydanny / @maraujopCustom business then instantiated with None goes here 13
    36. 36. Easy views.pydef my_view(request, template_name=myapp/my_form.html): # sticks in a POST or renders empty form form = MyForm(request.POST or None) if form.is_valid(): do_x() return redirect(home) return render(request, template_name, {form: form}) Advanced Django Form Usage @pydanny / @maraujop 14
    37. 37. Easy views.pydef my_view(request, template_name=myapp/my_form.html): # sticks in a POST or renders empty form form = MyForm(request.POST or None) if form.is_valid(): do_x() return redirect(home) return render(request, template_name, {form: form}) • 6 lines of code instead of 9 Advanced Django Form Usage @pydanny / @maraujop 14
    38. 38. Easy views.pydef my_view(request, template_name=myapp/my_form.html): # sticks in a POST or renders empty form form = MyForm(request.POST or None) if form.is_valid(): do_x() return redirect(home) return render(request, template_name, {form: form}) • 6 lines of code instead of 9 Advanced Django Form Usage • 33.3333333333% less code to debug @pydanny / @maraujop 14
    39. 39. Easy views.pydef my_view(request, template_name=myapp/my_form.html): # sticks in a POST or renders empty form form = MyForm(request.POST or None) if form.is_valid(): do_x() return redirect(home) return render(request, template_name, {form: form}) • 6 lines of code instead of 9 Advanced Django Form Usage • 33.3333333333% less code to debug @pydanny / @maraujop • One form instantiation 14
    40. 40. Easy views.pydef my_view(request, template_name=myapp/my_form.html): # sticks in a POST or renders empty form form = MyForm(request.POST or None) if form.is_valid(): do_x() return redirect(home) return render(request, template_name, {form: form}) • 6 lines of code instead of 9 Advanced Django Form Usage • 33.3333333333% less code to debug @pydanny / @maraujop • One form instantiation • less conditionals == less edge case insanity 14
    41. 41. What aboutfile uploads?
    42. 42. Easy views.py + filesdef my_view(request, template_name=myapp/my_form.html): form = MyForm(request.POST or None, request.FILES or None) if form.is_valid(): do_x() return redirect(home) return render(request, template_name, {form: form}) Advanced Django Form Usage @pydanny / @maraujop 16
    43. 43. Easy views.py + filesdef my_view(request, template_name=myapp/my_form.html): form = MyForm(request.POST or None, request.FILES or None) if form.is_valid(): do_x() return redirect(home) return render(request, template_name, {form: form}) Request.POST or None Advanced Django Form Usage @pydanny / @maraujop 16
    44. 44. Easy views.py + filesdef my_view(request, template_name=myapp/my_form.html): form = MyForm(request.POST or None, request.FILES or None) if form.is_valid(): do_x() return redirect(home) return render(request, template_name, {form: form}) Request.POST Request.FILES or None or None Advanced Django Form Usage @pydanny / @maraujop 16
    45. 45. Easy views.py + filesdef my_view(request, template_name=myapp/my_form.html): form = MyForm(request.POST or None, request.FILES or None) if form.is_valid(): do_x() return redirect(home) return render(request, template_name, {form: form}) Request.POST Request.FILES or None or None Advanced Django Form Usage @pydanny / @maraujop • 6 lines of code again! 16
    46. 46. Easy views.py + filesdef my_view(request, template_name=myapp/my_form.html): form = MyForm(request.POST or None, request.FILES or None) if form.is_valid(): do_x() return redirect(home) return render(request, template_name, {form: form}) Request.POST Request.FILES or None or None Advanced Django Form Usage @pydanny / @maraujop • 6 lines of code again! • Code donated by Audrey Roy 16
    47. 47. What aboutModelForms?
    48. 48. Advanced Django Form Usage @pydanny / @maraujoppydanny made up statistics 18
    49. 49. pydanny made up statistics• 91% of all Django projects use ModelForms Advanced Django Form Usage @pydanny / @maraujop 18
    50. 50. pydanny made up statistics• 91% of all Django projects use ModelForms• 80% ModelForms require trivial logic Advanced Django Form Usage @pydanny / @maraujop 18
    51. 51. pydanny made up statistics• 91% of all Django projects use ModelForms• 80% ModelForms require trivial logic• 20% ModelForms require complicated logic Advanced Django Form Usage @pydanny / @maraujop 18
    52. 52. pydanny made up statistics• 91% of all Django projects use ModelForms• 80% ModelForms require trivial logic• 20% ModelForms require complicated logic Advanced Django Form Usage @pydanny / @maraujop Let’s try and make that easy 18
    53. 53. A Basic ModelFormclass MyModelForm(forms.Form): class Meta: model = MyModel fields = [name] Advanced Django Form Usage @pydanny / @maraujop 19
    54. 54. Classic views.py for ModelFormdef my_model_edit(request, slug=slug, template_name=myapp/my_model_form.html): # I wouldnt call the variable model, because its an instance of a model, its confusing mymodel = get_object_or_404(MyModel, slug=slug) if request.method == POST: form = MyForm(request, instance=mymodel) if form.is_valid(): mymodel = form.save() mymodel.day_shown = datetime.datetime.now() # Do any extra model stuff here mymodel.save() return redirect(home) else: form = MyForm(instance=mymodel) return render(request, template_name, {form: form, model: mymodel}) Advanced Django Form Usage @pydanny / @maraujop 20
    55. 55. Classic views.py for ModelFormdef my_model_edit(request, slug=slug, template_name=myapp/my_model_form.html): # I wouldnt call the variable model, because its an instance of a model, its confusing mymodel = get_object_or_404(MyModel, slug=slug) if request.method == POST: form = MyForm(request, instance=mymodel) Form #1 if form.is_valid(): mymodel = form.save() mymodel.day_shown = datetime.datetime.now() # Do any extra model stuff here mymodel.save() return redirect(home) else: form = MyForm(instance=mymodel) return render(request, template_name, {form: form, model: mymodel}) Advanced Django Form Usage @pydanny / @maraujop 20
    56. 56. Classic views.py for ModelFormdef my_model_edit(request, slug=slug, template_name=myapp/my_model_form.html): # I wouldnt call the variable model, because its an instance of a model, its confusing mymodel = get_object_or_404(MyModel, slug=slug) if request.method == POST: form = MyForm(request, instance=mymodel) Form #1 if form.is_valid(): mymodel = form.save() mymodel.day_shown = datetime.datetime.now() # Do any extra model stuff here mymodel.save() return redirect(home) Form #2 else: form = MyForm(instance=mymodel) return render(request, template_name, {form: form, model: mymodel}) Advanced Django Form Usage @pydanny / @maraujop 20
    57. 57. Classic views.py for ModelForm def my_model_edit(request, slug=slug, template_name=myapp/my_model_form.html): # I wouldnt call the variable model, because its an instance of a model, its confusing mymodel = get_object_or_404(MyModel, slug=slug) if request.method == POST: form = MyForm(request, instance=mymodel) Form #1 if form.is_valid(): mymodel = form.save() mymodel.day_shown = datetime.datetime.now() # Do any extra model stuff here mymodel.save() return redirect(home) Form #2 else: form = MyForm(instance=mymodel) return render(request, template_name, {form: form, model: mymodel})Only 1 nested if, but real code Advanced Django Form Usage gets much more complex @pydanny / @maraujop 20
    58. 58. Classic views.py for ModelForm def my_model_edit(request, slug=slug, template_name=myapp/my_model_form.html): # I wouldnt call the variable model, because its an instance of a model, its confusing mymodel = get_object_or_404(MyModel, slug=slug) if request.method == POST: form = MyForm(request, instance=mymodel) Form #1 if form.is_valid(): mymodel = form.save() mymodel.day_shown = datetime.datetime.now() # Do any extra model stuff here mymodel.save() return redirect(home) Form #2 else: form = MyForm(instance=mymodel) return render(request, template_name, {form: form, model: mymodel})Only 1 nested if, but real code Advanced Django Form Usage gets much more complex @pydanny / @maraujop Custom business goes here 20
    59. 59. Classic views.py for ModelForm def my_model_edit(request, slug=slug, template_name=myapp/my_model_form.html): # I wouldnt call the variable model, because its an instance of a model, its confusing mymodel = get_object_or_404(MyModel, slug=slug) if request.method == POST: form = MyForm(request, instance=mymodel) Form #1 if form.is_valid(): mymodel = form.save() mymodel.day_shown = datetime.datetime.now() # Do any extra model stuff here mymodel.save() return redirect(home) Form #2 else: form = MyForm(instance=mymodel) return render(request, template_name, {form: form, model: mymodel})Only 1 nested if, but real code Advanced Django Form Usage gets much more complex @pydanny / @maraujop Custom business goes here 20
    60. 60. Classic views.py for ModelFormdef my_model_edit(request, slug=slug, template_name=myapp/my_model_form.html): # I wouldnt call the variable model, because its an instance of a model, its confusing mymodel = get_object_or_404(MyModel, slug=slug) if request.method == POST: form = MyForm(request, instance=mymodel) if form.is_valid(): mymodel = form.save() mymodel.day_shown = datetime.datetime.now() # Do any extra model stuff here mymodel.save() return redirect(home) else: form = MyForm(instance=mymodel) return render(request, template_name, {form: form, model: mymodel}) Advanced Django Form Usage @pydanny / @maraujop 21
    61. 61. Classic views.py for ModelFormdef my_model_edit(request, slug=slug, template_name=myapp/my_model_form.html): # I wouldnt call the variable model, because its an instance of a model, its confusing mymodel = get_object_or_404(MyModel, slug=slug) if request.method == POST: form = MyForm(request, instance=mymodel) if form.is_valid(): mymodel = form.save() mymodel.day_shown = datetime.datetime.now() # Do any extra model stuff here mymodel.save() return redirect(home) else: form = MyForm(instance=mymodel) return render(request, template_name, {form: form, model: mymodel}) • 12 lines of code Advanced Django Form Usage @pydanny / @maraujop 21
    62. 62. Classic views.py for ModelFormdef my_model_edit(request, slug=slug, template_name=myapp/my_model_form.html): # I wouldnt call the variable model, because its an instance of a model, its confusing mymodel = get_object_or_404(MyModel, slug=slug) if request.method == POST: form = MyForm(request, instance=mymodel) if form.is_valid(): mymodel = form.save() mymodel.day_shown = datetime.datetime.now() # Do any extra model stuff here mymodel.save() return redirect(home) else: form = MyForm(instance=mymodel) return render(request, template_name, {form: form, model: mymodel}) • 12 lines of code Advanced Django Form Usage • Nested conditionals @pydanny / @maraujop 21
    63. 63. Classic views.py for ModelFormdef my_model_edit(request, slug=slug, template_name=myapp/my_model_form.html): # I wouldnt call the variable model, because its an instance of a model, its confusing mymodel = get_object_or_404(MyModel, slug=slug) if request.method == POST: form = MyForm(request, instance=mymodel) if form.is_valid(): mymodel = form.save() mymodel.day_shown = datetime.datetime.now() # Do any extra model stuff here mymodel.save() return redirect(home) else: form = MyForm(instance=mymodel) return render(request, template_name, {form: form, model: mymodel}) • 12 lines of code Advanced Django Form Usage • Nested conditionals @pydanny / @maraujop • What if we have to handle 3 different submit buttons? 21
    64. 64. Classic views.py for ModelFormdef my_model_edit(request, slug=slug, template_name=myapp/my_model_form.html): # I wouldnt call the variable model, because its an instance of a model, its confusing mymodel = get_object_or_404(MyModel, slug=slug) if request.method == POST: form = MyForm(request, instance=mymodel) if form.is_valid(): mymodel = form.save() mymodel.day_shown = datetime.datetime.now() # Do any extra model stuff here mymodel.save() return redirect(home) else: form = MyForm(instance=mymodel) return render(request, template_name, {form: form, model: mymodel}) • 12 lines of code Advanced Django Form Usage • Nested conditionals @pydanny / @maraujop • What if we have to handle 3 different submit buttons? • Watch out for edge case insanity! 21
    65. 65. easy views.py + ModelFormdef my_model_edit(request, slug=slug, template_name=myapp/my_model_form.html): mymodel = get_object_or_404(MyModel, slug=slug) form = MyModelForm(request.POST or None, instance=mymodel) if form.is_valid(): mymodel = form.save() mymodel.edited_at_djangocon = True mymodel.save() return redirect(home) return render(request, template_name, {form: form, mymodel: mymodel}) Advanced Django Form Usage @pydanny / @maraujop 22
    66. 66. easy views.py + ModelFormdef my_model_edit(request, slug=slug, template_name=myapp/my_model_form.html): mymodel = get_object_or_404(MyModel, slug=slug) form = MyModelForm(request.POST or None, instance=mymodel) Single form if form.is_valid(): mymodel = form.save() mymodel.edited_at_djangocon = True mymodel.save() return redirect(home) return render(request, template_name, {form: form, mymodel: mymodel}) Advanced Django Form Usage @pydanny / @maraujop 22
    67. 67. easy views.py + ModelFormdef my_model_edit(request, slug=slug, template_name=myapp/my_model_form.html): mymodel = get_object_or_404(MyModel, slug=slug) form = MyModelForm(request.POST or None, instance=mymodel) Single form if form.is_valid(): mymodel = form.save() mymodel.edited_at_djangocon = True mymodel.save() return redirect(home) return render(request, template_name, {form: form, mymodel: mymodel}) Advanced Django Form UsageCustom business @pydanny / @maraujop goes here 22
    68. 68. easy views.py + ModelFormdef my_model_edit(request, slug=slug, template_name=myapp/my_model_form.html): mymodel = get_object_or_404(MyModel, slug=slug) form = MyModelForm(request.POST or None, instance=mymodel) Single form if form.is_valid(): mymodel = form.save() mymodel.edited_at_djangocon = True mymodel.save() return redirect(home) return render(request, template_name, {form: form, mymodel: mymodel}) If no request.POST, then instantiated with None Advanced Django Form UsageCustom business @pydanny / @maraujop goes here So this will fail validation! 22
    69. 69. easy views.py + ModelFormdef my_model_edit(request, slug=slug, template_name=myapp/my_model_form.html): mymodel = get_object_or_404(MyModel, slug=slug) form = MyModelForm(request.POST or None, instance=mymodel) if form.is_valid(): mymodel = form.save() mymodel.edited_at_djangocon = True mymodel.save() return redirect(home) return render(request, template_name, {form: form, mymodel: mymodel}) Advanced Django Form Usage @pydanny / @maraujop 23
    70. 70. easy views.py + ModelFormdef my_model_edit(request, slug=slug, template_name=myapp/my_model_form.html): mymodel = get_object_or_404(MyModel, slug=slug) form = MyModelForm(request.POST or None, instance=mymodel) if form.is_valid(): mymodel = form.save() mymodel.edited_at_djangocon = True mymodel.save() return redirect(home) return render(request, template_name, {form: form, mymodel: mymodel}) • 9 lines of code instead of 12 Advanced Django Form Usage @pydanny / @maraujop 23
    71. 71. easy views.py + ModelFormdef my_model_edit(request, slug=slug, template_name=myapp/my_model_form.html): mymodel = get_object_or_404(MyModel, slug=slug) form = MyModelForm(request.POST or None, instance=mymodel) if form.is_valid(): mymodel = form.save() mymodel.edited_at_djangocon = True mymodel.save() return redirect(home) return render(request, template_name, {form: form, mymodel: mymodel}) • 9 lines of code instead of 12 Advanced Django Form Usage @pydanny / @maraujop • One conditional 23
    72. 72. easy views.py + ModelFormdef my_model_edit(request, slug=slug, template_name=myapp/my_model_form.html): mymodel = get_object_or_404(MyModel, slug=slug) form = MyModelForm(request.POST or None, instance=mymodel) if form.is_valid(): mymodel = form.save() mymodel.edited_at_djangocon = True mymodel.save() return redirect(home) return render(request, template_name, {form: form, mymodel: mymodel}) • 9 lines of code instead of 12 Advanced Django Form Usage @pydanny / @maraujop • One conditional • clear and succinct code 23
    73. 73. What about newmodel instances?
    74. 74. add views.py + ModelFormdef my_model_add(request, template_name=myapp/my_model_form.html): form = MyModelForm(request.POST or None) if form.is_valid(): mymodel = form.save() No need for an mymodel.added_at_djangocon = True mymodel.save() instance here return redirect(home) return render(request,template_name,{form: form,mymodel:mymodel}) Advanced Django Form Usage @pydanny / @maraujop 25
    75. 75. add views.py + ModelFormdef my_model_add(request, template_name=myapp/my_model_form.html): form = MyModelForm(request.POST or None) if form.is_valid(): mymodel = form.save() No need for an mymodel.added_at_djangocon = True mymodel.save() instance here return redirect(home) return render(request,template_name,{form: form,mymodel:mymodel}) Advanced Django Form Usage This creates the @pydanny / @maraujop model instance. 25
    76. 76. I can make it smaller!def my_model_tiny_add(request,template_name=myapp/my_model_form.html): form = MyModelForm(request.POST or None) if form.is_valid(): form.save() Not setting defaults here return redirect(home) return render(request,template_name,{form:form,mymodel:mymodel}) Advanced Django Form Usage Good practice: Use model field defaults rather @pydanny / @maraujop than doing it in your views. 26
    77. 77. How do you test this stuff?
    78. 78. Advanced Django Form Usage @pydanny / @maraujopPlease don’t manually test your forms 28
    79. 79. Please don’t manually test your forms• This isn’t a testing talk but... Advanced Django Form Usage @pydanny / @maraujop 28
    80. 80. Please don’t manually test your forms• This isn’t a testing talk but...• Forms are the number one thing to test Advanced Django Form Usage @pydanny / @maraujop 28
    81. 81. Please don’t manually test your forms• This isn’t a testing talk but...• Forms are the number one thing to test• Don’t skip on testing them Advanced Django Form Usage @pydanny / @maraujop 28
    82. 82. Please don’t manually test your forms• This isn’t a testing talk but...• Forms are the number one thing to test• Don’t skip on testing them• Edge case insanity is the thing to fear Advanced Django Form Usage @pydanny / @maraujop 28
    83. 83. Django unit tests!def test_add_package_view(self): url = reverse(my-url) response = self.client.get(url) self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, package/package_form.html) for c in Category.objects.all(): self.assertContains(response, c.title) count = Package.objects.count() response = self.client.post(url, { category: Category.objects.all()[0].pk, repo_url: http://github.com/django/django, Advanced Django Form Usage slug: test-slug, @pydanny / @maraujop title: TEST TITLE, }, follow=True) self.assertEqual(Package.objects.count(), count + 1) self.assertContains(response, "Django") 29
    84. 84. Django unit tests!def test_add_package_view(self): url = reverse(my-url) response = self.client.get(url) POSTing a form self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, package/package_form.html) for c in Category.objects.all(): self.assertContains(response, c.title) count = Package.objects.count() response = self.client.post(url, { category: Category.objects.all()[0].pk, repo_url: http://github.com/django/django, Advanced Django Form Usage slug: test-slug, @pydanny / @maraujop title: TEST TITLE, }, follow=True) self.assertEqual(Package.objects.count(), count + 1) self.assertContains(response, "Django") 29
    85. 85. Django unit tests!def test_add_package_view(self): url = reverse(my-url) response = self.client.get(url) POSTing a form self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, package/package_form.html) for c in Category.objects.all(): self.assertContains(response, c.title) count = Package.objects.count() follow=True response = self.client.post(url, { category: Category.objects.all()[0].pk, repo_url: http://github.com/django/django, Advanced Django Form Usage slug: test-slug, @pydanny / @maraujop title: TEST TITLE, }, follow=True) self.assertEqual(Package.objects.count(), count + 1) self.assertContains(response, "Django") 29
    86. 86. Django unit tests!def test_add_package_view(self): url = reverse(my-url) response = self.client.get(url) POSTing a form self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, package/package_form.html) for c in Category.objects.all(): self.assertContains(response, c.title) count = Package.objects.count() follow=True response = self.client.post(url, { category: Category.objects.all()[0].pk, assertContains is repo_url: http://github.com/django/django, Advanced Django Form Usage slug: test-slug, your best friend @pydanny / @maraujop title: TEST TITLE, }, follow=True) self.assertEqual(Package.objects.count(), count + 1) self.assertContains(response, "Django") 29
    87. 87. Can wechange non-required to required?
    88. 88. non-required to required• Your model fields are non-required• but you want the form fields to be required Advanced Django Form Usage @pydanny / @maraujop 31
    89. 89. A basic Django models.pyclass MyModel(models.Model): name = models.CharField(_(Name), max_length=50, blank=True, null=True) age = models.IntegerField(_(Age in years), blank=True, null=True) profession = models.CharField(_(Profession), max_length=100, blank=True, null=True) bio = models.TextField(_(Bio), blank=True, null=True) Advanced Django Form Usage @pydanny / @maraujop 32
    90. 90. Classic forms.py overloadclass MyModelTooMuchTypingForm(forms.ModelForm): """ Ive done this and it sucks hard to debug and too much duplication """ name = forms.CharField(_(Name), max_length=50, required=True) age = forms.IntegerField(_(Age in years), required=True) profession = forms.CharField(_(Profession), required=True) bio = forms.TextField(_(Bio), required=True) class Meta: model = MyModel Advanced Django Form Usage @pydanny / @maraujop 33
    91. 91. Classic forms.py overloadclass MyModelTooMuchTypingForm(forms.ModelForm): """ Ive done this and it sucks hard to debug and too much duplication """ name = forms.CharField(_(Name), max_length=50, required=True) age = forms.IntegerField(_(Age in years), required=True) profession = forms.CharField(_(Profession), required=True) bio = forms.TextField(_(Bio), required=True) class Meta: model = MyModel Advanced Django Form Usage @pydanny / @maraujop class MyModel(models.Model): name = models.CharField(_(Name), max_length=50, blank=True, null=True) age = models.IntegerField(_(Age in years), blank=True, null=True) profession = models.CharField(_(Profession), max_length=100, blank=True, null=True) bio = models.TextField(_(Bio), blank=True, null=True) 33
    92. 92. Classic forms.py overloadclass MyModelTooMuchTypingForm(forms.ModelForm): """ Ive done this and it sucks hard to debug and too much duplication """ name = forms.CharField(_(Name), max_length=50, required=True) age = forms.IntegerField(_(Age in years), required=True) profession = forms.CharField(_(Profession), required=True) bio = forms.TextField(_(Bio), required=True) class Meta: Nearly duplicated code model = MyModel Advanced Django Form Usage @pydanny / @maraujop class MyModel(models.Model): name = models.CharField(_(Name), max_length=50, blank=True, null=True) age = models.IntegerField(_(Age in years), blank=True, null=True) profession = models.CharField(_(Profession), max_length=100, blank=True, null=True) bio = models.TextField(_(Bio), blank=True, null=True) 33
    93. 93. Classic forms.py overloadclass MyModelTooMuchTypingForm(forms.ModelForm): """ Ive done this and it sucks hard to debug and too much duplication """ name = forms.CharField(_(Name), max_length=50, required=True) age = forms.IntegerField(_(Age in years), required=True) profession = forms.CharField(_(Profession), required=True) bio = forms.TextField(_(Bio), required=True) class Meta: Nearly duplicated code model = MyModel Advanced Django Form Usage @pydanny / @maraujop class MyModel(models.Model): name = models.CharField(_(Name), max_length=50, blank=True, null=True) age = models.IntegerField(_(Age in years), blank=True, null=True) profession = models.CharField(_(Profession), max_length=100, blank=True, null=True) bio = models.TextField(_(Bio), blank=True, null=True) 33
    94. 94. Better forms.py overloadclass MyModelForm(forms.ModelForm): """ Much better and you are extending, not copy/pasting """ def __init__(self): super(MyModelForm, self).__init__(*args, **kwargs) self.fields[name].required = True self.fields[age].required = True self.fields[profession].required = True self.fields[profession].help_text = _("Hi professor") Advanced Django Form Usage class Meta: Fields are in a dict-like object @pydanny / @maraujop model = MyModel 34
    95. 95. Try it with inheritance!class BaseEmailForm(forms.Form): email = forms.EmailField(_(Email)) confirm_email = forms.EmailField(_(Email 2))class ContactForm(BaseEmailForm): message = forms.CharField(_(Message)) def __init__(self): super(ContactForm, self).__init__(*args, **kwargs) self.fields[confirm_email].label = _(Confirm your email) self.fields[confirm_email].description = _(We want to be absolutely certain we have your correct email address.) Advanced Django Form Usage @pydanny / @maraujop 35
    96. 96. Add dynamic fields to a form
    97. 97. Dynamically adding fields to a formdef my_view(request, template_name=myapp/my_model_form.html): form = MyModelForm(request.POST or None) # Lets add a field on the go, needs to be done before validating it form.fields[favorite_color] = forms.ChoiceField( label = "Which is your favorite color from these?", choices = ((blue, blue), (red, red), (green, green)), widget = forms.RadioSelect, required = True, ) if form.is_valid(): # Lets get users favorite color, # you can do whatever you want with it favorite_color = form.cleaned_data[favorite_color] Advanced Django Form Usage form.save() @pydanny / @maraujop return redirect(home) return render(request, template_name, {form: form}) 37
    98. 98. Dynamically adding fields to a formdef my_view(request, template_name=myapp/my_model_form.html): Form dictionary form = MyModelForm(request.POST or None) of fields # Lets add a field on the go, needs to be done before validating it form.fields[favorite_color] = forms.ChoiceField( label = "Which is your favorite color from these?", choices = ((blue, blue), (red, red), (green, green)), widget = forms.RadioSelect, required = True, ) if form.is_valid(): # Lets get users favorite color, # you can do whatever you want with it favorite_color = form.cleaned_data[favorite_color] Advanced Django Form Usage form.save() @pydanny / @maraujop return redirect(home) return render(request, template_name, {form: form}) 37
    99. 99. Dynamically adding fields to a formdef my_view(request, template_name=myapp/my_model_form.html): Form dictionary form = MyModelForm(request.POST or None) of fields # Lets add a field on the go, needs to be done before validating it form.fields[favorite_color] = forms.ChoiceField( label = "Which is your favorite color from these?", choices = ((blue, blue), (red, red), (green, green)), widget = forms.RadioSelect, required = True, ) Fields have to be added before the form.is_valid if form.is_valid(): # Lets get users favorite color, # you can do whatever you want with it favorite_color = form.cleaned_data[favorite_color] method is checked. Advanced Django Form Usage form.save() @pydanny / @maraujop return redirect(home) return render(request, template_name, {form: form}) 37
    100. 100. Can’t we just pass in alist of fields into a form from a view?
    101. 101. Constructor overrides forms.pyclass MyModelForm(forms.ModelForm): def __init__(self, *args, **kwargs): extra = kwargs.pop(extra) super(UserCreationForm, self).__init__(*args, **kwargs) for i, question in enumerate(extra): self.fields[custom_%s % i] = forms.CharField(label=question) def extra_fields(self): looping over """ Returns a tuple (question, answer) ‘extra’ iterable Advanced Django Form Usage """ @pydanny / @maraujop for name, value in self.cleaned_data.items(): if name.startswith(custom_): yield (self.fields[name].label, value) 39
    102. 102. Constructor overrides views.py def my_view(request, template_name=myapp/my_model_form.html): form = MyModelForm(request.POST or None, extra=["Whats your pets name?"]) Passing in a list if form.is_valid(): # We can gather extra fields data doing of form field for (question, answer) in form.extra_fields(): titles save_answer(request, question, answer) form.save() return redirect(home) Advanced Django Form Usage @pydanny / @maraujop return render(request, template_name, {form: form}) 40
    103. 103. I need a formset
    104. 104. formsets.pyclass ItemFormSet(BaseFormSet): def __init__(self, numberItems, *args, **kwargs): super(ItemFormSet, self).__init__(*args, **kwargs) self.numberItems = numberItems def clean(self): # Dont bother validating the formset # unless each form is valid on its own if any(self.errors): return for form in self.forms: Advanced Django Form Usage if not form.cleaned_data[your_choice] == mod_wsgi: @pydanny / @maraujop raise ValidationError(umod_wsgi is the way to go!) 42
    105. 105. formsets.pyclass ItemFormSet(BaseFormSet): def __init__(self, numberItems, *args, **kwargs): super(ItemFormSet, self).__init__(*args, **kwargs) self.numberItems = numberItems def clean(self): Forms must have # Dont bother validating the formset ‘your_choice’ field that # unless each form is valid on its own if any(self.errors): return only accepts “mod_wsgi” for form in self.forms: Advanced Django Form Usage if not form.cleaned_data[your_choice] == mod_wsgi: @pydanny / @maraujop raise ValidationError(umod_wsgi is the way to go!) 42
    106. 106. views.pyfrom django.forms.models import formset_factorydef wsgi_form_view(request): WsgiFormset = formset_factory(ExampleForm, extra=5, formset=ItemFormSet) formset = WsgiFormset(bogus_number, request.POST, request.FILES) Advanced Django Form Usage @pydanny / @maraujop Truncated formset view 43
    107. 107. Lets doprogrammatic layout of forms
    108. 108. Things I want python to describe in forms• Different fieldsets within the same form• Various buttons • submit • reset Advanced Django Form Usage @pydanny / @maraujop 45
    109. 109. Advanced Django Form Usage @pydanny / @maraujopdjango-uni-form 46
    110. 110. Programmatic layoutsclass ExampleForm(forms.Form): def __init__(self, *args, **kwargs): self.helper = FormHelper() self.helper.layout = Layout( Fieldset( first arg is the legend of the fieldset, like_website, favorite_number, ), Fieldset( second arg is the legend of the fieldset, favorite_color, favorite_food, Advanced Django Form Usage ) @pydanny / @maraujop ButtonHolder( Submit(submit, Submit, css_class=button white) ) ) return super(ExampleForm, 47 self).__init__(*args, **kwargs)
    111. 111. Programmatic layouts from uni_form.helpers import FormHelper, Submit, Reset from uni_form.helpers import Fieldset, ButtonHolder, Layoutclass ExampleForm(forms.Form): def __init__(self, *args, **kwargs): self.helper = FormHelper() self.helper.layout = Layout( Fieldset( first arg is the legend of the fieldset, like_website, favorite_number, ), Fieldset( second arg is the legend of the fieldset, favorite_color, favorite_food, Advanced Django Form Usage ) @pydanny / @maraujop ButtonHolder( Submit(submit, Submit, css_class=button white) ) ) return super(ExampleForm, 47 self).__init__(*args, **kwargs)
    112. 112. Programmatic layouts from uni_form.helpers import FormHelper, Submit, Reset from uni_form.helpers import Fieldset, ButtonHolder, Layoutclass ExampleForm(forms.Form): FormHelper def __init__(self, *args, **kwargs): self.helper = FormHelper() self.helper.layout = Layout( Fieldset( first arg is the legend of the fieldset, like_website, favorite_number, ), Fieldset( second arg is the legend of the fieldset, favorite_color, favorite_food, Advanced Django Form Usage ) @pydanny / @maraujop ButtonHolder( Submit(submit, Submit, css_class=button white) ) ) return super(ExampleForm, 47 self).__init__(*args, **kwargs)
    113. 113. Programmatic layouts from uni_form.helpers import FormHelper, Submit, Reset from uni_form.helpers import Fieldset, ButtonHolder, Layoutclass ExampleForm(forms.Form): FormHelper def __init__(self, *args, **kwargs): self.helper = FormHelper() self.helper.layout = Layout( Layout Fieldset( first arg is the legend of the fieldset, like_website, favorite_number, ), Fieldset( second arg is the legend of the fieldset, favorite_color, favorite_food, Advanced Django Form Usage ) @pydanny / @maraujop ButtonHolder( Submit(submit, Submit, css_class=button white) ) ) return super(ExampleForm, 47 self).__init__(*args, **kwargs)
    114. 114. Programmatic layouts from uni_form.helpers import FormHelper, Submit, Reset from uni_form.helpers import Fieldset, ButtonHolder, Layoutclass ExampleForm(forms.Form): FormHelper def __init__(self, *args, **kwargs): self.helper = FormHelper() self.helper.layout = Layout( Layout Fieldset( first arg is the legend of the fieldset, Fieldset like_website, favorite_number, ), Fieldset( second arg is the legend of the fieldset, favorite_color, favorite_food, Advanced Django Form Usage ) @pydanny / @maraujop ButtonHolder( Submit(submit, Submit, css_class=button white) ) ) return super(ExampleForm, 47 self).__init__(*args, **kwargs)
    115. 115. Programmatic layouts from uni_form.helpers import FormHelper, Submit, Reset from uni_form.helpers import Fieldset, ButtonHolder, Layoutclass ExampleForm(forms.Form): FormHelper def __init__(self, *args, **kwargs): self.helper = FormHelper() self.helper.layout = Layout( Layout Fieldset( first arg is the legend of the fieldset, Fieldset like_website, favorite_number, ), Fieldset( second arg is the legend of the fieldset, favorite_color, Button favorite_food, Holder Advanced Django Form Usage ) @pydanny / @maraujop ButtonHolder( Submit(submit, Submit, css_class=button white) ) ) return super(ExampleForm, 47 self).__init__(*args, **kwargs)
    116. 116. Programmatic layouts from uni_form.helpers import FormHelper, Submit, Reset from uni_form.helpers import Fieldset, ButtonHolder, Layoutclass ExampleForm(forms.Form): FormHelper def __init__(self, *args, **kwargs): self.helper = FormHelper() self.helper.layout = Layout( Layout Fieldset( first arg is the legend of the fieldset, Fieldset like_website, favorite_number, ), Fieldset( second arg is the legend of the fieldset, Button favorite_color, Button favorite_food, Holder Advanced Django Form Usage ) @pydanny / @maraujop ButtonHolder( Submit(submit, Submit, css_class=button white) ) ) return super(ExampleForm, 47 self).__init__(*args, **kwargs)
    117. 117. renders as divs{% load uni_form_tags %}{% uni_form my_form my_form.helper %} Renders the HTML form with buttons and everything wrapped in a fieldset. Advanced Django Form Usage @pydanny / @maraujop 48
    118. 118. Sample output<form action="#" class="uniForm"> <fieldset class="inlineLabels"> <div class="ctrlHolder"> <label for="name">Name</label> <input type="text" id="name" name="name" value="" size="35" class="textInput"/> </div> <div class="ctrlHolder"> <label for="email">Email</label> <input type="text" id="email" name="email" value="" size="35" class="textInput"/> </div> <div class="ctrlHolder"> <label for="comment">Comment</label> <textarea id="comment" name="comment" rows="25" cols="25"></textarea> </div> <div class="buttonHolder"> <label for="tos" class="secondaryAction"><input type="checkbox" id="tos" name="tos"/> Advanced Django Form UsageI agree to the <a href="#">terms of service</a></label> <button type="submit" class="primaryAction">Post comment</button> @pydanny / @maraujop </div> </fieldset></form> 49
    119. 119. django-uni-form• Programmatic layout• Div based forms• Section 508 compliant• Fully customizable templates Advanced Django Form Usage• http://django-uni-form.rtfd.org @pydanny / @maraujop 50
    120. 120. What aboutHTML5 Fields?
    121. 121. django-floppyforms• by Bruno Renie @brutasse• HTML5 Widgets• Fully customizable templates• Plays nice with django-uni-form Advanced Django Form Usage @pydanny / @maraujop 52
    122. 122. Easy to use! import floppyforms as forms class ExampleForm(forms.Form): username = forms.CharField( label=, widget = forms.TextInput( attrs={placeholder: @johndoe}, ), )• Django’s widget parameter attrs expects a dictionary Advanced Django Form Usage @pydanny / @maraujop• Replaces the normal widgets with HTML5 ones 53
    123. 123. Customizable widgetsimport floppyforms as formsclass OtherEmailInput(forms.EmailInput): template_name = path/to/other_email.html forms.py<input type="email" name="{{ name }}" id="{{ attrs.id }}" Advanced Django Form Usage placeholder="john@example.com" @pydanny / @maraujop {% if value %}value="{{ value }}"{% endif %}> path/to/other_email.html 54
    124. 124. Can you combinedjango-uni-form anddjango-floppyforms?
    125. 125. django-uni-form +django-floppyforms =AWESOME POWER
    126. 126. They get along wellclass ListFriendsForm(forms.Form): username = forms.CharField( widget = forms.TextInput( attrs={placeholder: @maraujop}, No changes or ) ), special code needed def __init__(self, *args, **kwargs): self.helper = FormHelper() self.helper.layout = Layout( Div( username, ) ButtonHolder( Submit(submit, Submit, css_class=button white) Advanced Django Form Usage ) @pydanny / @maraujop ) return super(ExampleForm, self).__init__(*args, **kwargs) 57
    127. 127. Forms refactor Django 1.4
    128. 128. Advanced Django Form Usage @pydanny / @maraujopWhat we are getting in 1.4 59
    129. 129. What we are getting in 1.4• Form rendering will use templates instead of HTML in Python Advanced Django Form Usage @pydanny / @maraujop 59
    130. 130. What we are getting in 1.4• Form rendering will use templates instead of HTML in Python • Template based widgets Advanced Django Form Usage @pydanny / @maraujop 59
    131. 131. What we are getting in 1.4• Form rendering will use templates instead of HTML in Python • Template based widgets • Template based form layout Advanced Django Form Usage @pydanny / @maraujop 59
    132. 132. Advanced Django Form Usage @pydanny / @maraujopforms refactor and django-uni-form 60
    133. 133. forms refactor and django-uni-form• forms refactor layout “lives” in templates Advanced Django Form Usage @pydanny / @maraujop 60
    134. 134. forms refactor and django-uni-form• forms refactor layout “lives” in templates• django-uni-form layout “lives” in python Advanced Django Form Usage @pydanny / @maraujop 60
    135. 135. forms refactor and django-uni-form• forms refactor layout “lives” in templates• django-uni-form layout “lives” in python• Different approaches, same objective Advanced Django Form Usage @pydanny / @maraujop 60
    136. 136. Give me a way to create custom form fields
    137. 137. The docs on custom fields“Its only requirements are that it implementa clean() method and that its __init__()method accept the core argumentsmentioned above (required, label, initial,widget, help_text).” Advanced Django Form Usage @pydanny / @maraujop 62
    138. 138. The docs on custom fields“Its only requirements are that it implementa clean() method and that its __init__()method accept the core argumentsmentioned above (required, label, initial,widget, help_text).” Advanced Django Form Usage The docs seem to be wrong @pydanny / @maraujop 62
    139. 139. How it really worksYou don’t need to implement “clean” You do need to implement:• required• widget • error_messages• label • show_hidden_initial Advanced Django Form Usage• help_text • validators @pydanny / @maraujop• initial • localize 63
    140. 140. How the heck then do I actually add a custom form field?!?
    141. 141. Advanced Django Form Usage @pydanny / @maraujopvalidation work?How does form 65
    142. 142. fields.pyclass AMarkField(forms.Field): widget = TextInput default_error_messages = { not_an_a: _(uyou can only input A here! damn!), } def __init__(self, **kwargs): super(AMarkField, self).__init__(**kwargs) def to_python(self, value): if value in validators.EMPTY_VALUES: Only accepts upper return None case A as input Advanced Django Form Usage if value != A: @pydanny / @maraujop raise ValidationError(self.error_messages[not_an_a]) return value 66
    143. 143. Not DRYclass MyModelForm(forms.ModelForm): mark = CharField() def clean_mark(self): mark_value = self.cleaned_data[mark] if mark_value is not None or mark_value.upper() != A: raise ValidationError(_(uOnly input A here!)) return mark_valueclass ExampleForm(forms.Form): mark = CharField() def clean_mark(self): Advanced Django Form Usage mark_value = self.cleaned_data[mark] @pydanny / @maraujop if mark_value is not None or mark_value.upper() != A: raise ValidationError(_(uOnly input A here!)) return mark_value 67
    144. 144. I want a custom field that validates JSON
    145. 145. The JSON fieldclass JSONField(forms.Field): default_error_messages = { invalid: This is not valid JSON string } def to_python(self, value): if value in validators.EMPTY_VALUES: return None try: json = simplejson.loads(value) Advanced Django Form Usage except ValueError: @pydanny / @maraujop raise ValidationError(self.error_messages[invalid]) return json 69
    146. 146. I want a customwidget that handles multiple widgets
    147. 147. widgets.pyfrom django.forms.extras.widgets import MultiWidgetclass AddressWidget(MultiWidget): def __init__(self, attrs=None): widgets = (TextInput, TextInput) super(AddressWidget, self).__init__(widgets, attrs) def decompress(self, value): """ If called, value is a string should return a list """ Advanced Django Form Usage if value: # parse stuff and return a list @pydanny / @maraujop return value.split() return [None, None] 71
    148. 148. widgets.pyfrom django.forms.extras.widgets import MultiWidgetclass AddressWidget(MultiWidget): Two TextInput def __init__(self, attrs=None): widgets!!! widgets = (TextInput, TextInput) super(AddressWidget, self).__init__(widgets, attrs) def decompress(self, value): """ If called, value is a string should return a list """ Advanced Django Form Usage if value: # parse stuff and return a list @pydanny / @maraujop return value.split() return [None, None] 71
    149. 149. widgets.pyfrom django.forms.extras.widgets import MultiWidgetclass AddressWidget(MultiWidget): Two TextInput def __init__(self, attrs=None): widgets!!! widgets = (TextInput, TextInput) super(AddressWidget, self).__init__(widgets, attrs) def decompress(self, value): """ If called, value is a string should return a list """ Advanced Django Form Usage if value: # parse stuff and return a list @pydanny / @maraujop return value.split() Called when a form with return [None, None] MultiWidget is passed initial or instance values 71
    150. 150. fields.py class AddressField(forms.Field): self.widget = AddressWidget def to_python(self, value): # Already gets a Python list return valueisinstance(["921 SW Sixth Avenue", "Portland"], list) Advanced Django Form Usage @pydanny / @maraujop 72
    151. 151. fields.py class AddressField(forms.Field): self.widget = AddressWidget def to_python(self, value): # Already gets a Python list return value isinstance(["921 SW Sixth Avenue", "Portland"], list) class ExampleForm(object): forms.CharField(widget=AddressWidget) Advanced Django Form Usageisinstance("921 SW Sixth Avenue, Portland", str) @pydanny / @maraujop 72
    152. 152. Give me a MultiValue, MultiWidget field
    153. 153. MultiValue, MultiWidget Field class AlternativeAddressField(forms.MultiValueField): widget = AddressWidget def __init__(self, *args, **kwargs): fields = (forms.CharField(), forms.CharField()) super(AlternativeAddressField, self).__init__(fields, *args, **kwargs) def compress(self, data_list): return data_list Advanced Django Form Usage @pydanny / @maraujop 74
    154. 154. MultiValue, MultiWidget Field class AlternativeAddressField(forms.MultiValueField): widget = AddressWidget def __init__(self, *args, **kwargs): fields = (forms.CharField(), forms.CharField()) super(AlternativeAddressField, self).__init__(fields, *args, **kwargs) def compress(self, data_list): return data_list• Clean does not work in the standard way Advanced Django Form Usage @pydanny / @maraujop 74
    155. 155. MultiValue, MultiWidget Field class AlternativeAddressField(forms.MultiValueField): widget = AddressWidget def __init__(self, *args, **kwargs): fields = (forms.CharField(), forms.CharField()) super(AlternativeAddressField, self).__init__(fields, *args, **kwargs) def compress(self, data_list): return data_list• Clean does not work in the standard way• Every field validated with its corresponding widget value Advanced Django Form Usage @pydanny / @maraujop 74
    156. 156. MultiValue, MultiWidget Field class AlternativeAddressField(forms.MultiValueField): widget = AddressWidget def __init__(self, *args, **kwargs): fields = (forms.CharField(), forms.CharField()) super(AlternativeAddressField, self).__init__(fields, *args, **kwargs) def compress(self, data_list): return data_list• Clean does not work in the standard way• Every field validated with its corresponding widget value Advanced Django Form Usage• @pydanny / @maraujop Beware! run_validator does run but validate is called 74
    157. 157. MultiValue, MultiWidget Field class AlternativeAddressField(forms.MultiValueField): widget = AddressWidget def __init__(self, *args, **kwargs): fields = (forms.CharField(), forms.CharField()) super(AlternativeAddressField, self).__init__(fields, *args, **kwargs) def compress(self, data_list): return data_list• Clean does not work in the standard way• Every field validated with its corresponding widget value Advanced Django Form Usage• @pydanny / @maraujop Beware! run_validator does run but validate is called Django Ticket #14184 74
    158. 158. Validators with Multi-Fieldclass AlternativeAddressField(forms.MultiValueField): widget = AddressWidget def __init__(self, *args, **kwargs): fields = (forms.CharField(), forms.CharField()) super(AlternativeAddressField, self).__init__(fields, *args, **kwargs) def validate(self, value): self._run_validators(value) def compress(self, data_list): Advanced Django Form Usage return data_list @pydanny / @maraujop 75
    159. 159. How about custom widget output?
    160. 160. Advanced Django Form Usage @pydanny / @maraujopCustom Widget Output So Easy! 77
    161. 161. def render(self, name, value, attrs=None): Custom Widget Output # HTML to be added to the output widget_labels = [ <label for="id_%s">Address: </label>, <label for="id_%s">Number: </label> ] if self.is_localized: for widget in self.widgets: widget.is_localized = self.is_localized # value is a list of values, each corresponding to a widget So Easy! # in self.widgets. if not isinstance(value, list): value = self.decompress(value) output = [] final_attrs = self.build_attrs(attrs) id_ = final_attrs.get(id, None) for i, widget in enumerate(self.widgets): try: widget_value = value[i] except IndexError: widget_value = None if id_: Advanced Django Form Usage final_attrs = dict(final_attrs, id=%s_%s % (id_, i)) # Adding labels @pydanny / @maraujop output.append(widget_labels[i] % (%s_%s % (name, i))) output.append(widget.render(name + _%s % i, widget_value, final_attrs)) return mark_safe(self.format_output(output)) 77
    162. 162. def render(self, name, value, attrs=None): Custom Widget Output # HTML to be added to the output widget_labels = [ <label for="id_%s">Address: </label>, <label for="id_%s">Number: </label> ] if self.is_localized: for widget in self.widgets: widget.is_localized = self.is_localized # value is a list of values, each corresponding to a widget So Easy! # in self.widgets. if not isinstance(value, list): value = self.decompress(value) output = [] final_attrs = self.build_attrs(attrs) id_ = final_attrs.get(id, None) for i, widget in enumerate(self.widgets): try: widget_value = value[i] except IndexError: widget_value = None if id_: Advanced Django Form Usage final_attrs = dict(final_attrs, id=%s_%s % (id_, i)) # Adding labels @pydanny / @maraujop output.append(widget_labels[i] % (%s_%s % (name, i))) output.append(widget.render(name + _%s % i, widget_value, final_attrs)) return mark_safe(self.format_output(output)) 77
    163. 163. Can’t you make iteasier to understand?
    1. A particular slide catching your eye?

      Clipping is a handy way to collect important slides you want to go back to later.

    ×