diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
index 930db7767e..696dd30387 100644
--- a/django/contrib/admin/options.py
+++ b/django/contrib/admin/options.py
@@ -94,6 +94,11 @@ class BaseModelAdmin(object):
kwargs['widget'] = widgets.AdminIntegerFieldWidget
return db_field.formfield(**kwargs)
+ # For CommaSeparatedIntegerFields, add a custom CSS class.
+ if isinstance(db_field, models.CommaSeparatedIntegerField):
+ kwargs['widget'] = widgets.AdminCommaSeparatedIntegerFieldWidget
+ return db_field.formfield(**kwargs)
+
# For TextInputs, add a custom CSS class.
if isinstance(db_field, models.CharField):
kwargs['widget'] = widgets.AdminTextInputWidget
diff --git a/django/contrib/admin/widgets.py b/django/contrib/admin/widgets.py
index d5ece3f933..aaf1caafe7 100644
--- a/django/contrib/admin/widgets.py
+++ b/django/contrib/admin/widgets.py
@@ -236,3 +236,10 @@ class AdminIntegerFieldWidget(forms.TextInput):
if attrs is not None:
final_attrs.update(attrs)
super(AdminIntegerFieldWidget, self).__init__(attrs=final_attrs)
+
+class AdminCommaSeparatedIntegerFieldWidget(forms.TextInput):
+ def __init__(self, attrs=None):
+ final_attrs = {'class': 'vCommaSeparatedIntegerField'}
+ if attrs is not None:
+ final_attrs.update(attrs)
+ super(AdminCommaSeparatedIntegerFieldWidget, self).__init__(attrs=final_attrs)
diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py
index d3fb27b948..26f83158b8 100644
--- a/django/db/models/fields/__init__.py
+++ b/django/db/models/fields/__init__.py
@@ -394,7 +394,17 @@ class CharField(Field):
# TODO: Maybe move this into contrib, because it's specialized.
class CommaSeparatedIntegerField(CharField):
- pass
+ def formfield(self, **kwargs):
+ defaults = {
+ 'form_class': forms.RegexField,
+ 'regex': '^[\d,]+$',
+ 'max_length': self.max_length,
+ 'error_messages': {
+ 'invalid': _(u'Enter only digits separated by commas.'),
+ }
+ }
+ defaults.update(kwargs)
+ return super(CommaSeparatedIntegerField, self).formfield(**defaults)
ansi_date_re = re.compile(r'^\d{4}-\d{1,2}-\d{1,2}$')
diff --git a/tests/modeltests/model_forms/models.py b/tests/modeltests/model_forms/models.py
index a2c522a1d9..b8d3f932f8 100644
--- a/tests/modeltests/model_forms/models.py
+++ b/tests/modeltests/model_forms/models.py
@@ -98,6 +98,12 @@ class ImageFile(models.Model):
def __unicode__(self):
return self.description
+class CommaSeparatedInteger(models.Model):
+ field = models.CommaSeparatedIntegerField(max_length=20)
+
+ def __unicode__(self):
+ return self.field
+
__test__ = {'API_TESTS': """
>>> from django import forms
>>> from django.forms.models import ModelForm, model_to_dict
@@ -1050,4 +1056,30 @@ True
+>>> class CommaSeparatedIntegerForm(ModelForm):
+... class Meta:
+... model = CommaSeparatedInteger
+
+>>> f = CommaSeparatedIntegerForm().fields['field']
+>>> f.clean('1,2,3')
+u'1,2,3'
+>>> f.clean('1a,2')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter only digits separated by commas.']
+>>> f.clean(',,,,')
+u',,,,'
+>>> f.clean('1.2')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter only digits separated by commas.']
+>>> f.clean('1,a,2')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter only digits separated by commas.']
+>>> f.clean('1,,2')
+u'1,,2'
+>>> f.clean('1')
+u'1'
+
"""}