When you write a blog post, people of Internet will point out any mistakes they find. It’s a great opportunity to improve existing code and look into other solutions. After posting The tale of DRY with django-crispy-forms I found out about formulation - a template-based solution for rendering forms, in contrast to crispy-forms that builds the form structure in code.
Also @maraujop – crispy-forms developer – made several very helpful suggestions in the comments area, which brings us to Part II.
Mistakes of the past
The story of Foo people can be found in this archive. Their end was tragic. They knew truth was somewhere close, but they were unable to grasp it to its fullest and failed to survive this cruel software world.
FooForm
s had their __init__
cluttered with the initialization
of a FormHelper
object and the attachment of the SubmitCancelFormActions
.
Nothing bad with SubmitCancelFormActions
itself, but as they grew in number
overriding the initialization started to look silly.
Decline of Foo marked the birth of powerful Bar people.
Reforms
Assume that Bar model, view and template are similar to Foo. Bar people knew they should start with the API, and so they shaped their dreams first.
What Bar wants is to declare an alternative FormHelper
, which is responsible
for customizing the form. Also it wants to provide helper attributes, that are different
for each form - the cancel URL, for example.
There are 2 unknown creatures here - SubmitCancelFormHelper
and ModelFormWithHelper
.
SubmitCancelFormHelper
as you might have guessed is of FormHelper
breed. It
tells you right to the face that it appends to the layout submit and cancel buttons.
See for yourself.
This class expects a cancel_href
named parameter. Afterwards it takes
advantage of the power it was given by FormHelper
and modifies the layout by
appending a button that we represent using HTML widget and a Submit widget.
SubmitCancelFormHelper
serves us well.
What about ModelFormWithHelper
mystery. This base class is a bit more tricky,
but still nothing out of this world.
Ohh, that’s a lot of code. Well, docstrings make it 1/3 of the listing. Let’s
see what ModelFormWithHelper
is trying to tell us.
In the initialization phase it checks if the class
has a helper_class
attribute. It’s mandatory to be there, otherwise there’s
no point in using this class at all. If there isn’t, ModelFormWithHelper
will
throw an exception to remind you, Hey, you forgot that attribute - helper_class!
Otherwise, it will initialize the form helper class and attach it
to self.helper
. But before that it extracts the helper kwargs.
Look at the dream BarForm
class. Bar people wanted to have a custom cancel URL
and used a meta attribute helper_cancel_href
. Method get_helper_kwargs()
deals
with all the helper_
attributes and strips them of that prefix to ultimately
feed them to the helper.
The class is not tied to any specific form helper, so you can create as many custom form helpers as your kingdom needs.
That’s it. Now forms will become more compact and readable due to the new meta attributes.