Advanced Django
  Form Usage
by Daniel Greenfeld and Miguel Araujo
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 Usage
http://www.flickr.com/photos/pydanny/4442245488/
                                                      Fiancé, Audrey Roy (audreyr)




                                                                                           @pydanny / @maraujop
                                                       2
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
Advanced Django Form Usage
                                 @pydanny / @maraujop
Tons of technical content




                                                  4
Tons of technical content
•   We probably won’t have time for questions




                                                Advanced Django Form Usage
                                                     @pydanny / @maraujop
                          4
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
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
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
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
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
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
Advanced Django Form Usage
                          @pydanny / @maraujop
Good Form Patterns
 Fundamentals of




                                           5
Fundamentals of
   Good Form Patterns

• Zen of Python still applies




                                Advanced Django Form Usage
                                     @pydanny / @maraujop
                     5
Fundamentals of
   Good Form Patterns

• Zen of Python still applies
   • import this




                                Advanced Django Form Usage
                                     @pydanny / @maraujop
                     5
Fundamentals of
   Good Form Patterns

• Zen of Python still applies
   • import this
• Spartan programming als0 is important




                                          Advanced Django Form Usage
                                               @pydanny / @maraujop
                   5
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
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
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
Advanced Django Form Usage
                                  @pydanny / @maraujop
Calling forms the easy way




                                                   9
Calling forms the easy way

• Smaller, cleaner code makes our lives better




                                                 Advanced Django Form Usage
                                                      @pydanny / @maraujop
                      9
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
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
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
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
Enough theory
A Basic Django Form
class MyForm(forms.Form):

   name = forms.CharField(_('Name'), required=True)




                                                  Advanced Django Form Usage
                                                       @pydanny / @maraujop
                            11
Standard views.py
def 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
Standard views.py
def 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
Standard views.py
def 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
Standard views.py
def 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
Standard views.py
def 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
Standard views.py
def 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
Easy views.py
def 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
Custom business
   goes here
                            13
Single form
              Easy views.py
def 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
Custom business
   goes here
                            13
Single form
              Easy views.py
def 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 / @maraujop
Custom business         then instantiated with None
   goes here
                            13
Easy views.py
def 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
Easy views.py
def 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
Easy views.py
def 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
Easy views.py
def 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
Easy views.py
def 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
What about
file uploads?
Easy views.py + files
def 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
Easy views.py + files
def 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
Easy views.py + files
def 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
Easy views.py + files
def 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
Easy views.py + files
def 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
What about
ModelForms?
Advanced Django Form Usage
                      @pydanny / @maraujop
pydanny made
 up statistics




                                       18
pydanny made
          up statistics

• 91% of all Django projects use ModelForms




                                              Advanced Django Form Usage
                                                   @pydanny / @maraujop
                    18
pydanny made
          up statistics

• 91% of all Django projects use ModelForms
• 80% ModelForms require trivial logic




                                              Advanced Django Form Usage
                                                   @pydanny / @maraujop
                    18
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
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
A Basic ModelForm

class MyModelForm(forms.Form):

    class Meta:
        model = MyModel
        fields = ['name']




                                 Advanced Django Form Usage
                                      @pydanny / @maraujop
              19
Classic views.py for ModelForm
def my_model_edit(request, slug=slug, template_name='myapp/my_model_form.html'):

   # I wouldn't call the variable model, because it's an instance of a model, it's 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
Classic views.py for ModelForm
def my_model_edit(request, slug=slug, template_name='myapp/my_model_form.html'):

   # I wouldn't call the variable model, because it's an instance of a model, it's 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
Classic views.py for ModelForm
def my_model_edit(request, slug=slug, template_name='myapp/my_model_form.html'):

   # I wouldn't call the variable model, because it's an instance of a model, it's 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
Classic views.py for ModelForm
 def my_model_edit(request, slug=slug, template_name='myapp/my_model_form.html'):

    # I wouldn't call the variable model, because it's an instance of a model, it's 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
Classic views.py for ModelForm
 def my_model_edit(request, slug=slug, template_name='myapp/my_model_form.html'):

    # I wouldn't call the variable model, because it's an instance of a model, it's 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
Classic views.py for ModelForm
 def my_model_edit(request, slug=slug, template_name='myapp/my_model_form.html'):

    # I wouldn't call the variable model, because it's an instance of a model, it's 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
Classic views.py for ModelForm
def my_model_edit(request, slug=slug, template_name='myapp/my_model_form.html'):

   # I wouldn't call the variable model, because it's an instance of a model, it's 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
Classic views.py for ModelForm
def my_model_edit(request, slug=slug, template_name='myapp/my_model_form.html'):

   # I wouldn't call the variable model, because it's an instance of a model, it's 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
Classic views.py for ModelForm
def my_model_edit(request, slug=slug, template_name='myapp/my_model_form.html'):

   # I wouldn't call the variable model, because it's an instance of a model, it's 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
Classic views.py for ModelForm
def my_model_edit(request, slug=slug, template_name='myapp/my_model_form.html'):

   # I wouldn't call the variable model, because it's an instance of a model, it's 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
Classic views.py for ModelForm
def my_model_edit(request, slug=slug, template_name='myapp/my_model_form.html'):

   # I wouldn't call the variable model, because it's an instance of a model, it's 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
easy views.py + ModelForm
def 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
easy views.py + ModelForm
def 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
easy views.py + ModelForm
def 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
Custom business




                                                                              @pydanny / @maraujop
   goes here

                                    22
easy views.py + ModelForm
def 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 Usage
Custom business




                                                                              @pydanny / @maraujop
   goes here                      So this will fail validation!

                                    22
easy views.py + ModelForm
def 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
easy views.py + ModelForm
def 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
easy views.py + ModelForm
def 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
easy views.py + ModelForm
def 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
What about new
model instances?
add views.py + ModelForm
def 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
add views.py + ModelForm
def 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
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
How do you test
  this stuff?
Advanced Django Form Usage
                             @pydanny / @maraujop
Please don’t manually
   test your forms




                                              28
Please don’t manually
      test your forms

• This isn’t a testing talk but...




                                     Advanced Django Form Usage
                                          @pydanny / @maraujop
                       28
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
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
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
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
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
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
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
Can we
change non-required
    to required?
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
A basic Django models.py

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)




                                                                               Advanced Django Form Usage
                                                                                    @pydanny / @maraujop
                                       32
Classic forms.py overload
class MyModelTooMuchTypingForm(forms.ModelForm):
    """ I've 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
Classic forms.py overload
class MyModelTooMuchTypingForm(forms.ModelForm):
    """ I've 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
Classic forms.py overload
class MyModelTooMuchTypingForm(forms.ModelForm):
    """ I've 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
Classic forms.py overload
class MyModelTooMuchTypingForm(forms.ModelForm):
    """ I've 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
Better forms.py overload
class 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
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
Add dynamic fields
    to a form
Dynamically adding fields
                to a form
def my_view(request, template_name='myapp/my_model_form.html'):

   form = MyModelForm(request.POST or None)

   # Let's 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():
        # Let's get user's 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
Dynamically adding fields
                to a form
def my_view(request, template_name='myapp/my_model_form.html'):     Form dictionary
   form = MyModelForm(request.POST or None)                            of fields
   # Let's 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():
        # Let's get user's 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
Dynamically adding fields
                to a form
def my_view(request, template_name='myapp/my_model_form.html'):     Form dictionary
   form = MyModelForm(request.POST or None)                            of fields
   # Let's 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():
        # Let's get user's 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
Can’t we just pass in a
list of fields into a form
       from a view?
Constructor overrides forms.py
class 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
Constructor overrides views.py
 def my_view(request, template_name='myapp/my_model_form.html'):

    form = MyModelForm(request.POST or None,
                      extra=["What's your pet's 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
I need a formset
formsets.py
class ItemFormSet(BaseFormSet):
    def __init__(self, numberItems, *args, **kwargs):
        super(ItemFormSet, self).__init__(*args, **kwargs)
        self.numberItems = numberItems

    def clean(self):
        # Don't 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(u'mod_wsgi is the way to go!')




                                 42
formsets.py
class ItemFormSet(BaseFormSet):
    def __init__(self, numberItems, *args, **kwargs):
        super(ItemFormSet, self).__init__(*args, **kwargs)
        self.numberItems = numberItems

    def clean(self):
                                          Forms must have
        # Don't 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(u'mod_wsgi is the way to go!')




                                 42
views.py
from django.forms.models import formset_factory

def 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
Lets do
programmatic layout
     of forms
Things I want python
   to describe in forms

• Different fieldsets within the same form
• Various buttons
  • submit
  • reset




                                           Advanced Django Form Usage
                                                @pydanny / @maraujop
                   45
Advanced Django Form Usage
                       @pydanny / @maraujop
django-uni-form



                                        46
Programmatic layouts
class 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)
Programmatic layouts
  from uni_form.helpers import FormHelper, Submit, Reset
  from uni_form.helpers import Fieldset, ButtonHolder, Layout


class 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)
Programmatic layouts
  from uni_form.helpers import FormHelper, Submit, Reset
  from uni_form.helpers import Fieldset, ButtonHolder, Layout


class 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)
Programmatic layouts
  from uni_form.helpers import FormHelper, Submit, Reset
  from uni_form.helpers import Fieldset, ButtonHolder, Layout


class 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)
Programmatic layouts
  from uni_form.helpers import FormHelper, Submit, Reset
  from uni_form.helpers import Fieldset, ButtonHolder, Layout


class 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)
Programmatic layouts
  from uni_form.helpers import FormHelper, Submit, Reset
  from uni_form.helpers import Fieldset, ButtonHolder, Layout


class 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)
Programmatic layouts
  from uni_form.helpers import FormHelper, Submit, Reset
  from uni_form.helpers import Fieldset, ButtonHolder, Layout


class 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)
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
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 Usage
I agree to the <a href="#">terms of service</a></label>
      <button type="submit" class="primaryAction">Post comment</button>




                                                                                                   @pydanny / @maraujop
    </div>
  </fieldset>
</form>




                                           49
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
What about
HTML5 Fields?
django-floppyforms

• by Bruno Renie @brutasse
• HTML5 Widgets
• Fully customizable templates
• Plays nice with django-uni-form




                                    Advanced Django Form Usage
                                         @pydanny / @maraujop
                    52
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
Customizable widgets
import floppyforms as forms

class 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
Can you combine
django-uni-form and
django-floppyforms?
django-uni-form +
django-floppyforms
       =
AWESOME POWER
They get along well
class 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
Forms refactor
  Django 1.4
Advanced Django Form Usage
                                  @pydanny / @maraujop
What we are getting in 1.4




                                                   59
What we are getting in 1.4

• Form rendering will use templates instead
  of HTML in Python




                                              Advanced Django Form Usage
                                                   @pydanny / @maraujop
                    59
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
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
Advanced Django Form Usage
                          @pydanny / @maraujop
forms refactor and
 django-uni-form




                                           60
forms refactor and
    django-uni-form

• forms refactor layout “lives” in templates




                                               Advanced Django Form Usage
                                                    @pydanny / @maraujop
                     60
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
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
Give me a way to
 create custom
   form fields
The docs on custom fields

“Its only requirements are that it implement
a clean() method and that its __init__()
method accept the core arguments
mentioned above (required, label, initial,
widget, help_text).”




                                               Advanced Django Form Usage
                                                    @pydanny / @maraujop
                      62
The docs on custom fields

“Its only requirements are that it implement
a clean() method and that its __init__()
method accept the core arguments
mentioned above (required, label, initial,
widget, help_text).”




                                               Advanced Django Form Usage
     The docs seem to be wrong




                                                    @pydanny / @maraujop
                      62
How it really works
You 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
How the heck then do I
 actually add a custom
     form field?!?
Advanced Django Form Usage
                        @pydanny / @maraujop
validation work?
How does form




                                         65
fields.py
class AMarkField(forms.Field):
    widget = TextInput
    default_error_messages = {
        'not_an_a': _(u'you 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
Not DRY
class 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(_(u'Only input A here!'))

        return mark_value

class 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(_(u'Only input A here!'))

        return mark_value
                                 67
I want a custom field
 that validates JSON
The JSON field
class 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
I want a custom
widget that handles
 multiple widgets
widgets.py
from django.forms.extras.widgets import MultiWidget

class 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
widgets.py
from django.forms.extras.widgets import MultiWidget

class 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
widgets.py
from django.forms.extras.widgets import MultiWidget

class 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
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)




                                                  Advanced Django Form Usage
                                                       @pydanny / @maraujop
                          72
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 Usage
isinstance("921 SW Sixth Avenue, Portland", str)




                                                          @pydanny / @maraujop
                            72
Give me a MultiValue,
 MultiWidget field
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
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
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
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
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
Validators with Multi-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 validate(self, value):
        self._run_validators(value)

    def compress(self, data_list):




                                                                Advanced Django Form Usage
        return data_list




                                                                     @pydanny / @maraujop
                                 75
How about custom
 widget output?
Advanced Django Form Usage
                                       @pydanny / @maraujop
Custom Widget Output



                       So Easy!

                                                        77
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
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
Can’t you make it
easier to understand?
Problems with Custom
       Widget Output

• Overring render means indepth knowledge
• Hard to do custom output with built-in widgets
• You can’t call super.render and customize easily
• Templates? Open Ticket #15667




                                                     Advanced Django Form Usage
                                                          @pydanny / @maraujop
                        79
Not in Lawrence
Kansas anymore
Problems with Custom
       Widget Output

• Oldest ticket in Django is related to forms
  (#23)
• ComboField is broken
• Validators are thought to validate simple values




                                                     Advanced Django Form Usage
  because of the way run_validators is coded




                                                          @pydanny / @maraujop
                        81
HTML5 tickets


• Ticket #16304
• Ticket #16630
• Just use django-floppyforms




                               Advanced Django Form Usage
                                    @pydanny / @maraujop
                      82
Validators

•   Imagine you have an input that get a list of emails seperated
    by spaces

•   Your widget returns: [“a@example.com”, “b@example.com”]

•   You want to run validators on all of them

     •




                                                               Advanced Django Form Usage
         There is an EmailValidator




                                                                    @pydanny / @maraujop
                               83
MultiValidator
class MultiValidator(object):
    def __init__(self, validators):
        self.validators = validators

    def __call__(self, data_list):
        errors = []
        for value in data_list:
            for validator in self.validators:
                try:
                    validator(value)
                except ValidationError, e:




                                                          Advanced Django Form Usage
                     if hasattr(e, 'code'):
                         errors.append("FiXED MESSAGE")




                                                               @pydanny / @maraujop
        raise ValidationError(errors)


                           84
Amazing stuff



• Ticket #27 reported by Adrian Holovaty
• Single form field for multiple database fields




                                                 Advanced Django Form Usage
                                                      @pydanny / @maraujop
                      85
Fin
Advanced Django Form Usage
                     @pydanny / @maraujop
This has been
 incredible




                                      87
This has been
         incredible

• We’ve just scratched the surface




                                     Advanced Django Form Usage
                                          @pydanny / @maraujop
                    87
This has been
         incredible

• We’ve just scratched the surface
• Keep your code clean - or you’ll regret it




                                               Advanced Django Form Usage
                                                    @pydanny / @maraujop
                     87
This has been
         incredible

• We’ve just scratched the surface
• Keep your code clean - or you’ll regret it
• Miguel is awesome




                                               Advanced Django Form Usage
                                                    @pydanny / @maraujop
                     87
Advanced Django Form Usage
                     @pydanny / @maraujop
This has been
 incredible




                                      88
This has been
        incredible
• We want to see this and more in the
  formal Django docs




                                        Advanced Django Form Usage
                                             @pydanny / @maraujop
                    88
This has been
        incredible
• We want to see this and more in the
  formal Django docs
• We’ve scratched our own itch




                                        Advanced Django Form Usage
                                             @pydanny / @maraujop
                    88
This has been
         incredible
• We want to see this and more in the
  formal Django docs
• We’ve scratched our own itch
• There are hidden things that need to see
  the light of day




                                             Advanced Django Form Usage
                                                  @pydanny / @maraujop
                     88
This has been
         incredible
• We want to see this and more in the
  formal Django docs
• We’ve scratched our own itch
• There are hidden things that need to see
  the light of day




                                             Advanced Django Form Usage
• Danny holds the DjangoCon talks record



                                                  @pydanny / @maraujop
                     88
Fin
Advanced Django Form Usage
                           @pydanny / @maraujop
             Daniel Greenfeld & Miguel Araujo
Questions?


                                                90

Advanced Django Forms Usage

  • 1.
    Advanced Django Form Usage by Daniel Greenfeld and Miguel Araujo
  • 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 Usage http://www.flickr.com/photos/pydanny/4442245488/ Fiancé, Audrey Roy (audreyr) @pydanny / @maraujop 2
  • 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.
    Advanced Django FormUsage @pydanny / @maraujop Tons of technical content 4
  • 5.
    Tons of technicalcontent • We probably won’t have time for questions Advanced Django Form Usage @pydanny / @maraujop 4
  • 6.
    Tons of technicalcontent • We probably won’t have time for questions • Slides will be posted right after the talk Advanced Django Form Usage @pydanny / @maraujop 4
  • 7.
    Tons of technicalcontent • 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.
    Tons of technicalcontent • 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.
    Tons of technicalcontent • 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.
    Tons of technicalcontent • 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.
    Tons of technicalcontent • 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.
    Advanced Django FormUsage @pydanny / @maraujop Good Form Patterns Fundamentals of 5
  • 13.
    Fundamentals of Good Form Patterns • Zen of Python still applies Advanced Django Form Usage @pydanny / @maraujop 5
  • 14.
    Fundamentals of Good Form Patterns • Zen of Python still applies • import this Advanced Django Form Usage @pydanny / @maraujop 5
  • 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.
    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.
    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.
    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.
    Advanced Django FormUsage @pydanny / @maraujop Calling forms the easy way 9
  • 20.
    Calling forms theeasy way • Smaller, cleaner code makes our lives better Advanced Django Form Usage @pydanny / @maraujop 9
  • 21.
    Calling forms theeasy way • Smaller, cleaner code makes our lives better • Line 5 of the Zen of Python Advanced Django Form Usage @pydanny / @maraujop 9
  • 22.
    Calling forms theeasy 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.
    Calling forms theeasy 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.
    Calling forms theeasy 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.
  • 26.
    A Basic DjangoForm class MyForm(forms.Form): name = forms.CharField(_('Name'), required=True) Advanced Django Form Usage @pydanny / @maraujop 11
  • 27.
    Standard views.py def 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.
    Standard views.py def 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.
    Standard views.py def 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.
    Standard views.py def 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.
    Standard views.py def 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.
    Standard views.py def 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.
    Easy views.py def 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 Custom business goes here 13
  • 34.
    Single form Easy views.py def 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 Custom business goes here 13
  • 35.
    Single form Easy views.py def 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 / @maraujop Custom business then instantiated with None goes here 13
  • 36.
    Easy views.py def 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.
    Easy views.py def 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.
    Easy views.py def 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.
    Easy views.py def 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.
    Easy views.py def 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.
  • 42.
    Easy views.py +files def 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.
    Easy views.py +files def 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.
    Easy views.py +files def 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.
    Easy views.py +files def 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.
    Easy views.py +files def 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.
  • 48.
    Advanced Django FormUsage @pydanny / @maraujop pydanny made up statistics 18
  • 49.
    pydanny made up statistics • 91% of all Django projects use ModelForms Advanced Django Form Usage @pydanny / @maraujop 18
  • 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.
    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.
    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.
    A Basic ModelForm classMyModelForm(forms.Form): class Meta: model = MyModel fields = ['name'] Advanced Django Form Usage @pydanny / @maraujop 19
  • 54.
    Classic views.py forModelForm def my_model_edit(request, slug=slug, template_name='myapp/my_model_form.html'): # I wouldn't call the variable model, because it's an instance of a model, it's 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.
    Classic views.py forModelForm def my_model_edit(request, slug=slug, template_name='myapp/my_model_form.html'): # I wouldn't call the variable model, because it's an instance of a model, it's 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.
    Classic views.py forModelForm def my_model_edit(request, slug=slug, template_name='myapp/my_model_form.html'): # I wouldn't call the variable model, because it's an instance of a model, it's 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.
    Classic views.py forModelForm def my_model_edit(request, slug=slug, template_name='myapp/my_model_form.html'): # I wouldn't call the variable model, because it's an instance of a model, it's 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.
    Classic views.py forModelForm def my_model_edit(request, slug=slug, template_name='myapp/my_model_form.html'): # I wouldn't call the variable model, because it's an instance of a model, it's 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.
    Classic views.py forModelForm def my_model_edit(request, slug=slug, template_name='myapp/my_model_form.html'): # I wouldn't call the variable model, because it's an instance of a model, it's 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.
    Classic views.py forModelForm def my_model_edit(request, slug=slug, template_name='myapp/my_model_form.html'): # I wouldn't call the variable model, because it's an instance of a model, it's 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.
    Classic views.py forModelForm def my_model_edit(request, slug=slug, template_name='myapp/my_model_form.html'): # I wouldn't call the variable model, because it's an instance of a model, it's 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.
    Classic views.py forModelForm def my_model_edit(request, slug=slug, template_name='myapp/my_model_form.html'): # I wouldn't call the variable model, because it's an instance of a model, it's 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.
    Classic views.py forModelForm def my_model_edit(request, slug=slug, template_name='myapp/my_model_form.html'): # I wouldn't call the variable model, because it's an instance of a model, it's 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.
    Classic views.py forModelForm def my_model_edit(request, slug=slug, template_name='myapp/my_model_form.html'): # I wouldn't call the variable model, because it's an instance of a model, it's 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.
    easy views.py +ModelForm def 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.
    easy views.py +ModelForm def 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.
    easy views.py +ModelForm def 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 Custom business @pydanny / @maraujop goes here 22
  • 68.
    easy views.py +ModelForm def 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 Usage Custom business @pydanny / @maraujop goes here So this will fail validation! 22
  • 69.
    easy views.py +ModelForm def 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.
    easy views.py +ModelForm def 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.
    easy views.py +ModelForm def 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.
    easy views.py +ModelForm def 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.
  • 74.
    add views.py +ModelForm def 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.
    add views.py +ModelForm def 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.
    I can makeit 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.
    How do youtest this stuff?
  • 78.
    Advanced Django FormUsage @pydanny / @maraujop Please don’t manually test your forms 28
  • 79.
    Please don’t manually test your forms • This isn’t a testing talk but... Advanced Django Form Usage @pydanny / @maraujop 28
  • 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.
    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.
    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.
    Django unit tests! deftest_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.
    Django unit tests! deftest_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.
    Django unit tests! deftest_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.
    Django unit tests! deftest_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.
  • 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.
    A basic Djangomodels.py 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) Advanced Django Form Usage @pydanny / @maraujop 32
  • 90.
    Classic forms.py overload classMyModelTooMuchTypingForm(forms.ModelForm): """ I've 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.
    Classic forms.py overload classMyModelTooMuchTypingForm(forms.ModelForm): """ I've 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.
    Classic forms.py overload classMyModelTooMuchTypingForm(forms.ModelForm): """ I've 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.
    Classic forms.py overload classMyModelTooMuchTypingForm(forms.ModelForm): """ I've 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.
    Better forms.py overload classMyModelForm(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.
    Try it withinheritance! 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.
  • 97.
    Dynamically adding fields to a form def my_view(request, template_name='myapp/my_model_form.html'): form = MyModelForm(request.POST or None) # Let's 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(): # Let's get user's 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.
    Dynamically adding fields to a form def my_view(request, template_name='myapp/my_model_form.html'): Form dictionary form = MyModelForm(request.POST or None) of fields # Let's 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(): # Let's get user's 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.
    Dynamically adding fields to a form def my_view(request, template_name='myapp/my_model_form.html'): Form dictionary form = MyModelForm(request.POST or None) of fields # Let's 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(): # Let's get user's 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.
    Can’t we justpass in a list of fields into a form from a view?
  • 101.
    Constructor overrides forms.py classMyModelForm(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.
    Constructor overrides views.py def my_view(request, template_name='myapp/my_model_form.html'): form = MyModelForm(request.POST or None, extra=["What's your pet's 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.
    I need aformset
  • 104.
    formsets.py class ItemFormSet(BaseFormSet): def __init__(self, numberItems, *args, **kwargs): super(ItemFormSet, self).__init__(*args, **kwargs) self.numberItems = numberItems def clean(self): # Don't 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(u'mod_wsgi is the way to go!') 42
  • 105.
    formsets.py class ItemFormSet(BaseFormSet): def __init__(self, numberItems, *args, **kwargs): super(ItemFormSet, self).__init__(*args, **kwargs) self.numberItems = numberItems def clean(self): Forms must have # Don't 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(u'mod_wsgi is the way to go!') 42
  • 106.
    views.py from django.forms.models importformset_factory def 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.
  • 108.
    Things I wantpython to describe in forms • Different fieldsets within the same form • Various buttons • submit • reset Advanced Django Form Usage @pydanny / @maraujop 45
  • 109.
    Advanced Django FormUsage @pydanny / @maraujop django-uni-form 46
  • 110.
    Programmatic layouts class 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.
    Programmatic layouts from uni_form.helpers import FormHelper, Submit, Reset from uni_form.helpers import Fieldset, ButtonHolder, Layout class 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.
    Programmatic layouts from uni_form.helpers import FormHelper, Submit, Reset from uni_form.helpers import Fieldset, ButtonHolder, Layout class 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.
    Programmatic layouts from uni_form.helpers import FormHelper, Submit, Reset from uni_form.helpers import Fieldset, ButtonHolder, Layout class 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.
    Programmatic layouts from uni_form.helpers import FormHelper, Submit, Reset from uni_form.helpers import Fieldset, ButtonHolder, Layout class 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.
    Programmatic layouts from uni_form.helpers import FormHelper, Submit, Reset from uni_form.helpers import Fieldset, ButtonHolder, Layout class 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.
    Programmatic layouts from uni_form.helpers import FormHelper, Submit, Reset from uni_form.helpers import Fieldset, ButtonHolder, Layout class 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.
    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.
    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 Usage I 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.
    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.
  • 121.
    django-floppyforms • by BrunoRenie @brutasse • HTML5 Widgets • Fully customizable templates • Plays nice with django-uni-form Advanced Django Form Usage @pydanny / @maraujop 52
  • 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.
    Customizable widgets import floppyformsas forms class 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.
    Can you combine django-uni-formand django-floppyforms?
  • 125.
  • 126.
    They get alongwell class 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.
    Forms refactor Django 1.4
  • 128.
    Advanced Django FormUsage @pydanny / @maraujop What we are getting in 1.4 59
  • 129.
    What we aregetting in 1.4 • Form rendering will use templates instead of HTML in Python Advanced Django Form Usage @pydanny / @maraujop 59
  • 130.
    What we aregetting in 1.4 • Form rendering will use templates instead of HTML in Python • Template based widgets Advanced Django Form Usage @pydanny / @maraujop 59
  • 131.
    What we aregetting 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.
    Advanced Django FormUsage @pydanny / @maraujop forms refactor and django-uni-form 60
  • 133.
    forms refactor and django-uni-form • forms refactor layout “lives” in templates Advanced Django Form Usage @pydanny / @maraujop 60
  • 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.
    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.
    Give me away to create custom form fields
  • 137.
    The docs oncustom fields “Its only requirements are that it implement a clean() method and that its __init__() method accept the core arguments mentioned above (required, label, initial, widget, help_text).” Advanced Django Form Usage @pydanny / @maraujop 62
  • 138.
    The docs oncustom fields “Its only requirements are that it implement a clean() method and that its __init__() method accept the core arguments mentioned above (required, label, initial, widget, help_text).” Advanced Django Form Usage The docs seem to be wrong @pydanny / @maraujop 62
  • 139.
    How it reallyworks You 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.
    How the heckthen do I actually add a custom form field?!?
  • 141.
    Advanced Django FormUsage @pydanny / @maraujop validation work? How does form 65
  • 142.
    fields.py class AMarkField(forms.Field): widget = TextInput default_error_messages = { 'not_an_a': _(u'you 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.
    Not DRY class 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(_(u'Only input A here!')) return mark_value class 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(_(u'Only input A here!')) return mark_value 67
  • 144.
    I want acustom field that validates JSON
  • 145.
    The JSON field classJSONField(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.
    I want acustom widget that handles multiple widgets
  • 147.
    widgets.py from django.forms.extras.widgets importMultiWidget class 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.
    widgets.py from django.forms.extras.widgets importMultiWidget class 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.
    widgets.py from django.forms.extras.widgets importMultiWidget class 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.
    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) Advanced Django Form Usage @pydanny / @maraujop 72
  • 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 Usage isinstance("921 SW Sixth Avenue, Portland", str) @pydanny / @maraujop 72
  • 152.
    Give me aMultiValue, MultiWidget field
  • 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.
    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.
    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.
    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.
    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.
    Validators with Multi-Field classAlternativeAddressField(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.
    How about custom widget output?
  • 160.
    Advanced Django FormUsage @pydanny / @maraujop Custom Widget Output So Easy! 77
  • 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.
    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.
    Can’t you makeit easier to understand?
  • 164.
    Problems with Custom Widget Output • Overring render means indepth knowledge • Hard to do custom output with built-in widgets • You can’t call super.render and customize easily • Templates? Open Ticket #15667 Advanced Django Form Usage @pydanny / @maraujop 79
  • 165.
  • 166.
    Problems with Custom Widget Output • Oldest ticket in Django is related to forms (#23) • ComboField is broken • Validators are thought to validate simple values Advanced Django Form Usage because of the way run_validators is coded @pydanny / @maraujop 81
  • 167.
    HTML5 tickets • Ticket#16304 • Ticket #16630 • Just use django-floppyforms Advanced Django Form Usage @pydanny / @maraujop 82
  • 168.
    Validators • Imagine you have an input that get a list of emails seperated by spaces • Your widget returns: [“a@example.com”, “b@example.com”] • You want to run validators on all of them • Advanced Django Form Usage There is an EmailValidator @pydanny / @maraujop 83
  • 169.
    MultiValidator class MultiValidator(object): def __init__(self, validators): self.validators = validators def __call__(self, data_list): errors = [] for value in data_list: for validator in self.validators: try: validator(value) except ValidationError, e: Advanced Django Form Usage if hasattr(e, 'code'): errors.append("FiXED MESSAGE") @pydanny / @maraujop raise ValidationError(errors) 84
  • 170.
    Amazing stuff • Ticket#27 reported by Adrian Holovaty • Single form field for multiple database fields Advanced Django Form Usage @pydanny / @maraujop 85
  • 171.
  • 172.
    Advanced Django FormUsage @pydanny / @maraujop This has been incredible 87
  • 173.
    This has been incredible • We’ve just scratched the surface Advanced Django Form Usage @pydanny / @maraujop 87
  • 174.
    This has been incredible • We’ve just scratched the surface • Keep your code clean - or you’ll regret it Advanced Django Form Usage @pydanny / @maraujop 87
  • 175.
    This has been incredible • We’ve just scratched the surface • Keep your code clean - or you’ll regret it • Miguel is awesome Advanced Django Form Usage @pydanny / @maraujop 87
  • 176.
    Advanced Django FormUsage @pydanny / @maraujop This has been incredible 88
  • 177.
    This has been incredible • We want to see this and more in the formal Django docs Advanced Django Form Usage @pydanny / @maraujop 88
  • 178.
    This has been incredible • We want to see this and more in the formal Django docs • We’ve scratched our own itch Advanced Django Form Usage @pydanny / @maraujop 88
  • 179.
    This has been incredible • We want to see this and more in the formal Django docs • We’ve scratched our own itch • There are hidden things that need to see the light of day Advanced Django Form Usage @pydanny / @maraujop 88
  • 180.
    This has been incredible • We want to see this and more in the formal Django docs • We’ve scratched our own itch • There are hidden things that need to see the light of day Advanced Django Form Usage • Danny holds the DjangoCon talks record @pydanny / @maraujop 88
  • 181.
  • 182.
    Advanced Django FormUsage @pydanny / @maraujop Daniel Greenfeld & Miguel Araujo Questions? 90

Editor's Notes

  • #2 D\n
  • #3 M\n
  • #4 D\n
  • #5 D\n
  • #6 D\n
  • #7 D\n
  • #8 D\n
  • #9 D\n
  • #10 D\n
  • #11 D\n
  • #12 D\n
  • #13 D\n
  • #14 D\n
  • #15 D\n
  • #16 D\nIf you can reduce your line count from 10 to 2 and make it more obvious, that is good\nThis is why so many people prefer things like Python over Java\n
  • #17 D\nYou want to have shallow code with as little if statements as possible\nOtherwise you create lots of hard to test edge cases\n
  • #18 D\n
  • #19 D\n
  • #20 D\n
  • #21 D\n
  • #22 D\n
  • #23 M\nShow me the code!\n
  • #24 D\n
  • #25 D\nassuming we have from forms import MyForm\nIn real life this can get big, complex, and nasty\n
  • #26 D\nassuming we have from forms import MyForm\nIn real life this can get big, complex, and nasty\n
  • #27 D\nassuming we have from forms import MyForm\nIn real life this can get big, complex, and nasty\n
  • #28 D\nassuming we have from forms import MyForm\nIn real life this can get big, complex, and nasty\n
  • #29 D\nassuming we have from forms import MyForm\nIn real life this can get big, complex, and nasty\n
  • #30 D\n
  • #31 D\n
  • #32 D\n
  • #33 D\n
  • #34 D\n
  • #35 D\n
  • #36 D \nMy fiancee, Audrey Roy, said my example needs to handle file uploads. How do we do that?\n
  • #37 M\n
  • #38 M\n
  • #39 M\n
  • #40 M\n
  • #41 M\nHey Danny, don&amp;#x2019;t 91% of real Django projects use ModelForms?\n
  • #42 D\n
  • #43 D\n
  • #44 D\n
  • #45 D\n
  • #46 D\n
  • #47 D\n
  • #48 D\n
  • #49 D\n
  • #50 D\n
  • #51 D\n
  • #52 D\n
  • #53 D\n
  • #54 D\n
  • #55 D\n
  • #56 D\n
  • #57 D\n
  • #58 D\n
  • #59 D\n
  • #60 D\n
  • #61 D\n
  • #62 D\nHey Miguel, how do I make this work when adding data?\n
  • #63 M\n
  • #64 M\n
  • #65 M\n
  • #66 D\n
  • #67 D\n
  • #68 D\n
  • #69 D\n
  • #70 D\n
  • #71 D\n
  • #72 D\n
  • #73 M\nAny other tricks?\n
  • #74 D\n
  • #75 D\n
  • #76 D\n
  • #77 D\n
  • #78 D\n
  • #79 D Fields are in a dictionary! We should remember this fact!\n
  • #80 D\n
  • #81 D\n\n
  • #82 M\nDidn&amp;#x2019;t you just show that fields are stored in a dict in the form? Why not just add another field that way?\n
  • #83 M\nDidn&amp;#x2019;t you just show that fields are stored in a dict in the form? Why not just add another field that way?\n
  • #84 D\n\n
  • #85 M - Let&apos;s override form constructor to wrap the logic of adding fields on the go\n\n
  • #86 M - Let&apos;s override form constructor to wrap the logic of adding fields on the go\n\n
  • #87 D\n\n
  • #88 M\n
  • #89 M\n
  • #90 M\n\n
  • #91 D\n
  • #92 D\n
  • #93 D\n
  • #94 D\n
  • #95 D\n
  • #96 D\n
  • #97 D\n
  • #98 D\n
  • #99 D\n
  • #100 D\n
  • #101 D\n
  • #102 D\n\n
  • #103 M\n
  • #104 M\n
  • #105 M\n
  • #106 D\n\n
  • #107 M\n\n
  • #108 M\nBecause both DUF and DFF do things in a clean, non-magical way - they just work together. \nNamespaces and clean code beat smart hackery any day.\n
  • #109 M\nTell me what is coming down the road\n
  • #110 D\n
  • #111 D\n
  • #112 D\n
  • #113 D\nIt&amp;#x2019;s okay that there are different objects\n
  • #114 D\nIt&amp;#x2019;s okay that there are different objects\n
  • #115 D\nIt&amp;#x2019;s okay that there are different objects\n
  • #116 M\n\n
  • #117 D\n\n
  • #118 D\n
  • #119 D\n\n
  • #120 M\n
  • #121 M Of course you could do the same with a regular CharField using form validationclean_&lt;fieldname&gt; method, but...\n
  • #122 M - Duplicated code. So do a custom form field instead of duplicating code everywhere\nYes, you could solve this with form inheritance, but what if the inheritance chain gets too long?\n
  • #123 M\n\n
  • #124 D This field returns an already JSON validated Python list when accessing cleaned_data\n
  • #125 D\n\n
  • #126 M We are using MultiWidget.\n
  • #127 M We are using MultiWidget.\n
  • #128 M The difference is that you can get fancier with validation on the list than the string\n
  • #129 D\n\n
  • #130 M You need to implement compress\nIt would be nice that fields were also an attribute in the parent class.\n
  • #131 M You need to implement compress\nIt would be nice that fields were also an attribute in the parent class.\n
  • #132 M You need to implement compress\nIt would be nice that fields were also an attribute in the parent class.\n
  • #133 M You need to implement compress\nIt would be nice that fields were also an attribute in the parent class.\n
  • #134 M\n
  • #135 M\n\n
  • #136 D - You are going to love how easy this is! Ready?\n
  • #137 D - You are going to love how easy this is! Ready?\n
  • #138 D\n\n
  • #139 M\n
  • #140 M\n\n
  • #141 M\n
  • #142 M\n
  • #143 M\n
  • #144 M * You can raise a ValidationError with a code. * You can raise a ValidationError with a list of messages. * You could raise a ValidationError on the first email that fails, but error will be less helpful. * Hard to customize, no error_messages, Do this in an upper layer, rewrite run_validators\n
  • #145 M\n
  • #146 D\n
  • #147 D\n
  • #148 D\n
  • #149 D\n
  • #150 M\n
  • #151 M\n
  • #152 M\n
  • #153 M\n
  • #154 D\n
  • #155 \n