Advanced Django Forms Usage

68,857
-1

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
7 Comments
143 Likes
Statistics
Notes
No Downloads
Views
Total Views
68,857
On Slideshare
0
From Embeds
0
Number of Embeds
32
Actions
Shares
0
Downloads
1,431
Comments
7
Likes
143
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’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’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’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's override form constructor to wrap the logic of adding fields on the go\n\n
  • M - Let'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’s okay that there are different objects\n
  • D\nIt’s okay that there are different objects\n
  • D\nIt’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_<fieldname> 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.

    ×