#22719: Proposal: Delayed Form binding (with working example code) ------------------------------------+-------------------------------- Reporter: bernhard.hermann@… | Owner: nobody Type: New feature | Status: new Component: Forms | Version: 1.6 Severity: Normal | Keywords: bound unbound bind Triage Stage: Unreviewed | Has patch: 0 Easy pickings: 0 | UI/UX: 0 ------------------------------------+-------------------------------- 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 -- Ticket URL: <https://code.djangoproject.com/ticket/22719> Django <https://code.djangoproject.com/> The Web framework for perfectionists with deadlines. -- You received this message because you are subscribed to the Google Groups "Django updates" group. To unsubscribe from this group and stop receiving emails from it, send an email to django-updates+unsubscr...@googlegroups.com. To post to this group, send email to django-updates@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/django-updates/072.f13010e01998d3b9b093f5a7cf0bca26%40djangoproject.com. For more options, visit https://groups.google.com/d/optout.