The document summarizes an upcoming talk by Daniel Greenfeld and Miguel Araujo on advanced Django form usage. It introduces the speakers and their backgrounds. It previews that the talk will cover fundamentals of good form patterns, calling forms in a cleaner way, handling file uploads simply, and using ModelForms. The talk aims to provide many technical tips to write forms with less code and complexity.
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
D\n
M\n
D\n
D\n
D\n
D\n
D\n
D\n
D\n
D\n
D\n
D\n
D\n
D\n
D\nIf you can reduce your line count from 10 to 2 and make it more obvious, that is good\nThis is why so many people prefer things like Python over Java\n
D\nYou want to have shallow code with as little if statements as possible\nOtherwise you create lots of hard to test edge cases\n
D\n
D\n
D\n
D\n
D\n
M\nShow me the code!\n
D\n
D\nassuming we have from forms import MyForm\nIn real life this can get big, complex, and nasty\n
D\nassuming we have from forms import MyForm\nIn real life this can get big, complex, and nasty\n
D\nassuming we have from forms import MyForm\nIn real life this can get big, complex, and nasty\n
D\nassuming we have from forms import MyForm\nIn real life this can get big, complex, and nasty\n
D\nassuming we have from forms import MyForm\nIn real life this can get big, complex, and nasty\n
D\n
D\n
D\n
D\n
D\n
D\n
D \nMy fiancee, Audrey Roy, said my example needs to handle file uploads. How do we do that?\n
M\n
M\n
M\n
M\n
M\nHey Danny, don&#x2019;t 91% of real Django projects use ModelForms?\n
D\n
D\n
D\n
D\n
D\n
D\n
D\n
D\n
D\n
D\n
D\n
D\n
D\n
D\n
D\n
D\n
D\n
D\n
D\n
D\n
D\nHey Miguel, how do I make this work when adding data?\n
M\n
M\n
M\n
D\n
D\n
D\n
D\n
D\n
D\n
D\n
M\nAny other tricks?\n
D\n
D\n
D\n
D\n
D\n
D Fields are in a dictionary! We should remember this fact!\n
D\n
D\n\n
M\nDidn&#x2019;t you just show that fields are stored in a dict in the form? Why not just add another field that way?\n
M\nDidn&#x2019;t you just show that fields are stored in a dict in the form? Why not just add another field that way?\n
D\n\n
M - Let's override form constructor to wrap the logic of adding fields on the go\n\n
M - Let's override form constructor to wrap the logic of adding fields on the go\n\n
D\n\n
M\n
M\n
M\n\n
D\n
D\n
D\n
D\n
D\n
D\n
D\n
D\n
D\n
D\n
D\n
D\n\n
M\n
M\n
M\n
D\n\n
M\n\n
M\nBecause both DUF and DFF do things in a clean, non-magical way - they just work together. \nNamespaces and clean code beat smart hackery any day.\n
M\nTell me what is coming down the road\n
D\n
D\n
D\n
D\nIt&#x2019;s okay that there are different objects\n
D\nIt&#x2019;s okay that there are different objects\n
D\nIt&#x2019;s okay that there are different objects\n
M\n\n
D\n\n
D\n
D\n\n
M\n
M Of course you could do the same with a regular CharField using form validationclean_<fieldname> method, but...\n
M - Duplicated code. So do a custom form field instead of duplicating code everywhere\nYes, you could solve this with form inheritance, but what if the inheritance chain gets too long?\n
M\n\n
D This field returns an already JSON validated Python list when accessing cleaned_data\n
D\n\n
M We are using MultiWidget.\n
M We are using MultiWidget.\n
M The difference is that you can get fancier with validation on the list than the string\n
D\n\n
M You need to implement compress\nIt would be nice that fields were also an attribute in the parent class.\n
M You need to implement compress\nIt would be nice that fields were also an attribute in the parent class.\n
M You need to implement compress\nIt would be nice that fields were also an attribute in the parent class.\n
M You need to implement compress\nIt would be nice that fields were also an attribute in the parent class.\n
M\n
M\n\n
D - You are going to love how easy this is! Ready?\n
D - You are going to love how easy this is! Ready?\n
D\n\n
M\n
M\n\n
M\n
M\n
M\n
M * You can raise a ValidationError with a code. * You can raise a ValidationError with a list of messages. * You could raise a ValidationError on the first email that fails, but error will be less helpful. * Hard to customize, no error_messages, Do this in an upper layer, rewrite run_validators\n