Merge pull request #52 from goinnn/min-choices
Min choices field option
This commit is contained in:
commit
5d02ceb6ba
16 changed files with 124 additions and 74 deletions
66
.travis.yml
66
.travis.yml
|
@ -7,52 +7,60 @@ python:
|
|||
- "3.4"
|
||||
- "3.5"
|
||||
env:
|
||||
- DJANGO=1.4.22
|
||||
- DJANGO=1.5.12
|
||||
- DJANGO=1.6.11
|
||||
- DJANGO=1.7.11
|
||||
- DJANGO=1.8.14
|
||||
- DJANGO=1.9.9
|
||||
- DJANGO=1.10.1
|
||||
- DJANGO_VERSION='Django>=1.4,<1.5'
|
||||
- DJANGO_VERSION='Django>=1.5,<1.6'
|
||||
- DJANGO_VERSION='Django>=1.6,<1.7'
|
||||
- DJANGO_VERSION='Django>=1.7,<1.8'
|
||||
- DJANGO_VERSION='Django>=1.8,<1.9'
|
||||
- DJANGO_VERSION='Django>=1.9,<1.10'
|
||||
- DJANGO_VERSION='Django>=1.10,<1.11'
|
||||
- DJANGO_VERSION='https://github.com/django/django/archive/master.tar.gz'
|
||||
matrix:
|
||||
exclude:
|
||||
- python: "2.6"
|
||||
env: DJANGO=1.7.11
|
||||
env: DJANGO_VERSION='Django>=1.7,<1.8'
|
||||
- python: "2.6"
|
||||
env: DJANGO=1.8.14
|
||||
env: DJANGO_VERSION='Django>=1.8,<1.9'
|
||||
- python: "2.6"
|
||||
env: DJANGO=1.9.9
|
||||
env: DJANGO_VERSION='Django>=1.9,<1.10'
|
||||
- python: "2.6"
|
||||
env: DJANGO=1.10.1
|
||||
env: DJANGO_VERSION='Django>=1.10,<1.11'
|
||||
- python: "2.6"
|
||||
env: DJANGO_VERSION='https://github.com/django/django/archive/master.tar.gz'
|
||||
- python: "3.3"
|
||||
env: DJANGO=1.4.22
|
||||
env: DJANGO_VERSION='Django>=1.4,<1.5'
|
||||
- python: "3.3"
|
||||
env: DJANGO_VERSION='Django>=1.5,<1.6'
|
||||
- python: "3.3"
|
||||
env: DJANGO_VERSION='Django>=1.9,<1.10'
|
||||
- python: "3.3"
|
||||
env: DJANGO_VERSION='Django>=1.10,<1.11'
|
||||
- python: "3.3"
|
||||
env: DJANGO_VERSION='https://github.com/django/django/archive/master.tar.gz'
|
||||
- python: "3.4"
|
||||
env: DJANGO=1.4.22
|
||||
- python: "3.5"
|
||||
env: DJANGO=1.4.22
|
||||
- python: "3.3"
|
||||
env: DJANGO=1.5.12
|
||||
env: DJANGO_VERSION='Django>=1.4,<1.5'
|
||||
- python: "3.4"
|
||||
env: DJANGO=1.5.12
|
||||
- python: "3.5"
|
||||
env: DJANGO=1.5.12
|
||||
env: DJANGO_VERSION='Django>=1.5,<1.6'
|
||||
- python: "3.4"
|
||||
env: DJANGO=1.6.11
|
||||
env: DJANGO_VERSION='Django>=1.6,<1.7'
|
||||
- python: "3.5"
|
||||
env: DJANGO=1.6.11
|
||||
env: DJANGO_VERSION='Django>=1.4,<1.5'
|
||||
- python: "3.5"
|
||||
env: DJANGO=1.7.11
|
||||
- python: "3.3"
|
||||
env: DJANGO=1.9.9
|
||||
- python: "3.3"
|
||||
env: DJANGO=1.10.1
|
||||
env: DJANGO_VERSION='Django>=1.5,<1.6'
|
||||
- python: "3.5"
|
||||
env: DJANGO_VERSION='Django>=1.6,<1.7'
|
||||
- python: "3.5"
|
||||
env: DJANGO_VERSION='Django>=1.7,<1.8'
|
||||
allow_failures:
|
||||
- env: DJANGO_VERSION='https://github.com/django/django/archive/master.tar.gz'
|
||||
|
||||
install:
|
||||
- pip install -q Django==$DJANGO
|
||||
- pip install tox coveralls
|
||||
- pip install -q $DJANGO_VERSION
|
||||
- pip install tox coveralls flake8
|
||||
|
||||
script:
|
||||
- coverage erase
|
||||
- if [[ $(python -c 'import sys; print("0" if sys.version_info < (2, 7) else "1")') == "1" ]]; then flake8 --ignore=E501; fi
|
||||
- PYTHONPATH=. coverage run -p example/run_tests.py
|
||||
- PYTHONPATH=. coverage run -p example/run_tests.py example.settings_no_debug
|
||||
after_success:
|
||||
|
|
|
@ -22,4 +22,5 @@ from .models import Book
|
|||
class BookAdmin(admin.ModelAdmin):
|
||||
pass
|
||||
|
||||
|
||||
admin.site.register(Book, BookAdmin)
|
||||
|
|
|
@ -28,15 +28,15 @@ CATEGORY_CHOICES = (
|
|||
)
|
||||
|
||||
TAGS_CHOICES = (
|
||||
('sex', _('Sex')),
|
||||
('work', _('Work')),
|
||||
('happy', _('Happy')),
|
||||
('food', _('Food')),
|
||||
('field', _('Field')),
|
||||
('boring', _('Boring')),
|
||||
('interesting', _('Interesting')),
|
||||
('huge', _('Huge')),
|
||||
('nice', _('Nice')),
|
||||
('sex', _('Sex')), # noqa: E241
|
||||
('work', _('Work')), # noqa: E241
|
||||
('happy', _('Happy')), # noqa: E241
|
||||
('food', _('Food')), # noqa: E241
|
||||
('field', _('Field')), # noqa: E241
|
||||
('boring', _('Boring')), # noqa: E241
|
||||
('interesting', _('Interesting')), # noqa: E241
|
||||
('huge', _('Huge')), # noqa: E241
|
||||
('nice', _('Nice')), # noqa: E241
|
||||
)
|
||||
|
||||
PROVINCES = (
|
||||
|
@ -52,7 +52,7 @@ STATES = (
|
|||
|
||||
PROVINCES_AND_STATES = (
|
||||
(_("Canada - Provinces"), PROVINCES),
|
||||
(_("USA - States"), STATES),
|
||||
(_("USA - States"), STATES), # noqa: E241
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ from .models import Book, PROVINCES, STATES, PROVINCES_AND_STATES
|
|||
|
||||
|
||||
if sys.version_info < (3,):
|
||||
u = unicode
|
||||
u = unicode # noqa: F821
|
||||
else:
|
||||
u = str
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ except ImportError: # Django < 1.4
|
|||
from .views import app_index
|
||||
|
||||
|
||||
urlpatterns = patterns('',
|
||||
urlpatterns = patterns(
|
||||
'',
|
||||
url(r'^$', app_index, name='app_index'),
|
||||
)
|
||||
|
|
|
@ -191,13 +191,13 @@ INSTALLED_APPS = (
|
|||
|
||||
|
||||
# If formadmin is installed
|
||||
from django.conf import ENVIRONMENT_VARIABLE
|
||||
from django.conf import ENVIRONMENT_VARIABLE # noqa: E402
|
||||
# I check it if formadmin is installed of this way because if I execute
|
||||
# python manage.py runserver --settings=settings_no_debug
|
||||
# I get an error
|
||||
if os.environ[ENVIRONMENT_VARIABLE] == 'example.settings':
|
||||
try:
|
||||
import formadmin
|
||||
import formadmin # noqa: F401
|
||||
INSTALLED_APPS += ('formadmin',)
|
||||
except ImportError:
|
||||
pass
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from example.settings import *
|
||||
from example.settings import * # noqa: F401,F403
|
||||
|
||||
DEBUG = False
|
||||
TEMPLATE_DEBUG = DEBUG
|
||||
|
|
|
@ -42,12 +42,14 @@ js_info_dict = {
|
|||
'packages': ('django.conf',),
|
||||
}
|
||||
|
||||
urlpatterns = patterns('',
|
||||
urlpatterns = patterns(
|
||||
'',
|
||||
url(r'^', include('app.urls')),
|
||||
url(r'^admin/', include(admin.site.urls)),
|
||||
)
|
||||
|
||||
urlpatterns += patterns('',
|
||||
urlpatterns += patterns(
|
||||
'',
|
||||
url(r'^%s(?P<path>.*)$' % settings.MEDIA_URL[1:],
|
||||
serve,
|
||||
{'document_root': settings.MEDIA_ROOT, 'show_indexes': True}),
|
||||
|
|
|
@ -15,16 +15,17 @@ framework.
|
|||
"""
|
||||
import os
|
||||
|
||||
# This application object is used by any WSGI server configured to use this
|
||||
# file. This includes Django's development server, if the WSGI_APPLICATION
|
||||
# setting points here.
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
# We defer to a DJANGO_SETTINGS_MODULE already in the environment. This breaks
|
||||
# if running multiple sites in the same mod_wsgi process. To fix this, use
|
||||
# mod_wsgi daemon mode with each site in its own daemon process, or use
|
||||
# os.environ["DJANGO_SETTINGS_MODULE"] = "test_project.settings"
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "example.settings")
|
||||
|
||||
# This application object is used by any WSGI server configured to use this
|
||||
# file. This includes Django's development server, if the WSGI_APPLICATION
|
||||
# setting points here.
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
application = get_wsgi_application()
|
||||
|
||||
# Apply WSGI middleware here.
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
from multiselectfield.db.fields import MultiSelectField
|
||||
from multiselectfield.forms.fields import MultiSelectFormField
|
||||
from multiselectfield.db.fields import MultiSelectField # noqa: F401
|
||||
from multiselectfield.forms.fields import MultiSelectFormField # noqa: F401
|
||||
|
|
|
@ -22,12 +22,12 @@ from django.db import models
|
|||
from django.utils.text import capfirst
|
||||
from django.core import exceptions
|
||||
|
||||
from ..forms.fields import MultiSelectFormField, MaxChoicesValidator
|
||||
from ..forms.fields import MultiSelectFormField, MinChoicesValidator, MaxChoicesValidator
|
||||
from ..utils import get_max_length
|
||||
from ..validators import MaxValueMultiFieldValidator
|
||||
|
||||
if sys.version_info < (3,):
|
||||
string_type = unicode
|
||||
string_type = unicode # noqa: F821
|
||||
else:
|
||||
string_type = str
|
||||
|
||||
|
@ -50,10 +50,13 @@ class MultiSelectField(models.CharField):
|
|||
""" Choice values can not contain commas. """
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.min_choices = kwargs.pop('min_choices', None)
|
||||
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.min_choices is not None:
|
||||
self.validators.append(MinChoicesValidator(self.min_choices))
|
||||
if self.max_choices is not None:
|
||||
self.validators.append(MaxChoicesValidator(self.max_choices))
|
||||
|
||||
|
@ -145,6 +148,7 @@ class MultiSelectField(models.CharField):
|
|||
setattr(cls, 'get_%s_list' % self.name, get_list)
|
||||
setattr(cls, 'get_%s_display' % self.name, get_display)
|
||||
|
||||
|
||||
if VERSION < (1, 8):
|
||||
MultiSelectField = add_metaclass(models.SubfieldBase)(MultiSelectField)
|
||||
|
||||
|
|
|
@ -17,13 +17,14 @@
|
|||
from django import forms
|
||||
|
||||
from ..utils import get_max_length
|
||||
from ..validators import MaxValueMultiFieldValidator, MaxChoicesValidator
|
||||
from ..validators import MaxValueMultiFieldValidator, MinChoicesValidator, MaxChoicesValidator
|
||||
|
||||
|
||||
class MultiSelectFormField(forms.MultipleChoiceField):
|
||||
widget = forms.CheckboxSelectMultiple
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.min_choices = kwargs.pop('min_choices', None)
|
||||
self.max_choices = kwargs.pop('max_choices', None)
|
||||
self.max_length = kwargs.pop('max_length', None)
|
||||
super(MultiSelectFormField, self).__init__(*args, **kwargs)
|
||||
|
@ -31,3 +32,5 @@ class MultiSelectFormField(forms.MultipleChoiceField):
|
|||
self.validators.append(MaxValueMultiFieldValidator(self.max_length))
|
||||
if self.max_choices is not None:
|
||||
self.validators.append(MaxChoicesValidator(self.max_choices))
|
||||
if self.min_choices is not None:
|
||||
self.validators.append(MinChoicesValidator(self.min_choices))
|
||||
|
|
|
@ -18,8 +18,8 @@ import sys
|
|||
|
||||
|
||||
if sys.version_info[0] == 2:
|
||||
string = basestring
|
||||
string_type = unicode
|
||||
string = basestring # noqa: F821
|
||||
string_type = unicode # noqa: F821
|
||||
else:
|
||||
string = str
|
||||
string_type = string
|
||||
|
|
|
@ -20,9 +20,16 @@ from django.utils.translation import ugettext_lazy as _
|
|||
|
||||
|
||||
class MaxValueMultiFieldValidator(validators.MaxLengthValidator):
|
||||
clean = lambda self, x: len(','.join(x))
|
||||
code = 'max_multifield_value'
|
||||
|
||||
def clean(self, x):
|
||||
return len(','.join(x))
|
||||
|
||||
|
||||
class MinChoicesValidator(validators.MinLengthValidator):
|
||||
message = _(u'You must select a minimum of %(limit_value)d choices.')
|
||||
code = 'min_choices'
|
||||
|
||||
|
||||
class MaxChoicesValidator(validators.MaxLengthValidator):
|
||||
message = _(u'You must select a maximum of %(limit_value)d choices.')
|
||||
|
|
2
setup.py
2
setup.py
|
@ -23,6 +23,7 @@ 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.1.4",
|
||||
|
@ -51,6 +52,7 @@ setup(
|
|||
'django>=1.4',
|
||||
'tox',
|
||||
'coverage',
|
||||
'flake8',
|
||||
],
|
||||
install_requires=[
|
||||
'django>=1.4',
|
||||
|
|
43
tox.ini
43
tox.ini
|
@ -1,11 +1,12 @@
|
|||
[tox]
|
||||
envlist = py26-dj14,py27-dj14,py26-dj15,py27-dj15,py26-dj16,py27-dj16,py33-dj16,py27-dj17,py33-dj17,py34-dj17,py27-dj18,py33-dj18,py34-dj18,py35-dj18,py27-dj19,py34-dj19,py35-dj19,py27-dj110,py34-dj110,py35-dj110
|
||||
envlist = py26-dj14,py26-dj15,py26-dj16,py27-dj14,py27-dj15,py27-dj16,py27-dj17,py27-dj18,py27-dj19,py27-dj110,py33-dj16,py33-dj17,py33-dj18,py34-dj17,py34-dj18,py34-dj19,py34-dj110,py35-dj18,py35-dj19,py35-dj110
|
||||
|
||||
[testenv]
|
||||
usedevelop = True
|
||||
setenv =
|
||||
PYTHONPATH=.
|
||||
commands =
|
||||
python {envbindir}/flake8 --ignore=E501
|
||||
python {envbindir}/coverage run -p example/run_tests.py
|
||||
python {envbindir}/coverage run -p example/run_tests.py example.settings_no_debug
|
||||
install_command =
|
||||
|
@ -18,6 +19,7 @@ deps =
|
|||
pillow==1.7.8
|
||||
PyYAML==3.10
|
||||
coveralls==0.3
|
||||
flake8
|
||||
|
||||
[testenv:py26-dj15]
|
||||
basepython = python2.6
|
||||
|
@ -26,6 +28,7 @@ deps =
|
|||
pillow==1.7.8
|
||||
PyYAML==3.10
|
||||
coveralls==0.3
|
||||
flake8
|
||||
|
||||
[testenv:py26-dj16]
|
||||
basepython = python2.6
|
||||
|
@ -34,6 +37,7 @@ deps =
|
|||
pillow==1.7.8
|
||||
PyYAML==3.10
|
||||
coveralls==0.3
|
||||
flake8
|
||||
|
||||
|
||||
[testenv:py27-dj14]
|
||||
|
@ -43,6 +47,7 @@ deps =
|
|||
pillow==1.7.8
|
||||
PyYAML==3.10
|
||||
coveralls==0.3
|
||||
flake8
|
||||
|
||||
[testenv:py27-dj15]
|
||||
basepython = python2.7
|
||||
|
@ -51,6 +56,7 @@ deps =
|
|||
pillow==1.7.8
|
||||
PyYAML==3.10
|
||||
coveralls==0.3
|
||||
flake8
|
||||
|
||||
[testenv:py27-dj16]
|
||||
basepython = python2.7
|
||||
|
@ -59,6 +65,7 @@ deps =
|
|||
pillow==1.7.8
|
||||
PyYAML==3.10
|
||||
coveralls==0.3
|
||||
flake8
|
||||
|
||||
[testenv:py27-dj17]
|
||||
basepython = python2.7
|
||||
|
@ -67,30 +74,34 @@ deps =
|
|||
pillow==1.7.8
|
||||
PyYAML==3.10
|
||||
coveralls==0.3
|
||||
flake8
|
||||
|
||||
[testenv:py27-dj18]
|
||||
basepython = python2.7
|
||||
deps =
|
||||
django==1.8.14
|
||||
django==1.8.17
|
||||
pillow==1.7.8
|
||||
PyYAML==3.10
|
||||
coveralls==0.3
|
||||
flake8
|
||||
|
||||
[testenv:py27-dj19]
|
||||
basepython = python2.7
|
||||
deps =
|
||||
django==1.9.9
|
||||
django==1.9.12
|
||||
pillow==1.7.8
|
||||
PyYAML==3.10
|
||||
coveralls==0.3
|
||||
flake8
|
||||
|
||||
[testenv:py27-dj110]
|
||||
basepython = python2.7
|
||||
deps =
|
||||
django==1.10.1
|
||||
django==1.10.4
|
||||
pillow==1.7.8
|
||||
PyYAML==3.10
|
||||
coveralls==0.3
|
||||
flake8
|
||||
|
||||
|
||||
[testenv:py33-dj16]
|
||||
|
@ -100,6 +111,7 @@ deps =
|
|||
pillow==2.1.0
|
||||
PyYAML==3.10
|
||||
coveralls==0.3
|
||||
flake8
|
||||
|
||||
[testenv:py33-dj17]
|
||||
basepython = python3.3
|
||||
|
@ -108,14 +120,16 @@ deps =
|
|||
pillow==2.1.0
|
||||
PyYAML==3.10
|
||||
coveralls==0.3
|
||||
flake8
|
||||
|
||||
[testenv:py33-dj18]
|
||||
basepython = python3.3
|
||||
deps =
|
||||
django==1.8.14
|
||||
django==1.8.17
|
||||
pillow==2.1.0
|
||||
PyYAML==3.10
|
||||
coveralls==0.3
|
||||
flake8
|
||||
|
||||
|
||||
[testenv:py34-dj17]
|
||||
|
@ -125,52 +139,59 @@ deps =
|
|||
pillow==2.1.0
|
||||
PyYAML==3.10
|
||||
coveralls==0.3
|
||||
flake8
|
||||
|
||||
[testenv:py34-dj18]
|
||||
basepython = python3.4
|
||||
deps =
|
||||
django==1.8.14
|
||||
django==1.8.17
|
||||
pillow==2.1.0
|
||||
PyYAML==3.10
|
||||
coveralls==0.3
|
||||
flake8
|
||||
|
||||
[testenv:py34-dj19]
|
||||
basepython = python3.4
|
||||
deps =
|
||||
django==1.9.9
|
||||
django==1.9.12
|
||||
pillow==2.1.0
|
||||
PyYAML==3.10
|
||||
coveralls==0.3
|
||||
flake8
|
||||
|
||||
[testenv:py34-dj110]
|
||||
basepython = python3.4
|
||||
deps =
|
||||
django==1.10.1
|
||||
django==1.10.4
|
||||
pillow==2.1.0
|
||||
PyYAML==3.10
|
||||
coveralls==0.3
|
||||
flake8
|
||||
|
||||
|
||||
[testenv:py35-dj18]
|
||||
basepython = python3.5
|
||||
deps =
|
||||
django==1.8.14
|
||||
django==1.8.17
|
||||
pillow==2.1.0
|
||||
PyYAML==3.10
|
||||
coveralls==0.3
|
||||
flake8
|
||||
|
||||
[testenv:py35-dj19]
|
||||
basepython = python3.5
|
||||
deps =
|
||||
django==1.9.9
|
||||
django==1.9.12
|
||||
pillow==2.1.0
|
||||
PyYAML==3.10
|
||||
coveralls==0.3
|
||||
flake8
|
||||
|
||||
[testenv:py35-dj110]
|
||||
basepython = python3.5
|
||||
deps =
|
||||
django==1.10.1
|
||||
django==1.10.4
|
||||
pillow==2.1.0
|
||||
PyYAML==3.10
|
||||
coveralls==0.3
|
||||
flake8
|
||||
|
|
Loading…
Reference in a new issue