Code

Opened 2 years ago

Closed 23 months ago

#18064 closed New feature (duplicate)

Allow changing form field properties after form creation

Reported by: gcc 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.

Attachments (0)

Change History (4)

comment:1 Changed 2 years ago by Keryn Knight <django@…>

  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset
  • Type changed from Uncategorized to New feature

For future reference/discussion, #17924 looks to cover a similar sort of need.

comment:2 Changed 2 years ago by gcc

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.

comment:3 Changed 2 years ago by jezdez

Closing as a duplicate of #17924.

comment:4 Changed 23 months ago by apollo13

  • Resolution set to duplicate
  • Status changed from new to closed

Actually closing it…

Add Comment

Modify Ticket

Change Properties
<Author field>
Action
as closed
as The resolution will be set. Next status will be 'closed'
The resolution will be deleted. Next status will be 'new'
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.