Skip to content

Commit

Permalink
Fixed #22206 -- Passed models.TextField.max_length to forms.CharField…
Browse files Browse the repository at this point in the history
….maxlength
  • Loading branch information
qris authored and claudep committed Mar 5, 2014
1 parent ac699cd commit 95c74b9
Show file tree
Hide file tree
Showing 8 changed files with 37 additions and 8 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Expand Up @@ -666,6 +666,7 @@ answer newbie questions, and generally made Django that much better:
Rachel Willmer <http://www.willmer.com/kb/>
Jakub Wilk <ubanus@users.sf.net>
Ian A Wilson <http://ianawilson.com>
Chris Wilson <chris+github@qwirx.com>
Jakub Wiśniowski <restless.being@gmail.com>
Maciej Wiśniowski <pigletto@gmail.com>
wojtek
Expand Down
5 changes: 4 additions & 1 deletion django/db/models/fields/__init__.py
Expand Up @@ -1850,7 +1850,10 @@ def get_prep_value(self, value):
return smart_text(value)

def formfield(self, **kwargs):
defaults = {'widget': forms.Textarea}
# 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, 'widget': forms.Textarea}
defaults.update(kwargs)
return super(TextField, self).formfield(**defaults)

Expand Down
2 changes: 1 addition & 1 deletion django/forms/fields.py
Expand Up @@ -223,7 +223,7 @@ def to_python(self, value):

def widget_attrs(self, widget):
attrs = super(CharField, self).widget_attrs(widget)
if self.max_length is not None and isinstance(widget, TextInput):
if self.max_length is not None:
# The HTML attribute is maxlength, not max_length.
attrs.update({'maxlength': str(self.max_length)})
return attrs
Expand Down
7 changes: 7 additions & 0 deletions docs/ref/models/fields.txt
Expand Up @@ -968,6 +968,13 @@ databases supported by Django.
A large text field. The default form widget for this field is a
:class:`~django.forms.Textarea`.

.. 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.

.. admonition:: MySQL users

If you are using this field with MySQLdb 1.2.1p2 and the ``utf8_bin``
Expand Down
4 changes: 4 additions & 0 deletions docs/releases/1.7.txt
Expand Up @@ -488,6 +488,10 @@ Forms
Each radio button or checkbox includes an ``id_for_label`` attribute to
output the element's ID.

* The ``<textarea>`` tags rendered by :class:`~django.forms.Textarea` now
include a ``maxlength`` attribute if the :class:`~django.db.models.TextField`
model field has a ``max_length``.

* :attr:`Field.choices<django.db.models.Field.choices>` now allows you to
customize the "empty choice" label by including a tuple with an empty string
or ``None`` for the key and the custom label as the value. The default blank
Expand Down
9 changes: 4 additions & 5 deletions tests/forms_tests/tests/test_fields.py
Expand Up @@ -38,7 +38,7 @@
DecimalField, EmailField, Field, FileField, FilePathField, FloatField,
Form, forms, HiddenInput, IntegerField, MultipleChoiceField,
NullBooleanField, NumberInput, PasswordInput, RadioSelect, RegexField,
SplitDateTimeField, TextInput, TimeField, TypedChoiceField,
SplitDateTimeField, TextInput, Textarea, TimeField, TypedChoiceField,
TypedMultipleChoiceField, URLField, ValidationError, Widget,
)
from django.test import SimpleTestCase
Expand Down Expand Up @@ -148,14 +148,13 @@ def test_charfield_widget_attrs(self):
# Return an empty dictionary if max_length is None
f = CharField()
self.assertEqual(f.widget_attrs(TextInput()), {})

# Or if the widget is not TextInput or PasswordInput
f = CharField(max_length=10)
self.assertEqual(f.widget_attrs(HiddenInput()), {})
self.assertEqual(f.widget_attrs(Textarea()), {})

# Otherwise, return a maxlength attribute equal to max_length
f = CharField(max_length=10)
self.assertEqual(f.widget_attrs(TextInput()), {'maxlength': '10'})
self.assertEqual(f.widget_attrs(PasswordInput()), {'maxlength': '10'})
self.assertEqual(f.widget_attrs(Textarea()), {'maxlength': '10'})

# IntegerField ################################################################

Expand Down
15 changes: 15 additions & 0 deletions tests/model_fields/tests.py
Expand Up @@ -203,6 +203,21 @@ def test_booleanfield_to_python(self):
def test_nullbooleanfield_to_python(self):
self._test_to_python(models.NullBooleanField())

def test_charfield_textfield_max_length_passed_to_formfield(self):
"""
Test that CharField and TextField pass their max_length attributes to
form fields created using their .formfield() method (#22206).
"""
cf1 = models.CharField()
cf2 = models.CharField(max_length=1234)
self.assertIsNone(cf1.formfield().max_length)
self.assertEqual(1234, cf2.formfield().max_length)

tf1 = models.TextField()
tf2 = models.TextField(max_length=2345)
self.assertIsNone(tf1.formfield().max_length)
self.assertEqual(2345, tf2.formfield().max_length)

def test_booleanfield_choices_blank(self):
"""
Test that BooleanField with choices and defaults doesn't generate a
Expand Down
2 changes: 1 addition & 1 deletion tests/model_forms/tests.py
Expand Up @@ -533,7 +533,7 @@ def test_widget_overrides(self):
form = FieldOverridesByFormMetaForm()
self.assertHTMLEqual(
str(form['name']),
'<textarea id="id_name" rows="10" cols="40" name="name"></textarea>',
'<textarea id="id_name" rows="10" cols="40" name="name" maxlength="20"></textarea>',
)
self.assertHTMLEqual(
str(form['url']),
Expand Down

0 comments on commit 95c74b9

Please sign in to comment.