Ce diaporama a bien été signalé.
Nous utilisons votre profil LinkedIn et vos données d’activité pour vous proposer des publicités personnalisées et pertinentes. Vous pouvez changer vos préférences de publicités à tout moment.

Advanced Django forms usage

1 328 vues

Publié le

DjangoCon.us 2011 talk about Django forms advanced features given by Miguel Araujo and Daniel Greenfeld.

Publié dans : Technologie
  • Soyez le premier à commenter

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?

×