From a0b976a7581cbb1e874290aa77855bf2d54c607e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Mart=C3=ADn?= Date: Thu, 27 Sep 2012 16:02:41 +0200 Subject: [PATCH] Initial version --- CHANGES.rst | 4 ++ MANIFEST.in | 4 ++ README.md => README.rst | 0 setup.py | 29 ++++++++++++ src/multiselectfield/__init__.py | 2 + src/multiselectfield/db/__init__.py | 0 src/multiselectfield/db/fields.py | 64 ++++++++++++++++++++++++++ src/multiselectfield/forms/__init__.py | 0 src/multiselectfield/forms/fields.py | 19 ++++++++ 9 files changed, 122 insertions(+) create mode 100644 CHANGES.rst create mode 100644 MANIFEST.in rename README.md => README.rst (100%) create mode 100644 setup.py create mode 100644 src/multiselectfield/__init__.py create mode 100644 src/multiselectfield/db/__init__.py create mode 100644 src/multiselectfield/db/fields.py create mode 100644 src/multiselectfield/forms/__init__.py create mode 100644 src/multiselectfield/forms/fields.py diff --git a/CHANGES.rst b/CHANGES.rst new file mode 100644 index 0000000..9df7d33 --- /dev/null +++ b/CHANGES.rst @@ -0,0 +1,4 @@ +0.0.1 (2012-09-27) +------------------ + +* Initial version from the next `snippet `_ \ No newline at end of file diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..8274ccc --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,4 @@ +include README.rst +include AUTHORS +include CHANGES +include *.py diff --git a/README.md b/README.rst similarity index 100% rename from README.md rename to README.rst diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..a7d6952 --- /dev/null +++ b/setup.py @@ -0,0 +1,29 @@ +#http://djangosnippets.org/users/danielroseman/ +# -*- coding: utf-8 -*- + +import os +from setuptools import setup, find_packages + + +def read(*rnames): + return open(os.path.join(os.path.dirname(__file__), *rnames)).read() + +setup( + name="django-multiselectfield", + version="0.0.1", + author="Daniel Roseman", + description="Django multiple select field", + long_description=(read('README.rst') + '\n\n' + read('CHANGES.rst')), + classifiers=[ + 'Development Status :: 4 - Beta', + 'Framework :: Django', + 'License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)', + ], + license="LGPL 3", + keywords="django,multiple,select,field,choices", + url='https://github.com/goinnn/django-multiselectfield', + packages=find_packages(where='src'), + package_dir={'': 'src'}, + include_package_data=True, + zip_safe=False, +) diff --git a/src/multiselectfield/__init__.py b/src/multiselectfield/__init__.py new file mode 100644 index 0000000..32e5b67 --- /dev/null +++ b/src/multiselectfield/__init__.py @@ -0,0 +1,2 @@ +from multiselectfield.db.fields import MultiSelectField +from multiselectfield.forms.fields import MultiSelectFormField diff --git a/src/multiselectfield/db/__init__.py b/src/multiselectfield/db/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/multiselectfield/db/fields.py b/src/multiselectfield/db/fields.py new file mode 100644 index 0000000..71acc24 --- /dev/null +++ b/src/multiselectfield/db/fields.py @@ -0,0 +1,64 @@ +from django.db import models +from django.utils.text import capfirst +from django.core import exceptions + +from multiselectfield import MultiSelectFormField + + +class MultiSelectField(models.Field): + """ Choice values can not contain commas. """ + __metaclass__ = models.SubfieldBase + + def get_internal_type(self): + return "CharField" + + def get_choices_default(self): + return self.get_choices(include_blank=False) + + def get_choices_selected(self, arr_choices=''): + if not arr_choices: + return False + list = [] + for choice_selected in arr_choices: + list.append(choice_selected[0]) + return list + + def _get_FIELD_display(self, field): + value = getattr(self, field.attname) + choicedict = dict(field.choices) + + def value_to_string(self, obj): + value = self._get_val_from_obj(obj) + return self.get_db_prep_value(value) + + def validate(self, value, model_instance): + arr_choices = self.get_choices_selected(self.get_choices_default()) + for opt_select in value: + if (opt_select not in arr_choices): + raise exceptions.ValidationError(self.error_messages['invalid_choice'] % value) + return + + def formfield(self, **kwargs): + defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), + 'help_text': self.help_text, 'choices': self.choices} + if self.has_default(): + defaults['initial'] = self.get_default() + defaults.update(kwargs) + return MultiSelectFormField(**defaults) + + def get_db_prep_value(self, value, connection, prepared=False): + if isinstance(value, basestring): + return value + elif isinstance(value, list): + return ",".join(value) + + def to_python(self, value): + if value is not None: + return value if isinstance(value, list) else value.split(',') + return '' + + def contribute_to_class(self, cls, name): + super(MultiSelectField, self).contribute_to_class(cls, name) + if self.choices: + func = lambda self, fieldname = name, choicedict = dict(self.choices): ",".join([choicedict.get(value, value) for value in getattr(self, fieldname)]) + setattr(cls, 'get_%s_display' % self.name, func) diff --git a/src/multiselectfield/forms/__init__.py b/src/multiselectfield/forms/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/multiselectfield/forms/fields.py b/src/multiselectfield/forms/fields.py new file mode 100644 index 0000000..d1f369d --- /dev/null +++ b/src/multiselectfield/forms/fields.py @@ -0,0 +1,19 @@ +from django import forms +from django.contrib.humanize.templatetags.humanize import apnumber +from django.template.defaultfilters import pluralize + + +class MultiSelectFormField(forms.MultipleChoiceField): + widget = forms.CheckboxSelectMultiple + + def __init__(self, *args, **kwargs): + self.max_choices = kwargs.pop('max_choices', 0) + 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