#22206 closed New feature (fixed)

TextField doesn't set max_length attribute on its formfield, unlike CharField

Reported by: gcc Owned by: gcc
Component: Forms Version: master
Severity: Normal Keywords:
Cc: Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

In django/db/models/fields/init.py, the CharField field type constructs its widgets with a max_length parameter, which ends up in the widget:

class CharField(Field): # django.db.forms.fields
    def formfield(self, **kwargs):
        # Passing max_length to forms.CharField means that the value's length
        # will be validated twice. This is considered acceptable since we want
        # the value in the form field (to pass into widget for example).
        defaults = {'max_length': self.max_length}
        defaults.update(kwargs)
        return super(CharField, self).formfield(**defaults)

class CharField(Field): # django.forms.fields
    def widget_attrs(self, widget):
        attrs = super(CharField, self).widget_attrs(widget)
        if self.max_length is not None and isinstance(widget, TextInput):
            # The HTML attribute is maxlength, not max_length.
            attrs.update({'maxlength': str(self.max_length)})
        return attrs

But nothing like this happens for the TextField database field, which creates a TextField form field, which uses a Textarea widget by default:

class TextField(Field):
    description = _("Text")

    def formfield(self, **kwargs):
        defaults = {'widget': forms.Textarea}
        defaults.update(kwargs)
        return super(TextField, self).formfield(**defaults)

I think that TextField doesn't override form_class, so it gets the default CharField from Field.formfield(), as CharField does, but with a custom widget. So all we'd need to do is set the attribute in the CharField db field:

    def formfield(self, **kwargs):
        # Passing max_length to forms.CharField means that the value's length
        # will be validated twice. This is considered acceptable since we want
        # the value in the form field (to pass into widget for example).
        defaults = {'max_length': self.max_length}
        defaults.update(kwargs)
        return super(TextField, self).formfield(**defaults)

and add the attribute to the widget in CharField:

    def widget_attrs(self, widget):
        attrs = super(CharField, self).widget_attrs(widget)
        if self.max_length is not None: # all types of widgets, not hard coded
            # The HTML attribute is maxlength, not max_length.
            attrs.update({'maxlength': str(self.max_length)})
        return attrs

Change History (5)

comment:1 Changed 17 months ago by gcc

  • Has patch set
  • Needs documentation unset
  • Needs tests unset
  • Owner changed from nobody to gcc
  • Patch needs improvement unset
  • Status changed from new to assigned

comment:2 Changed 17 months ago by claudep

  • Triage Stage changed from Unreviewed to Accepted
  • Version changed from 1.6 to master

Sure, maxlength is now also handled by textarea in HTML 5 (http://dev.w3.org/html5/markup/textarea.html#textarea.attrs.maxlength).

comment:3 Changed 17 months ago by claudep

The patch is now in good shape. However, I'd like to add an additional note in the TextField documentation, something like this:

.. versionchanged:: 1.7

    If you specify a ``max_length`` attribute, it will be reflected in the
    :class:`~django.forms.Textarea` widget of the auto-generated form field.
    However it is not enforced at the model or database level. Use a
    :class:`CharField` for that.

Opinion?

comment:4 Changed 17 months ago by mjtamlyn

Yes, I think that admonition is quite important.

comment:5 Changed 17 months ago by Claude Paroz <claude@…>

  • Resolution set to fixed
  • Status changed from assigned to closed

In 95c74b9d699c29fe808684774548e2864d64665a:

Fixed #22206 -- Passed models.TextField.max_length to forms.CharField.maxlength

Note: See TracTickets for help on using tickets.
Back to Top