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

Advanced Django Forms Usage

79,684 views

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

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?

×