Now max_length is not required, the Multiselect field calculate it. And max_choices could be a param in the model

This commit is contained in:
Pablo Martín 2013-11-26 18:56:37 +01:00
parent cd3ef2e8e7
commit 8c9c3a7682
5 changed files with 95 additions and 20 deletions

View file

@ -41,9 +41,10 @@ TAGS_CHOICES = (
class Book(models.Model): class Book(models.Model):
title = models.CharField(max_length=200) title = models.CharField(max_length=200)
categories = MultiSelectField(choices=CATEGORY_CHOICES, max_length=10, categories = MultiSelectField(choices=CATEGORY_CHOICES,
null=True, blank=True) max_choices=3)
tags = MultiSelectField(choices=TAGS_CHOICES, max_length=10) tags = MultiSelectField(choices=TAGS_CHOICES,
null=True, blank=True)
def __str__(self): def __str__(self):
return self.title return self.title

View file

@ -21,7 +21,9 @@ from django.db import models
from django.utils.text import capfirst from django.utils.text import capfirst
from django.core import exceptions 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: if sys.version_info[0] == 2:
string = basestring string = basestring
@ -48,11 +50,19 @@ def add_metaclass(metaclass):
class MultiSelectField(models.CharField): class MultiSelectField(models.CharField):
""" Choice values can not contain commas. """ """ 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): def get_choices_default(self):
return self.get_choices(include_blank=False) return self.get_choices(include_blank=False)
def get_choices_selected(self, arr_choices=''): def get_choices_selected(self, arr_choices=None):
if not arr_choices: if arr_choices is None:
return False return False
list = [] list = []
for choice_selected in arr_choices: for choice_selected in arr_choices:
@ -75,8 +85,12 @@ class MultiSelectField(models.CharField):
return return
def formfield(self, **kwargs): def formfield(self, **kwargs):
defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), defaults = {'required': not self.blank,
'help_text': self.help_text, 'choices': self.choices} '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(): if self.has_default():
defaults['initial'] = self.get_default() defaults['initial'] = self.get_default()
defaults.update(kwargs) defaults.update(kwargs)

View file

@ -15,22 +15,19 @@
# along with this programe. If not, see <http://www.gnu.org/licenses/>. # along with this programe. If not, see <http://www.gnu.org/licenses/>.
from django import forms 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): class MultiSelectFormField(forms.MultipleChoiceField):
widget = forms.CheckboxSelectMultiple widget = forms.CheckboxSelectMultiple
def __init__(self, *args, **kwargs): 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) super(MultiSelectFormField, self).__init__(*args, **kwargs)
self.max_length = get_max_length(self.choices, self.max_length)
def clean(self, value): self.validators.append(MaxValueMultiFieldValidator(self.max_length))
if not value and self.required: if self.max_choices is not None:
raise forms.ValidationError(self.error_messages['required']) self.validators.append(MaxChoicesValidator(self.max_choices))
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

View file

@ -0,0 +1,34 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2013 by Pablo Martín <goinnn@gmail.com>
#
# 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 <http://www.gnu.org/licenses/>.
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

View file

@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2013 by Pablo Martín <goinnn@gmail.com>
#
# 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 <http://www.gnu.org/licenses/>.
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'