Merge pull request #99 from goinnn/add-back-compatibility

Add back compatibility
This commit is contained in:
tomasgarzon 2019-10-04 10:45:12 +02:00 committed by GitHub
commit 66266dd30b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 264 additions and 57 deletions

2
.gitignore vendored
View file

@ -10,3 +10,5 @@ dist
.coverage* .coverage*
.env .env
example/example.db example/example.db
.tox

View file

@ -5,25 +5,79 @@ python:
- "3.4" - "3.4"
- "3.5" - "3.5"
- "3.6" - "3.6"
- "3.7"
env: env:
- 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='Django>=1.11,<2.0' - DJANGO_VERSION='Django>=1.11,<2.0'
- DJANGO_VERSION='Django>=2.0,<2.1' - DJANGO_VERSION='Django>=2.0,<2.1'
- DJANGO_VERSION='Django>=2.1,<2.2' - DJANGO_VERSION='Django>=2.1,<2.2'
- DJANGO_VERSION='https://github.com/django/django/archive/master.tar.gz' - DJANGO_VERSION='https://github.com/django/django/archive/master.tar.gz'
matrix: matrix:
exclude: exclude:
- python: "2.7"
env: DJANGO_VERSION='https://github.com/django/django/archive/master.tar.gz'
- python: "2.7"
env: DJANGO_VERSION='Django>=2.1,<2.2'
- python: "2.7" - python: "2.7"
env: DJANGO_VERSION='Django>=2.0,<2.1' env: DJANGO_VERSION='Django>=2.0,<2.1'
- python: "2.7" - python: "2.7"
env: DJANGO_VERSION='Django>=2.1,<2.2' env: DJANGO_VERSION='Django>=1.6,<1.7'
- python: "2.7" - python: "3.4"
env: DJANGO_VERSION='https://github.com/django/django/archive/master.tar.gz' env: DJANGO_VERSION='Django>=1.4,<1.5'
- python: "3.4"
env: DJANGO_VERSION='Django>=1.5,<1.6'
- python: "3.4"
env: DJANGO_VERSION='Django>=1.6,<1.7'
- python: "3.4" - python: "3.4"
env: DJANGO_VERSION='Django>=2.1,<2.2' env: DJANGO_VERSION='Django>=2.1,<2.2'
- python: "2.7" - python: "3.4"
env: DJANGO_VERSION='https://github.com/django/django/archive/master.tar.gz' env: DJANGO_VERSION='https://github.com/django/django/archive/master.tar.gz'
allow_failures: - python: "3.5"
- env: DJANGO_VERSION='https://github.com/django/django/archive/master.tar.gz' env: DJANGO_VERSION='Django>=1.4,<1.5'
- python: "3.5"
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'
- python: "3.5"
env: DJANGO_VERSION='Django>=2.1,<2.2'
- python: "3.5"
env: DJANGO_VERSION='https://github.com/django/django/archive/master.tar.gz'
- python: "3.6"
env: DJANGO_VERSION='Django>=1.4,<1.5'
- python: "3.6"
env: DJANGO_VERSION='Django>=1.5,<1.6'
- python: "3.6"
env: DJANGO_VERSION='Django>=1.6,<1.7'
- python: "3.6"
env: DJANGO_VERSION='Django>=1.7,<1.8'
- python: "3.6"
env: DJANGO_VERSION='Django>=1.8,<1.9'
- python: "3.6"
env: DJANGO_VERSION='Django>=1.9,<1.10'
- python: "3.6"
env: DJANGO_VERSION='Django>=1.10,<1.11'
- python: "3.7"
env: DJANGO_VERSION='Django>=1.4,<1.5'
- python: "3.7"
env: DJANGO_VERSION='Django>=1.5,<1.6'
- python: "3.7"
env: DJANGO_VERSION='Django>=1.6,<1.7'
- python: "3.7"
env: DJANGO_VERSION='Django>=1.7,<1.8'
- python: "3.7"
env: DJANGO_VERSION='Django>=1.8,<1.9'
- python: "3.7"
env: DJANGO_VERSION='Django>=1.9,<1.10'
- python: "3.7"
env: DJANGO_VERSION='Django>=1.10,<1.11'
install: install:
- pip install -q $DJANGO_VERSION - pip install -q $DJANGO_VERSION

View file

@ -1,3 +1,12 @@
0.1.9 (2019-10-02)
------------------
* Added support for Django 2
* Added support for Python 3.6
* Drop support for Python (2.6, 3.3)
* Thanks to:
* `hirokinko <https://github.com/hirokinko>`_
0.1.6 (2017-05-10) 0.1.6 (2017-05-10)
------------------ ------------------
@ -64,7 +73,7 @@
* Test/example project * Test/example project
* Now works if the first composant of the list of tuple is an integer * Now works if the first composant of the list of tuple is an integer
* Now max_length is not required, the Multiselect field calculate it automatically. * Now max_length is not required, the Multiselect field calculate it automatically.
* The max_choices attr can be a attr in the model field * The max_choices attr can be a attr in the model field
* Refactor the code * Refactor the code
* Spanish translations * Spanish translations
@ -88,4 +97,4 @@
0.0.1 (2012-09-27) 0.0.1 (2012-09-27)
------------------ ------------------
* Initial version from the next `snippet <http://djangosnippets.org/snippets/1200/>`_ * Initial version from the next `snippet <http://djangosnippets.org/snippets/1200/>`_

View file

@ -16,7 +16,7 @@ This egg is inspired by this `snippet <http://djangosnippets.org/snippets/1200/>
Supported Python versions: 2.7, 3.4+ Supported Python versions: 2.7, 3.4+
Supported Django versions: 1.11-2.0+ Supported Django versions: 1.4-2.0+
Installation Installation
============ ============
@ -35,25 +35,25 @@ Configure your models.py
.. code-block:: python .. code-block:: python
from multiselectfield import MultiSelectField from multiselectfield import MultiSelectField
# ... # ...
MY_CHOICES = (('item_key1', 'Item title 1.1'), MY_CHOICES = (('item_key1', 'Item title 1.1'),
('item_key2', 'Item title 1.2'), ('item_key2', 'Item title 1.2'),
('item_key3', 'Item title 1.3'), ('item_key3', 'Item title 1.3'),
('item_key4', 'Item title 1.4'), ('item_key4', 'Item title 1.4'),
('item_key5', 'Item title 1.5')) ('item_key5', 'Item title 1.5'))
MY_CHOICES2 = ((1, 'Item title 2.1'), MY_CHOICES2 = ((1, 'Item title 2.1'),
(2, 'Item title 2.2'), (2, 'Item title 2.2'),
(3, 'Item title 2.3'), (3, 'Item title 2.3'),
(4, 'Item title 2.4'), (4, 'Item title 2.4'),
(5, 'Item title 2.5')) (5, 'Item title 2.5'))
class MyModel(models.Model): class MyModel(models.Model):
# ..... # .....
my_field = MultiSelectField(choices=MY_CHOICES) my_field = MultiSelectField(choices=MY_CHOICES)
my_field2 = MultiSelectField(choices=MY_CHOICES2, my_field2 = MultiSelectField(choices=MY_CHOICES2,
max_choices=3, max_choices=3,
@ -103,7 +103,7 @@ Django REST Framework comes with a ``MultipleChoiceField`` that works perfectly
.. code-block:: python .. code-block:: python
from rest_framework import fields, serializers from rest_framework import fields, serializers
from myapp.models import MY_CHOICES, MY_CHOICES2 from myapp.models import MY_CHOICES, MY_CHOICES2
class MyModelSerializer(serializers.HyperlinkedModelSerializer): class MyModelSerializer(serializers.HyperlinkedModelSerializer):

View file

@ -32,8 +32,12 @@ else:
u = str u = str
def get_field(model, name): if VERSION < (1, 9):
return model._meta.get_field(name) def get_field(model, name):
return model._meta.get_field_by_name(name)[0]
else:
def get_field(model, name):
return model._meta.get_field(name)
class MultiSelectTestCase(TestCase): class MultiSelectTestCase(TestCase):
@ -73,8 +77,12 @@ class MultiSelectTestCase(TestCase):
# call Field.from_db_field, it simply returns a Python representation # call Field.from_db_field, it simply returns a Python representation
# of the data in the database (which in our case is a string of # of the data in the database (which in our case is a string of
# comma-separated values). The bug was fixed in Django 1.8+. # comma-separated values). The bug was fixed in Django 1.8+.
self.assertListEqual(tag_list_list, [['sex', 'work', 'happy']]) if VERSION >= (1, 6) and VERSION < (1, 8):
self.assertListEqual(categories_list_list, [['1', '3', '5']]) self.assertStringEqual(tag_list_list, [u('sex,work,happy')])
self.assertStringEqual(categories_list_list, [u('1,3,5')])
else:
self.assertListEqual(tag_list_list, [['sex', 'work', 'happy']])
self.assertListEqual(categories_list_list, [['1', '3', '5']])
def test_form(self): def test_form(self):
form_class = modelform_factory(Book, fields=('title', 'tags', 'categories')) form_class = modelform_factory(Book, fields=('title', 'tags', 'categories'))
@ -131,17 +139,28 @@ class MultiSelectTestCase(TestCase):
self.assertEqual(len(form_class.base_fields), 1) self.assertEqual(len(form_class.base_fields), 1)
form = form_class(initial={'published_in': ['BC', 'AK']}) form = form_class(initial={'published_in': ['BC', 'AK']})
expected_html = u("""<p><label>Province or State:</label> <ul id="id_published_in"><li>Canada - Provinces<ul id="id_published_in_0"><li><label for="id_published_in_0_0"><input id="id_published_in_0_0" name="published_in" type="checkbox" value="AB" /> Alberta</label></li>\n""" expected_html = u("""<p><label for="id_published_in_0">Province or State:</label> <ul id="id_published_in"><li>Canada - Provinces<ul id="id_published_in_0"><li><label for="id_published_in_0_0"><input id="id_published_in_0_0" name="published_in" type="checkbox" value="AB" /> Alberta</label></li>\n"""
"""<li><label for="id_published_in_0_1"><input checked="checked" id="id_published_in_0_1" name="published_in" type="checkbox" value="BC" /> British Columbia</label></li></ul></li>\n""" """<li><label for="id_published_in_0_1"><input checked="checked" id="id_published_in_0_1" name="published_in" type="checkbox" value="BC" /> British Columbia</label></li></ul></li>\n"""
"""<li>USA - States<ul id="id_published_in_1"><li><label for="id_published_in_1_0"><input checked="checked" id="id_published_in_1_0" name="published_in" type="checkbox" value="AK" /> Alaska</label></li>\n""" """<li>USA - States<ul id="id_published_in_1"><li><label for="id_published_in_1_0"><input checked="checked" id="id_published_in_1_0" name="published_in" type="checkbox" value="AK" /> Alaska</label></li>\n"""
"""<li><label for="id_published_in_1_1"><input id="id_published_in_1_1" name="published_in" type="checkbox" value="AL" /> Alabama</label></li>\n""" """<li><label for="id_published_in_1_1"><input id="id_published_in_1_1" name="published_in" type="checkbox" value="AL" /> Alabama</label></li>\n"""
"""<li><label for="id_published_in_1_2"><input id="id_published_in_1_2" name="published_in" type="checkbox" value="AZ" /> Arizona</label></li></ul></li></ul></p>""") """<li><label for="id_published_in_1_2"><input id="id_published_in_1_2" name="published_in" type="checkbox" value="AZ" /> Arizona</label></li></ul></li></ul></p>""")
actual_html = form.as_p() actual_html = form.as_p()
if (1, 11) <= VERSION:
# Django 1.11+ does not assign 'for' attributes on labels if they
# are group labels
expected_html = expected_html.replace('label for="id_published_in_0"', 'label')
if VERSION < (1, 6):
# Django 1.6 renders the Python repr() for each group (eg: tuples
# with HTML entities), so we skip the test for that version
self.assertEqual(expected_html.replace('\n', ''), actual_html.replace('\n', ''))
if VERSION >= (2, 0): if VERSION >= (2, 0):
expected_html = expected_html.replace('input checked="checked"', 'input checked') expected_html = expected_html.replace('input checked="checked"', 'input checked')
if VERSION >= (1, 7):
self.assertHTMLEqual(expected_html, actual_html)
self.assertHTMLEqual(expected_html, actual_html) self.assertHTMLEqual(expected_html, actual_html)

View file

@ -14,14 +14,17 @@
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with this software. If not, see <http://www.gnu.org/licenses/>. # along with this software. If not, see <http://www.gnu.org/licenses/>.
from django import VERSION
from django.conf import settings from django.conf import settings
from django.contrib.auth import login from django.contrib.auth import login
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.urls import reverse
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
if VERSION >= (2, 0):
from django.urls import reverse
else:
from django.core.urlresolvers import reverse
def app_index(request): def app_index(request):
user = get_user_model().objects.get(username='admin') user = get_user_model().objects.get(username='admin')

View file

@ -181,7 +181,7 @@ INSTALLED_APPS = (
'django.contrib.messages', 'django.contrib.messages',
'django.contrib.staticfiles', 'django.contrib.staticfiles',
# Uncomment the next line to enable the admin: # Uncomment the next line to enable the admin:
'django.contrib.admin', # 'django.contrib.admin',
# Uncomment the next line to enable admin documentation: # Uncomment the next line to enable admin documentation:
# 'django.contrib.admindocs', # 'django.contrib.admindocs',

View file

@ -14,33 +14,54 @@
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with this software. If not, see <http://www.gnu.org/licenses/>. # along with this software. If not, see <http://www.gnu.org/licenses/>.
from django import VERSION
from django.conf import settings from django.conf import settings
try:
from django.conf.urls import include, url
except ImportError:
from django.urls import include, url
from django.contrib import admin
from django.views.static import serve from django.views.static import serve
admin.autodiscover() try:
from django.conf.urls import include, url
# Compatibility for Django > 1.8
def patterns(prefix, *args):
if VERSION < (1, 9):
from django.conf.urls import patterns as django_patterns
return django_patterns(prefix, *args)
elif prefix != '':
raise NotImplementedError("You need to update your URLConf for "
"Django 1.10, or tweak it to remove the "
"prefix parameter")
else:
return list(args)
except ImportError: # Django < 1.4
if VERSION < (1, 4):
from django.conf.urls.defaults import include, patterns, url
else:
from django.urls import include, url
js_info_dict = { js_info_dict = {
'packages': ('django.conf',), 'packages': ('django.conf',),
} }
urlpatterns = [ if VERSION < (1, 11):
url(r'^', include('app.urls')), urlpatterns = patterns(
url(r'^admin/', admin.site.urls), '',
] url(r'^', include('app.urls')),
)
urlpatterns += [ urlpatterns += patterns(
url( '',
r'^%s(?P<path>.*)$' % settings.MEDIA_URL[1:], url(r'^%s(?P<path>.*)$' % settings.MEDIA_URL[1:]),
serve, )
{ else:
'document_root': settings.MEDIA_ROOT, urlpatterns = [
'show_indexes': True, url(r'^', include('app.urls')),
}, url(
), r'^%s(?P<path>.*)$' % settings.MEDIA_URL[1:],
] serve,
{
'document_root': settings.MEDIA_ROOT,
'show_indexes': True,
},
),
]

View file

@ -19,6 +19,7 @@
import os import os
import sys import sys
import django
from django.conf import ENVIRONMENT_VARIABLE from django.conf import ENVIRONMENT_VARIABLE
from django.core import management from django.core import management
from django.core.wsgi import get_wsgi_application from django.core.wsgi import get_wsgi_application
@ -29,6 +30,10 @@ if len(sys.argv) == 1:
else: else:
os.environ[ENVIRONMENT_VARIABLE] = sys.argv[1] os.environ[ENVIRONMENT_VARIABLE] = sys.argv[1]
application = get_wsgi_application() if django.VERSION[0] == 1 and django.VERSION[1] >= 7:
from django.core.wsgi import get_wsgi_application as get_wsgi_application_v1
application = get_wsgi_application_v1()
else:
application = get_wsgi_application()
management.call_command('test', 'app') management.call_command('test', 'app')

View file

@ -20,7 +20,6 @@ from django import VERSION
from django.db import models from django.db import models
from django.utils.text import capfirst from django.utils.text import capfirst
from django.utils.encoding import python_2_unicode_compatible
from django.core import exceptions from django.core import exceptions
from ..forms.fields import MultiSelectFormField, MinChoicesValidator, MaxChoicesValidator from ..forms.fields import MultiSelectFormField, MinChoicesValidator, MaxChoicesValidator
@ -47,7 +46,6 @@ def add_metaclass(metaclass):
return wrapper return wrapper
@python_2_unicode_compatible
class MSFList(list): class MSFList(list):
def __init__(self, choices, *args, **kwargs): def __init__(self, choices, *args, **kwargs):
@ -58,6 +56,10 @@ class MSFList(list):
msg_list = [msgl.choices.get(int(i)) if i.isdigit() else msgl.choices.get(i) for i in msgl] msg_list = [msgl.choices.get(int(i)) if i.isdigit() else msgl.choices.get(i) for i in msgl]
return u', '.join([string_type(s) for s in msg_list]) return u', '.join([string_type(s) for s in msg_list])
if sys.version_info < (3,):
def __unicode__(self, msgl):
return self.__str__(msgl)
class MultiSelectField(models.CharField): class MultiSelectField(models.CharField):
""" Choice values can not contain commas. """ """ Choice values can not contain commas. """
@ -102,7 +104,10 @@ class MultiSelectField(models.CharField):
return choices_selected return choices_selected
def value_to_string(self, obj): def value_to_string(self, obj):
value = super(MultiSelectField, self).value_from_object(obj) try:
value = self._get_val_from_obj(obj)
except AttributeError:
value = super(MultiSelectField, self).value_from_object(obj)
return self.get_prep_value(value) return self.get_prep_value(value)
def validate(self, value, model_instance): def validate(self, value, model_instance):

View file

@ -31,7 +31,7 @@ def read(*rnames):
setup( setup(
name="django-multiselectfield", name="django-multiselectfield",
version="0.1.8", version="0.1.9",
author="Pablo Martin", author="Pablo Martin",
author_email="goinnn@gmail.com", author_email="goinnn@gmail.com",
description="Django multiple select field", description="Django multiple select field",
@ -41,8 +41,10 @@ setup(
'Framework :: Django', 'Framework :: Django',
'License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)', 'License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)',
'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.6',
@ -54,13 +56,13 @@ setup(
packages=find_packages(), packages=find_packages(),
include_package_data=True, include_package_data=True,
tests_require=[ tests_require=[
'django>=1.11', 'django>=1.4',
'tox', 'tox',
'coverage', 'coverage',
'flake8', 'flake8',
], ],
install_requires=[ install_requires=[
'django>=1.11', 'django>=1.4',
], ],
zip_safe=False, zip_safe=False,
) )

95
tox.ini
View file

@ -1,5 +1,5 @@
[tox] [tox]
envlist = py{27,34,35,36}-dj111,py{34,35,36,37}-dj20,py{35,36,37}-dj21 envlist = py{27,34,35,36}-dj{14,15,16,16,17,18,19,110,111},py{34,35,36,37}-dj{14,15,16,16,17,18,19,110,111,20},py{35,36,37}-dj{21,22},
[testenv] [testenv]
usedevelop = True usedevelop = True
@ -12,6 +12,69 @@ commands =
install_command = install_command =
pip install {opts} {packages} pip install {opts} {packages}
[testenv:py27-dj14]
basepython = python2.7
deps =
django==1.4.22
pillow==1.7.8
PyYAML==3.10
coveralls==0.3
flake8
[testenv:py27-dj15]
basepython = python2.7
deps =
django==1.5.12
pillow==1.7.8
PyYAML==3.10
coveralls==0.3
flake8
[testenv:py27-dj16]
basepython = python2.7
deps =
django==1.6.11
pillow==1.7.8
PyYAML==3.10
coveralls==0.3
flake8
[testenv:py27-dj17]
basepython = python2.7
deps =
django==1.7.11
pillow==1.7.8
PyYAML==3.10
coveralls==0.3
flake8
[testenv:py27-dj18]
basepython = python2.7
deps =
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.12
pillow==1.7.8
PyYAML==3.10
coveralls==0.3
flake8
[testenv:py27-dj110]
basepython = python2.7
deps =
django==1.10.4
pillow==1.7.8
PyYAML==3.10
coveralls==0.3
flake8
[testenv:py27-dj111] [testenv:py27-dj111]
basepython = python2.7 basepython = python2.7
deps = deps =
@ -21,7 +84,6 @@ deps =
coveralls==0.3 coveralls==0.3
flake8 flake8
[testenv:py34-dj111] [testenv:py34-dj111]
basepython = python3.4 basepython = python3.4
deps = deps =
@ -40,7 +102,6 @@ deps =
coveralls==0.3 coveralls==0.3
flake8 flake8
[testenv:py35-dj111] [testenv:py35-dj111]
basepython = python3.5 basepython = python3.5
deps = deps =
@ -68,6 +129,15 @@ deps =
coveralls==0.3 coveralls==0.3
flake8 flake8
[testenv:py35-dj22]
basepython = python3.5
deps =
django==2.2.1
pillow==2.1.0
PyYAML==3.13
coveralls==0.3
flake8
[testenv:py36-dj111] [testenv:py36-dj111]
basepython = python3.6 basepython = python3.6
deps = deps =
@ -95,6 +165,14 @@ deps =
coveralls==0.3 coveralls==0.3
flake8 flake8
[testenv:py36-dj22]
basepython = python3.6
deps =
django==2.2.1
pillow==2.1.0
PyYAML==3.13
coveralls==0.3
flake8
[testenv:py37-dj20] [testenv:py37-dj20]
basepython = python3.7 basepython = python3.7
@ -112,4 +190,13 @@ deps =
pillow==2.1.0 pillow==2.1.0
PyYAML==3.13 PyYAML==3.13
coveralls==0.3 coveralls==0.3
flake8 flake8
[testenv:py37-dj22]
basepython = python3.7
deps =
django==2.2.1
pillow==2.1.0
PyYAML==3.13
coveralls==0.3
flake8