Opened 13 years ago
Closed 12 years ago
#18064 closed New feature (duplicate)
Allow changing form field properties after form creation
Reported by: | Chris Wilson | Owned by: | nobody |
---|---|---|---|
Component: | Forms | Version: | 1.4 |
Severity: | Normal | Keywords: | |
Cc: | Triage Stage: | Unreviewed | |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
Currently, if I want to tweak the properties of some fields in a ModelForm, I have to replace them in the subclass like this:
class DocumentForm(ModelForm): title = models.Document._meta.get_field('title').formfield(required=False) programs = models.Document._meta.get_field('programs').formfield(required=False) authors = models.Document._meta.get_field('authors').formfield(required=False) confidential = models.Document._meta.get_field('confidential').formfield(widget=AdminYesNoWidget) uploader = models.Document._meta.get_field('uploader').formfield(required=False) file = models.Document._meta.get_field('file').formfield(widget=AdminFileWidgetWithSize)
This is very bulky to just change some properties. The problem is that the field copies some of its properties into weird places like the widget during initialisation:
class Field(object): ... def __init__(...) ... if help_text is None: self.help_text = u'' else: self.help_text = smart_unicode(help_text) widget = widget or self.widget if isinstance(widget, type): widget = widget() # Trigger the localization machinery if needed. self.localize = localize if self.localize: widget.is_localized = True # Let the widget know whether it should display as required. widget.is_required = self.required # Hook into self.widget_attrs() for any Field-specific HTML attributes. extra_attrs = self.widget_attrs(widget) if extra_attrs: widget.attrs.update(extra_attrs) self.widget = widget
If we refactored this so that all the property initialisation was done in setters, then we could just write:
class DocumentForm(ModelForm): def __init__(...) self['title'].required = False self['programs'].help_text = 'Hold down Ctrl to select multiple options' self['authors'].required = False self['confidential'].widget = AdminYesNoWidget self['uploader'].required = False self['file'].widget = AdminFileWidgetWithSize
Which is more concise, much clearer, and does not override things unnecessarily.
I can write the code, but first I want to:
- see if any core developers object, to avoid wasting effort
- know how to run the Django tests and which suite(s) I should be running
- know where to put the tests.
Change History (4)
comment:1 by , 13 years ago
Type: | Uncategorized → New feature |
---|
comment:2 by , 13 years ago
On Thu, 5 Apr 2012, Beres Botond wrote:
Isn't it this what you are trying to do?
class DocumentForm(ModelForm): def __init__(self, *args, **kwargs): super(MyForm, self).__init__(*args, **kwargs) self.fields['title'].required = False
Which works perfectly fine. You can tweak pretty much any property at
runtime like that, without replacing the field entirely.
Unfortunately not. As you can see from the code above, the Field constructor copies the required parameter to the widget, but only during __init__
:
# Let the widget know whether it should display as required. widget.is_required = self.required
So changing the property afterwards doesn't affect the widget as it should. Currently only the FileInput is affected by this, but nothing says that other controls won't take advantage of knowing this in future.
I agree with Keryn that #17924 covers a similar need, except for configuring the controls at runtime, e.g. depending on settings or database contents.
For future reference/discussion, #17924 looks to cover a similar sort of need.