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):
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

View File

@ -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)

View File

@ -15,22 +15,19 @@
# along with this programe. If not, see <http://www.gnu.org/licenses/>.
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))

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'