diff --git a/example/example/app/models.py b/example/example/app/models.py
index 628ffc5..f601d9f 100644
--- a/example/example/app/models.py
+++ b/example/example/app/models.py
@@ -41,9 +41,10 @@ TAGS_CHOICES = (
class Book(models.Model):
title = models.CharField(max_length=200)
- categories = MultiSelectField(choices=CATEGORY_CHOICES, max_length=10,
- null=True, blank=True)
- tags = MultiSelectField(choices=TAGS_CHOICES, max_length=10)
+ categories = MultiSelectField(choices=CATEGORY_CHOICES,
+ max_choices=3)
+ tags = MultiSelectField(choices=TAGS_CHOICES,
+ null=True, blank=True)
def __str__(self):
return self.title
diff --git a/src/multiselectfield/db/fields.py b/src/multiselectfield/db/fields.py
index d26420a..acc184c 100644
--- a/src/multiselectfield/db/fields.py
+++ b/src/multiselectfield/db/fields.py
@@ -21,7 +21,9 @@ from django.db import models
from django.utils.text import capfirst
from django.core import exceptions
-from ..forms.fields import MultiSelectFormField
+from ..forms.fields import MultiSelectFormField, MaxChoicesValidator
+from ..utils import get_max_length
+from ..validators import MaxValueMultiFieldValidator
if sys.version_info[0] == 2:
string = basestring
@@ -48,11 +50,19 @@ def add_metaclass(metaclass):
class MultiSelectField(models.CharField):
""" Choice values can not contain commas. """
+ def __init__(self, *args, **kwargs):
+ self.max_choices = kwargs.pop('max_choices', None)
+ super(MultiSelectField, self).__init__(*args, **kwargs)
+ self.max_length = get_max_length(self.choices, self.max_length)
+ self.validators[0] = MaxValueMultiFieldValidator(self.max_length)
+ if self.max_choices is not None:
+ self.validators.append(MaxChoicesValidator(self.max_choices))
+
def get_choices_default(self):
return self.get_choices(include_blank=False)
- def get_choices_selected(self, arr_choices=''):
- if not arr_choices:
+ def get_choices_selected(self, arr_choices=None):
+ if arr_choices is None:
return False
list = []
for choice_selected in arr_choices:
@@ -75,8 +85,12 @@ class MultiSelectField(models.CharField):
return
def formfield(self, **kwargs):
- defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name),
- 'help_text': self.help_text, 'choices': self.choices}
+ defaults = {'required': not self.blank,
+ 'label': capfirst(self.verbose_name),
+ 'help_text': self.help_text,
+ 'choices': self.choices,
+ 'max_length': self.max_length,
+ 'max_choices': self.max_choices}
if self.has_default():
defaults['initial'] = self.get_default()
defaults.update(kwargs)
diff --git a/src/multiselectfield/forms/fields.py b/src/multiselectfield/forms/fields.py
index 23e4491..db3fd1b 100644
--- a/src/multiselectfield/forms/fields.py
+++ b/src/multiselectfield/forms/fields.py
@@ -15,22 +15,19 @@
# along with this programe. If not, see .
from django import forms
-from django.contrib.humanize.templatetags.humanize import apnumber
-from django.template.defaultfilters import pluralize
+
+from ..utils import get_max_length
+from ..validators import MaxValueMultiFieldValidator, MaxChoicesValidator
class MultiSelectFormField(forms.MultipleChoiceField):
widget = forms.CheckboxSelectMultiple
def __init__(self, *args, **kwargs):
- self.max_choices = kwargs.pop('max_choices', 0)
+ self.max_choices = kwargs.pop('max_choices', None)
+ self.max_length = kwargs.pop('max_length', None)
super(MultiSelectFormField, self).__init__(*args, **kwargs)
-
- def clean(self, value):
- if not value and self.required:
- raise forms.ValidationError(self.error_messages['required'])
- if value and self.max_choices and len(value) > self.max_choices:
- raise forms.ValidationError('You must select a maximum of %s choice%s.'
- % (apnumber(self.max_choices),
- pluralize(self.max_choices)))
- return value
+ self.max_length = get_max_length(self.choices, self.max_length)
+ self.validators.append(MaxValueMultiFieldValidator(self.max_length))
+ if self.max_choices is not None:
+ self.validators.append(MaxChoicesValidator(self.max_choices))
diff --git a/src/multiselectfield/utils.py b/src/multiselectfield/utils.py
new file mode 100644
index 0000000..1f9e8c6
--- /dev/null
+++ b/src/multiselectfield/utils.py
@@ -0,0 +1,34 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2013 by Pablo Martín
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this programe. If not, see .
+
+import sys
+
+
+if sys.version_info[0] == 2:
+ string = basestring
+ string_type = unicode
+else:
+ string = str
+ string_type = string
+
+
+def get_max_length(choices, max_length, default=200):
+ if max_length is None:
+ if choices:
+ return len(','.join([string_type(key) for key, label in choices]))
+ else:
+ return default
+ return max_length
diff --git a/src/multiselectfield/validators.py b/src/multiselectfield/validators.py
new file mode 100644
index 0000000..bd45332
--- /dev/null
+++ b/src/multiselectfield/validators.py
@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2013 by Pablo Martín
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this programe. If not, see .
+
+
+from django.core import validators
+from django.utils.translation import ugettext_lazy as _
+
+
+class MaxValueMultiFieldValidator(validators.MaxLengthValidator):
+ clean = lambda self, x: len(','.join(x))
+ code = 'max_multifield_value'
+
+
+class MaxChoicesValidator(validators.MaxLengthValidator):
+ message = _(u'You must select a maximum of %(limit_value)d choices.')
+ code = 'max_choices'