SlideShare ist ein Scribd-Unternehmen logo
1 von 182
Downloaden Sie, um offline zu lesen
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
Advanced Django forms usage
Advanced Django forms usage
Advanced Django forms usage
Advanced Django forms usage
Advanced Django forms usage
Advanced Django forms usage
Advanced Django forms usage
Advanced Django forms usage

Weitere ähnliche Inhalte

Ähnlich wie Advanced Django forms usage

Django Package Thunderdome by Audrey Roy & Daniel Greenfeld
Django Package Thunderdome by Audrey Roy & Daniel GreenfeldDjango Package Thunderdome by Audrey Roy & Daniel Greenfeld
Django Package Thunderdome by Audrey Roy & Daniel GreenfeldAudrey Roy
 
Realtime Apps with Django
Realtime Apps with DjangoRealtime Apps with Django
Realtime Apps with DjangoRenyi Khor
 
5 loaded guns Go hapily aims at your feet
5 loaded guns Go hapily aims at your feet5 loaded guns Go hapily aims at your feet
5 loaded guns Go hapily aims at your feetMárk Sági-Kazár
 
Developing With Django
Developing With DjangoDeveloping With Django
Developing With DjangoDaniel Ryan
 
Drupal 8: Huge wins, a Bigger Community, and why you (and I) will Love it
Drupal 8: Huge wins, a Bigger Community, and why you (and I) will Love itDrupal 8: Huge wins, a Bigger Community, and why you (and I) will Love it
Drupal 8: Huge wins, a Bigger Community, and why you (and I) will Love itRyan Weaver
 
We Want YOU! Contributing to the Django Community
We Want YOU! Contributing to the Django CommunityWe Want YOU! Contributing to the Django Community
We Want YOU! Contributing to the Django CommunityMarcel Chastain
 
Perl5 meta programming
Perl5 meta programmingPerl5 meta programming
Perl5 meta programmingkarupanerura
 
Intro to Python for C# Developers
Intro to Python for C# DevelopersIntro to Python for C# Developers
Intro to Python for C# DevelopersSarah Dutkiewicz
 
Pharo VM Performance
Pharo VM PerformancePharo VM Performance
Pharo VM PerformancePharo
 
Why you should use Django in your next project.
Why you should use Django in your next project.Why you should use Django in your next project.
Why you should use Django in your next project.Eyad Toma
 

Ähnlich wie Advanced Django forms usage (11)

Django Package Thunderdome by Audrey Roy & Daniel Greenfeld
Django Package Thunderdome by Audrey Roy & Daniel GreenfeldDjango Package Thunderdome by Audrey Roy & Daniel Greenfeld
Django Package Thunderdome by Audrey Roy & Daniel Greenfeld
 
Realtime Apps with Django
Realtime Apps with DjangoRealtime Apps with Django
Realtime Apps with Django
 
5 loaded guns Go hapily aims at your feet
5 loaded guns Go hapily aims at your feet5 loaded guns Go hapily aims at your feet
5 loaded guns Go hapily aims at your feet
 
Developing With Django
Developing With DjangoDeveloping With Django
Developing With Django
 
Drupal 8: Huge wins, a Bigger Community, and why you (and I) will Love it
Drupal 8: Huge wins, a Bigger Community, and why you (and I) will Love itDrupal 8: Huge wins, a Bigger Community, and why you (and I) will Love it
Drupal 8: Huge wins, a Bigger Community, and why you (and I) will Love it
 
We Want YOU! Contributing to the Django Community
We Want YOU! Contributing to the Django CommunityWe Want YOU! Contributing to the Django Community
We Want YOU! Contributing to the Django Community
 
Perl5 meta programming
Perl5 meta programmingPerl5 meta programming
Perl5 meta programming
 
Intro to Python for C# Developers
Intro to Python for C# DevelopersIntro to Python for C# Developers
Intro to Python for C# Developers
 
Pharo VM Performance
Pharo VM PerformancePharo VM Performance
Pharo VM Performance
 
Why you should use Django in your next project.
Why you should use Django in your next project.Why you should use Django in your next project.
Why you should use Django in your next project.
 
Introduction to Padrino
Introduction to PadrinoIntroduction to Padrino
Introduction to Padrino
 

Kürzlich hochgeladen

UiPath Studio Web workshop series - Day 6
UiPath Studio Web workshop series - Day 6UiPath Studio Web workshop series - Day 6
UiPath Studio Web workshop series - Day 6DianaGray10
 
Introduction to Matsuo Laboratory (ENG).pptx
Introduction to Matsuo Laboratory (ENG).pptxIntroduction to Matsuo Laboratory (ENG).pptx
Introduction to Matsuo Laboratory (ENG).pptxMatsuo Lab
 
UiPath Community: AI for UiPath Automation Developers
UiPath Community: AI for UiPath Automation DevelopersUiPath Community: AI for UiPath Automation Developers
UiPath Community: AI for UiPath Automation DevelopersUiPathCommunity
 
Empowering Africa's Next Generation: The AI Leadership Blueprint
Empowering Africa's Next Generation: The AI Leadership BlueprintEmpowering Africa's Next Generation: The AI Leadership Blueprint
Empowering Africa's Next Generation: The AI Leadership BlueprintMahmoud Rabie
 
Artificial Intelligence & SEO Trends for 2024
Artificial Intelligence & SEO Trends for 2024Artificial Intelligence & SEO Trends for 2024
Artificial Intelligence & SEO Trends for 2024D Cloud Solutions
 
Meet the new FSP 3000 M-Flex800™
Meet the new FSP 3000 M-Flex800™Meet the new FSP 3000 M-Flex800™
Meet the new FSP 3000 M-Flex800™Adtran
 
Nanopower In Semiconductor Industry.pdf
Nanopower  In Semiconductor Industry.pdfNanopower  In Semiconductor Industry.pdf
Nanopower In Semiconductor Industry.pdfPedro Manuel
 
Designing A Time bound resource download URL
Designing A Time bound resource download URLDesigning A Time bound resource download URL
Designing A Time bound resource download URLRuncy Oommen
 
Building Your Own AI Instance (TBLC AI )
Building Your Own AI Instance (TBLC AI )Building Your Own AI Instance (TBLC AI )
Building Your Own AI Instance (TBLC AI )Brian Pichman
 
UiPath Solutions Management Preview - Northern CA Chapter - March 22.pdf
UiPath Solutions Management Preview - Northern CA Chapter - March 22.pdfUiPath Solutions Management Preview - Northern CA Chapter - March 22.pdf
UiPath Solutions Management Preview - Northern CA Chapter - March 22.pdfDianaGray10
 
Basic Building Blocks of Internet of Things.
Basic Building Blocks of Internet of Things.Basic Building Blocks of Internet of Things.
Basic Building Blocks of Internet of Things.YounusS2
 
UWB Technology for Enhanced Indoor and Outdoor Positioning in Physiological M...
UWB Technology for Enhanced Indoor and Outdoor Positioning in Physiological M...UWB Technology for Enhanced Indoor and Outdoor Positioning in Physiological M...
UWB Technology for Enhanced Indoor and Outdoor Positioning in Physiological M...UbiTrack UK
 
Connector Corner: Extending LLM automation use cases with UiPath GenAI connec...
Connector Corner: Extending LLM automation use cases with UiPath GenAI connec...Connector Corner: Extending LLM automation use cases with UiPath GenAI connec...
Connector Corner: Extending LLM automation use cases with UiPath GenAI connec...DianaGray10
 
VoIP Service and Marketing using Odoo and Asterisk PBX
VoIP Service and Marketing using Odoo and Asterisk PBXVoIP Service and Marketing using Odoo and Asterisk PBX
VoIP Service and Marketing using Odoo and Asterisk PBXTarek Kalaji
 
Apres-Cyber - The Data Dilemma: Bridging Offensive Operations and Machine Lea...
Apres-Cyber - The Data Dilemma: Bridging Offensive Operations and Machine Lea...Apres-Cyber - The Data Dilemma: Bridging Offensive Operations and Machine Lea...
Apres-Cyber - The Data Dilemma: Bridging Offensive Operations and Machine Lea...Will Schroeder
 
How Accurate are Carbon Emissions Projections?
How Accurate are Carbon Emissions Projections?How Accurate are Carbon Emissions Projections?
How Accurate are Carbon Emissions Projections?IES VE
 
NIST Cybersecurity Framework (CSF) 2.0 Workshop
NIST Cybersecurity Framework (CSF) 2.0 WorkshopNIST Cybersecurity Framework (CSF) 2.0 Workshop
NIST Cybersecurity Framework (CSF) 2.0 WorkshopBachir Benyammi
 
activity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdf
activity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdf
activity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdfJamie (Taka) Wang
 

Kürzlich hochgeladen (20)

UiPath Studio Web workshop series - Day 6
UiPath Studio Web workshop series - Day 6UiPath Studio Web workshop series - Day 6
UiPath Studio Web workshop series - Day 6
 
Introduction to Matsuo Laboratory (ENG).pptx
Introduction to Matsuo Laboratory (ENG).pptxIntroduction to Matsuo Laboratory (ENG).pptx
Introduction to Matsuo Laboratory (ENG).pptx
 
UiPath Community: AI for UiPath Automation Developers
UiPath Community: AI for UiPath Automation DevelopersUiPath Community: AI for UiPath Automation Developers
UiPath Community: AI for UiPath Automation Developers
 
Empowering Africa's Next Generation: The AI Leadership Blueprint
Empowering Africa's Next Generation: The AI Leadership BlueprintEmpowering Africa's Next Generation: The AI Leadership Blueprint
Empowering Africa's Next Generation: The AI Leadership Blueprint
 
Artificial Intelligence & SEO Trends for 2024
Artificial Intelligence & SEO Trends for 2024Artificial Intelligence & SEO Trends for 2024
Artificial Intelligence & SEO Trends for 2024
 
Meet the new FSP 3000 M-Flex800™
Meet the new FSP 3000 M-Flex800™Meet the new FSP 3000 M-Flex800™
Meet the new FSP 3000 M-Flex800™
 
Nanopower In Semiconductor Industry.pdf
Nanopower  In Semiconductor Industry.pdfNanopower  In Semiconductor Industry.pdf
Nanopower In Semiconductor Industry.pdf
 
Designing A Time bound resource download URL
Designing A Time bound resource download URLDesigning A Time bound resource download URL
Designing A Time bound resource download URL
 
Building Your Own AI Instance (TBLC AI )
Building Your Own AI Instance (TBLC AI )Building Your Own AI Instance (TBLC AI )
Building Your Own AI Instance (TBLC AI )
 
UiPath Solutions Management Preview - Northern CA Chapter - March 22.pdf
UiPath Solutions Management Preview - Northern CA Chapter - March 22.pdfUiPath Solutions Management Preview - Northern CA Chapter - March 22.pdf
UiPath Solutions Management Preview - Northern CA Chapter - March 22.pdf
 
Basic Building Blocks of Internet of Things.
Basic Building Blocks of Internet of Things.Basic Building Blocks of Internet of Things.
Basic Building Blocks of Internet of Things.
 
UWB Technology for Enhanced Indoor and Outdoor Positioning in Physiological M...
UWB Technology for Enhanced Indoor and Outdoor Positioning in Physiological M...UWB Technology for Enhanced Indoor and Outdoor Positioning in Physiological M...
UWB Technology for Enhanced Indoor and Outdoor Positioning in Physiological M...
 
Connector Corner: Extending LLM automation use cases with UiPath GenAI connec...
Connector Corner: Extending LLM automation use cases with UiPath GenAI connec...Connector Corner: Extending LLM automation use cases with UiPath GenAI connec...
Connector Corner: Extending LLM automation use cases with UiPath GenAI connec...
 
20150722 - AGV
20150722 - AGV20150722 - AGV
20150722 - AGV
 
VoIP Service and Marketing using Odoo and Asterisk PBX
VoIP Service and Marketing using Odoo and Asterisk PBXVoIP Service and Marketing using Odoo and Asterisk PBX
VoIP Service and Marketing using Odoo and Asterisk PBX
 
20230104 - machine vision
20230104 - machine vision20230104 - machine vision
20230104 - machine vision
 
Apres-Cyber - The Data Dilemma: Bridging Offensive Operations and Machine Lea...
Apres-Cyber - The Data Dilemma: Bridging Offensive Operations and Machine Lea...Apres-Cyber - The Data Dilemma: Bridging Offensive Operations and Machine Lea...
Apres-Cyber - The Data Dilemma: Bridging Offensive Operations and Machine Lea...
 
How Accurate are Carbon Emissions Projections?
How Accurate are Carbon Emissions Projections?How Accurate are Carbon Emissions Projections?
How Accurate are Carbon Emissions Projections?
 
NIST Cybersecurity Framework (CSF) 2.0 Workshop
NIST Cybersecurity Framework (CSF) 2.0 WorkshopNIST Cybersecurity Framework (CSF) 2.0 Workshop
NIST Cybersecurity Framework (CSF) 2.0 Workshop
 
activity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdf
activity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdf
activity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdf
 

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 Form Usage @pydanny / @maraujop Tons of technical content 4
  • 5. Tons of technical content • We probably won’t have time for questions Advanced Django Form Usage @pydanny / @maraujop 4
  • 6. Tons of technical content • We probably won’t have time for questions • Slides will be posted right after the talk Advanced Django Form Usage @pydanny / @maraujop 4
  • 7. Tons of technical content • We probably won’t have time for questions • Slides will be posted right after the talk • Too much content in the abstract Advanced Django Form Usage @pydanny / @maraujop 4
  • 8. Tons of technical content • We probably won’t have time for questions • Slides will be posted right after the talk • Too much content in the abstract • Special thanks to: Advanced Django Form Usage @pydanny / @maraujop 4
  • 9. Tons of technical content • We probably won’t have time for questions • Slides will be posted right after the talk • Too much content in the abstract • Special thanks to: • Brian Rosner Advanced Django Form Usage @pydanny / @maraujop 4
  • 10. Tons of technical content • We probably won’t have time for questions • Slides will be posted right after the talk • Too much content in the abstract • Special thanks to: • Brian Rosner Advanced Django Form Usage • James Tauber @pydanny / @maraujop 4
  • 11. Tons of technical content • We probably won’t have time for questions • Slides will be posted right after the talk • Too much content in the abstract • Special thanks to: • Brian Rosner Advanced Django Form Usage • James Tauber @pydanny / @maraujop • Frank Wiles 4
  • 12. Advanced Django Form Usage @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 Form Usage @pydanny / @maraujop Calling forms the easy way 9
  • 20. Calling forms the easy way • Smaller, cleaner code makes our lives better Advanced Django Form Usage @pydanny / @maraujop 9
  • 21. Calling forms the easy way • Smaller, cleaner code makes our lives better • Line 5 of the Zen of Python Advanced Django Form Usage @pydanny / @maraujop 9
  • 22. Calling forms the easy way • Smaller, cleaner code makes our lives better • Line 5 of the Zen of Python • Flat is better than nested. Advanced Django Form Usage @pydanny / @maraujop 9
  • 23. Calling forms the easy way • Smaller, cleaner code makes our lives better • Line 5 of the Zen of Python • Flat is better than nested. • Aim for minimal boilerplate Advanced Django Form Usage @pydanny / @maraujop 9
  • 24. 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
  • 26. A Basic Django Form 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
  • 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
  • 48. Advanced Django Form Usage @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 class MyModelForm(forms.Form): class Meta: model = MyModel fields = ['name'] Advanced Django Form Usage @pydanny / @maraujop 19
  • 54. 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
  • 55. 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
  • 56. 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
  • 57. 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
  • 58. 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
  • 59. 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
  • 60. 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
  • 61. 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
  • 62. 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
  • 63. 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
  • 64. 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
  • 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. What about new model instances?
  • 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 make it smaller! def my_model_tiny_add(request,template_name='myapp/my_model_form.html'): form = MyModelForm(request.POST or None) if form.is_valid(): form.save() Not setting defaults here return redirect('home') return render(request,template_name,{'form':form,'mymodel':mymodel}) Advanced Django Form Usage Good practice: Use model field defaults rather @pydanny / @maraujop than doing it in your views. 26
  • 77. How do you test this stuff?
  • 78. Advanced Django Form Usage @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! def test_add_package_view(self): url = reverse('my-url') response = self.client.get(url) self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, 'package/package_form.html') for c in Category.objects.all(): self.assertContains(response, c.title) count = Package.objects.count() response = self.client.post(url, { 'category': Category.objects.all()[0].pk, 'repo_url': 'http://github.com/django/django', Advanced Django Form Usage 'slug': 'test-slug', @pydanny / @maraujop 'title': 'TEST TITLE', }, follow=True) self.assertEqual(Package.objects.count(), count + 1) self.assertContains(response, "Django") 29
  • 84. Django unit tests! def test_add_package_view(self): url = reverse('my-url') response = self.client.get(url) POSTing a form self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, 'package/package_form.html') for c in Category.objects.all(): self.assertContains(response, c.title) count = Package.objects.count() response = self.client.post(url, { 'category': Category.objects.all()[0].pk, 'repo_url': 'http://github.com/django/django', Advanced Django Form Usage 'slug': 'test-slug', @pydanny / @maraujop 'title': 'TEST TITLE', }, follow=True) self.assertEqual(Package.objects.count(), count + 1) self.assertContains(response, "Django") 29
  • 85. Django unit tests! def test_add_package_view(self): url = reverse('my-url') response = self.client.get(url) POSTing a form self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, 'package/package_form.html') for c in Category.objects.all(): self.assertContains(response, c.title) count = Package.objects.count() follow=True response = self.client.post(url, { 'category': Category.objects.all()[0].pk, 'repo_url': 'http://github.com/django/django', Advanced Django Form Usage 'slug': 'test-slug', @pydanny / @maraujop 'title': 'TEST TITLE', }, follow=True) self.assertEqual(Package.objects.count(), count + 1) self.assertContains(response, "Django") 29
  • 86. 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
  • 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 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
  • 90. 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
  • 91. 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
  • 92. 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
  • 93. 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
  • 94. 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
  • 95. Try it with inheritance! class BaseEmailForm(forms.Form): email = forms.EmailField(_('Email')) confirm_email = forms.EmailField(_('Email 2')) class ContactForm(BaseEmailForm): message = forms.CharField(_('Message')) def __init__(self): super(ContactForm, self).__init__(*args, **kwargs) self.fields['confirm_email'].label = _('Confirm your email') self.fields['confirm_email'].description = _('We want to be absolutely certain we have your correct email address.') Advanced Django Form Usage @pydanny / @maraujop 35
  • 96. Add dynamic fields to a form
  • 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 just pass in a list of fields into a form from a view?
  • 101. 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
  • 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 a formset
  • 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 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
  • 108. Things I want python to describe in forms • Different fieldsets within the same form • Various buttons • submit • reset Advanced Django Form Usage @pydanny / @maraujop 45
  • 109. Advanced Django Form Usage @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
  • 121. django-floppyforms • by Bruno Renie @brutasse • HTML5 Widgets • Fully customizable templates • Plays nice with django-uni-form Advanced Django Form Usage @pydanny / @maraujop 52
  • 122. 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 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
  • 124. Can you combine django-uni-form and django-floppyforms?
  • 126. 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
  • 127. Forms refactor Django 1.4
  • 128. Advanced Django Form Usage @pydanny / @maraujop What we are getting in 1.4 59
  • 129. What we are getting in 1.4 • Form rendering will use templates instead of HTML in Python Advanced Django Form Usage @pydanny / @maraujop 59
  • 130. What we are getting in 1.4 • Form rendering will use templates instead of HTML in Python • Template based widgets Advanced Django Form Usage @pydanny / @maraujop 59
  • 131. What we are getting in 1.4 • Form rendering will use templates instead of HTML in Python • Template based widgets • Template based form layout Advanced Django Form Usage @pydanny / @maraujop 59
  • 132. Advanced Django Form Usage @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 a way to create custom form fields
  • 137. 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
  • 138. 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
  • 139. 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
  • 140. How the heck then do I actually add a custom form field?!?
  • 141. Advanced Django Form Usage @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 a custom field that validates JSON
  • 145. 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
  • 146. I want a custom widget that handles multiple widgets
  • 147. 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
  • 148. 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
  • 149. 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
  • 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 a MultiValue, 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 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
  • 159. How about custom widget output?
  • 160. Advanced Django Form Usage @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 make it 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
  • 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. Fin
  • 172. Advanced Django Form Usage @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

Hinweis der Redaktion

  1. D\n
  2. M\n
  3. D\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\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
  16. 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
  17. D\n
  18. D\n
  19. D\n
  20. D\n
  21. D\n
  22. M\nShow me the code!\n
  23. D\n
  24. D\nassuming we have from forms import MyForm\nIn real life this can get big, complex, and nasty\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\n
  30. D\n
  31. D\n
  32. D\n
  33. D\n
  34. D\n
  35. D \nMy fiancee, Audrey Roy, said my example needs to handle file uploads. How do we do that?\n
  36. M\n
  37. M\n
  38. M\n
  39. M\n
  40. M\nHey Danny, don&amp;#x2019;t 91% of real Django projects use ModelForms?\n
  41. D\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\nHey Miguel, how do I make this work when adding data?\n
  62. M\n
  63. M\n
  64. M\n
  65. D\n
  66. D\n
  67. D\n
  68. D\n
  69. D\n
  70. D\n
  71. D\n
  72. M\nAny other tricks?\n
  73. D\n
  74. D\n
  75. D\n
  76. D\n
  77. D\n
  78. D Fields are in a dictionary! We should remember this fact!\n
  79. D\n
  80. D\n\n
  81. 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
  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. D\n\n
  84. M - Let&apos;s override form constructor to wrap the logic of adding fields on the go\n\n
  85. M - Let&apos;s override form constructor to wrap the logic of adding fields on the go\n\n
  86. D\n\n
  87. M\n
  88. M\n
  89. M\n\n
  90. D\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\n
  102. M\n
  103. M\n
  104. M\n
  105. D\n\n
  106. M\n\n
  107. 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
  108. M\nTell me what is coming down the road\n
  109. D\n
  110. D\n
  111. D\n
  112. D\nIt&amp;#x2019;s okay that there are different objects\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. M\n\n
  116. D\n\n
  117. D\n
  118. D\n\n
  119. M\n
  120. M Of course you could do the same with a regular CharField using form validationclean_&lt;fieldname&gt; method, but...\n
  121. 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
  122. M\n\n
  123. D This field returns an already JSON validated Python list when accessing cleaned_data\n
  124. D\n\n
  125. M We are using MultiWidget.\n
  126. M We are using MultiWidget.\n
  127. M The difference is that you can get fancier with validation on the list than the string\n
  128. D\n\n
  129. M You need to implement compress\nIt would be nice that fields were also an attribute in the parent class.\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\n
  134. M\n\n
  135. D - You are going to love how easy this is! Ready?\n
  136. D - You are going to love how easy this is! Ready?\n
  137. D\n\n
  138. M\n
  139. M\n\n
  140. M\n
  141. M\n
  142. M\n
  143. 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
  144. M\n
  145. D\n
  146. D\n
  147. D\n
  148. D\n
  149. M\n
  150. M\n
  151. M\n
  152. M\n
  153. D\n
  154. \n