﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
22719	Proposal: Delayed Form binding (with working example code)	bernhard.hermann@…	nobody	"Hello everybody!

I would like to propose adding a mechanism to ""bind"" Form-objects in a way other than at instantiation (e.g. {{{ Form(request.POST)}}}).

This idea was inspired by the following assertion by Paul Graham:
  ""When I see patterns in my programs, I consider it a sign of trouble.
  The shape of a program should reflect only the problem it needs to 
  solve. Any other regularity in the code is a sign, to me at least, 
  that I'm using abstractions that aren't powerful enough-- often that 
  I'm generating by hand the expansions of some macro that I need to 
  write.""   [ http://www.paulgraham.com/icad.html ]

Let me give a quick example from the Django documentation's ""Working with forms"":
{{{#!python
def contact(request):
    if request.method == 'POST':
        form = ContactForm(request.POST)
        if form.is_valid():
            pass
        else:
            form = ContactForm()
    
    return render(request, 'contact.html', {'form': form,})
}}}
[ highlighted: http://pastebin.com/MAKUSrmY ]

This works well when I don't need to supply any keyword arguments. As soon as I want to do that, the code looks like this:
{{{#!python
def contact(request):
    if request.method == 'POST':
        form = ContactForm(request.POST, prefix='something-%s' % (some_id,), label_suffix=': ')
        if form.is_valid():
            pass
    else:
        form = ContactForm(prefix='something-%s' % (some_id,), label_suffix=': ')
    
    return render(request, 'contact.html', {'form': form,})
}}}
[ highlighted: http://pastebin.com/9FKS9ExU ]

The really bad (IMHO: wrong) thing about this is that the code
{{{    prefix='something-%s' % (some_id,), label_suffix=': '}}}
must be duplicated.

I am therefore using this class to bind the Form late:
{{{#!python
class FlexiForm(forms.Form):
    def set_fields(self):
        for (f_name, attributes) in self._thefields.items():
            for (att_name, att_value) in attributes.items():
                setattr(self._theinstance.fields[f_name], att_name, att_value)

    def __init__(self, form, args=None, kwargs=None, fields=None):
        self._theform = form
        self._theargs = args or []
        self._thekwargs = kwargs or {}
        self._thefields = fields or {}
        self.bind()

    # !: make it possible to use kwargs here
    def bind(self, *args):
        self._theinstance = self._theform(*(list(args) + self._theargs), **self._thekwargs)
        self.set_fields()
        return self

    def __getattr__(self, name):
        return getattr(self._theinstance, name)

    def __setattr__(self, name, value):
        inst = self
        if name not in ['_theform', '_theargs', '_thekwargs', '_thefields', '_theinstance']:
            inst = self.__theinstance
        super(FlexiForm, inst).__setattr__(name, value)
}}}
[ highlighted: http://pastebin.com/wzY4qWUJ ]
(This is the actual verbatim class from my code. If you spot any errors, please tell!)

Melding this with the above example, we get:
{{{#!python
def contact(request):
    form = FlexiForm(ContactForm, kwargs={'prefix': 'something-%s' % (some_id,), 'label_suffix': ': ',})
    if request.method == 'POST':
        form.bind(request.POST)  # substitutes internal ContactForm() with ContactForm(request.POST)
        if form.is_valid():
           pass
    else:
        pass  # Form already instantiated!

    return render(request, 'contact.html', {
        'form': form,
    })
}}}
[ highlighted: http://pastebin.com/zNA9MzRJ ]

'''Advantage: keyword (and other) arguments that are vitally important to many of my forms don't need to be duplicated in different locations.
Probability of mistakes/bugs is lowered, maintainability and readability improved.'''

I am sure there is a more elegant/correct way to do this.
I suspect it should be added to Django's Form class.

I am looking forward to your ...
[x] criticism
[x] reasons why this shouldn't be done
[x] promises that it will be implemented soon
[x] mindless ranting

best regards,
Bernhard"	New feature	closed	Forms	1.6	Normal	wontfix	bound unbound bind		Unreviewed	0	0	0	0	0	0
