diff --git a/.travis.yml b/.travis.yml index 48faa38..164ce64 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,16 +4,15 @@ sudo: false env: - - TOX_ENV=py27-django18 - - TOX_ENV=py27-django19 - - TOX_ENV=py27-django110 - - TOX_ENV=py34-django18 - - TOX_ENV=py34-django19 - - TOX_ENV=py34-django110 + - TOX_ENV=py27-django111 + - TOX_ENV=py36-django111 + - TOX_ENV=py36-django20 + - TOX_ENV=py36-django21 + - TOX_ENV=py36-django22 install: - pip install tox script: -- tox -e $TOX_ENV \ No newline at end of file +- tox -e $TOX_ENV diff --git a/custom_field/admin.py b/custom_field/admin.py index c45fb04..3748894 100644 --- a/custom_field/admin.py +++ b/custom_field/admin.py @@ -5,7 +5,9 @@ class CFAdmin(CustomFieldAdmin): - list_display = ('content_type', 'name') - list_filter = ('content_type',) - search_fields = ('content_type__name', 'name') + list_display = ("content_type", "name") + list_filter = ("content_type",) + search_fields = ("content_type__name", "name") + + admin.site.register(CustomField, CFAdmin) diff --git a/custom_field/custom_field.py b/custom_field/custom_field.py index 81cde67..9b56033 100644 --- a/custom_field/custom_field.py +++ b/custom_field/custom_field.py @@ -17,18 +17,22 @@ class CustomFieldModel(object): """ Abstract class adds some helper functions a Model """ + @property def get_custom_fields(self): """ Return a list of custom fields for this model """ return CustomField.objects.filter( - content_type=ContentType.objects.get_for_model(self)) + content_type=ContentType.objects.get_for_model(self) + ) def get_model_custom_fields(self): """ Return a list of custom fields for this model, directly callable without an instance. Use like Foo.get_model_custom_fields(Foo) """ return CustomField.objects.filter( - content_type=ContentType.objects.get_for_model(self)) + content_type=ContentType.objects.get_for_model(self) + ) + get_model_custom_fields = Callable(get_model_custom_fields) def get_custom_field(self, field_name): @@ -36,8 +40,7 @@ def get_custom_field(self, field_name): field_name - Name of the custom field you want. """ content_type = ContentType.objects.get_for_model(self) - return CustomField.objects.get( - content_type=content_type, name=field_name) + return CustomField.objects.get(content_type=content_type, name=field_name) def get_custom_value(self, field_name): """ Get a value for a specified custom field @@ -45,7 +48,8 @@ def get_custom_value(self, field_name): """ custom_field = self.get_custom_field(field_name) return CustomFieldValue.objects.get_or_create( - field=custom_field, object_id=self.id)[0].value + field=custom_field, object_id=self.id + )[0].value def set_custom_value(self, field_name, value): """ Set a value for a specified custom field @@ -54,7 +58,8 @@ def set_custom_value(self, field_name, value): """ custom_field = self.get_custom_field(field_name) custom_value = CustomFieldValue.objects.get_or_create( - field=custom_field, object_id=self.id)[0] + field=custom_field, object_id=self.id + )[0] custom_value.value = value custom_value.save() @@ -64,7 +69,7 @@ def __init__(self, *args, **kwargs): super(CustomFieldValueForm, self).__init__(*args, **kwargs) if self.instance: try: - self.fields['value'] = self.instance.get_form_field() + self.fields["value"] = self.instance.get_form_field() except ObjectDoesNotExist: pass @@ -73,8 +78,8 @@ class CustomInline(admin.GenericTabularInline): model = CustomFieldValue form = CustomFieldValueForm can_delete = False - readonly_fields = ('field',) - fields = ('field', 'value') + readonly_fields = ("field",) + fields = ("field", "value") extra = 0 max_num = 0 @@ -87,6 +92,7 @@ class CustomFieldAdmin(ModelAdmin): """ Abstract class addes functionality to deal with custom fields in Django admin. """ + inlines = () def change_view(self, request, object_id, *args, **kwargs): @@ -95,26 +101,23 @@ def change_view(self, request, object_id, *args, **kwargs): inlines.append(CustomInline) self.inlines = inlines return super(CustomFieldAdmin, self).change_view( - request, object_id, *args, **kwargs) + request, object_id, *args, **kwargs + ) def get_form(self, request, obj=None, **kwargs): if obj: content_type = ContentType.objects.get_for_model(obj) - custom_fields = CustomField.objects.filter( - content_type=content_type) + custom_fields = CustomField.objects.filter(content_type=content_type) for custom_field in custom_fields: try: field_value, created = CustomFieldValue.objects.get_or_create( - content_type=content_type, - object_id=obj.id, - field=custom_field, + content_type=content_type, object_id=obj.id, field=custom_field, ) except IntegrityError: # This can happen because content_type is really a # cache field and didn't always exist field_value, created = CustomFieldValue.objects.get_or_create( - object_id=obj.id, - field=custom_field, + object_id=obj.id, field=custom_field, ) field_value.content_type = content_type field_value.save() diff --git a/custom_field/migrations/0001_initial.py b/custom_field/migrations/0001_initial.py index e141521..07e1ed1 100644 --- a/custom_field/migrations/0001_initial.py +++ b/custom_field/migrations/0001_initial.py @@ -1,48 +1,98 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('contenttypes', '0001_initial'), + ("contenttypes", "0001_initial"), ] operations = [ migrations.CreateModel( - name='CustomField', + name="CustomField", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('name', models.CharField(max_length=75)), - ('field_type', models.CharField(default=b't', max_length=1, choices=[(b't', b'Text'), (b'i', b'Integer'), (b'b', b'Boolean (Yes/No)')])), - ('default_value', models.CharField(help_text=b'You may leave blank. For Boolean use True or False', max_length=255, blank=True)), - ('content_type', models.ForeignKey(to='contenttypes.ContentType')), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("name", models.CharField(max_length=75)), + ( + "field_type", + models.CharField( + default=b"t", + max_length=1, + choices=[ + (b"t", b"Text"), + (b"i", b"Integer"), + (b"b", b"Boolean (Yes/No)"), + ], + ), + ), + ( + "default_value", + models.CharField( + help_text=b"You may leave blank. For Boolean use True or False", + max_length=255, + blank=True, + ), + ), + ( + "content_type", + models.ForeignKey( + to="contenttypes.ContentType", on_delete=models.CASCADE + ), + ), ], - options={ - }, + options={}, bases=(models.Model,), ), migrations.CreateModel( - name='CustomFieldValue', + name="CustomFieldValue", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('value', models.CharField(max_length=255, null=True, blank=True)), - ('object_id', models.PositiveIntegerField()), - ('content_type', models.ForeignKey(blank=True, to='contenttypes.ContentType', null=True)), - ('field', models.ForeignKey(related_name='instance', to='custom_field.CustomField')), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("value", models.CharField(max_length=255, null=True, blank=True)), + ("object_id", models.PositiveIntegerField()), + ( + "content_type", + models.ForeignKey( + blank=True, + to="contenttypes.ContentType", + null=True, + on_delete=models.CASCADE, + ), + ), + ( + "field", + models.ForeignKey( + related_name="instance", + to="custom_field.CustomField", + on_delete=models.CASCADE, + ), + ), ], - options={ - }, + options={}, bases=(models.Model,), ), migrations.AlterUniqueTogether( - name='customfieldvalue', - unique_together=set([('field', 'object_id')]), + name="customfieldvalue", unique_together=set([("field", "object_id")]), ), migrations.AlterUniqueTogether( - name='customfield', - unique_together=set([('name', 'content_type')]), + name="customfield", unique_together=set([("name", "content_type")]), ), ] diff --git a/custom_field/migrations/0002_auto_20150119_2032.py b/custom_field/migrations/0002_auto_20150119_2032.py index 7d358f6..7283507 100644 --- a/custom_field/migrations/0002_auto_20150119_2032.py +++ b/custom_field/migrations/0002_auto_20150119_2032.py @@ -7,43 +7,63 @@ class Migration(migrations.Migration): dependencies = [ - ('custom_field', '0001_initial'), + ("custom_field", "0001_initial"), ] operations = [ migrations.AddField( - model_name='customfield', - name='field_choices', - field=models.CharField(help_text=b'List the choices you want displayed, seperated by commas. This is only valid for Dropdown, Multiple, and Checkbox field types', max_length=2000, blank=True), + model_name="customfield", + name="field_choices", + field=models.CharField( + help_text=b"List the choices you want displayed, seperated by commas. This is only valid for Dropdown, Multiple, and Checkbox field types", + max_length=2000, + blank=True, + ), preserve_default=True, ), migrations.AddField( - model_name='customfield', - name='is_required', + model_name="customfield", + name="is_required", field=models.BooleanField(default=False), preserve_default=True, ), migrations.AlterField( - model_name='customfield', - name='default_value', - field=models.CharField(help_text=b'You may leave blank. For Boolean use True or False', max_length=5000, blank=True), + model_name="customfield", + name="default_value", + field=models.CharField( + help_text=b"You may leave blank. For Boolean use True or False", + max_length=5000, + blank=True, + ), preserve_default=True, ), migrations.AlterField( - model_name='customfield', - name='field_type', - field=models.CharField(default=b't', max_length=1, choices=[(b't', b'Text'), (b'a', b'Large Text Field'), (b'i', b'Integer'), (b'f', b'Floating point decimal'), (b'b', b'Boolean (Yes/No)'), (b'm', b'Dropdown Choices'), (b'd', b'Date')]), + model_name="customfield", + name="field_type", + field=models.CharField( + default=b"t", + max_length=1, + choices=[ + (b"t", b"Text"), + (b"a", b"Large Text Field"), + (b"i", b"Integer"), + (b"f", b"Floating point decimal"), + (b"b", b"Boolean (Yes/No)"), + (b"m", b"Dropdown Choices"), + (b"d", b"Date"), + ], + ), preserve_default=True, ), migrations.AlterField( - model_name='customfield', - name='name', + model_name="customfield", + name="name", field=models.CharField(max_length=150), preserve_default=True, ), migrations.AlterField( - model_name='customfieldvalue', - name='value', + model_name="customfieldvalue", + name="value", field=models.CharField(max_length=5000, null=True, blank=True), preserve_default=True, ), diff --git a/custom_field/migrations/0003_customfield_mask.py b/custom_field/migrations/0003_customfield_mask.py index a8052f9..b5480c2 100644 --- a/custom_field/migrations/0003_customfield_mask.py +++ b/custom_field/migrations/0003_customfield_mask.py @@ -7,13 +7,17 @@ class Migration(migrations.Migration): dependencies = [ - ('custom_field', '0002_auto_20150119_2032'), + ("custom_field", "0002_auto_20150119_2032"), ] operations = [ migrations.AddField( - model_name='customfield', - name='mask', - field=models.CharField(help_text=b"You may leave blank. For user Jquery Mask, ex: '00/00/0000' for date.", max_length=5000, blank=True), + model_name="customfield", + name="mask", + field=models.CharField( + help_text=b"You may leave blank. For user Jquery Mask, ex: '00/00/0000' for date.", + max_length=5000, + blank=True, + ), ), ] diff --git a/custom_field/migrations/0004_django2_upgrade.py b/custom_field/migrations/0004_django2_upgrade.py new file mode 100644 index 0000000..1c64ec2 --- /dev/null +++ b/custom_field/migrations/0004_django2_upgrade.py @@ -0,0 +1,58 @@ +# Generated by Django 2.2.1 on 2019-06-11 13:35 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("custom_field", "0003_customfield_mask"), + ] + + operations = [ + migrations.AlterField( + model_name="customfield", + name="default_value", + field=models.CharField( + blank=True, + help_text="You may leave blank. For Boolean use True or False", + max_length=5000, + ), + ), + migrations.AlterField( + model_name="customfield", + name="field_choices", + field=models.CharField( + blank=True, + help_text="List the choices you want displayed, seperated by commas. This is only valid for Dropdown, Multiple, and Checkbox field types", + max_length=2000, + ), + ), + migrations.AlterField( + model_name="customfield", + name="field_type", + field=models.CharField( + choices=[ + ("t", "Text"), + ("a", "Large Text Field"), + ("i", "Integer"), + ("f", "Floating point decimal"), + ("b", "Boolean (Yes/No)"), + ("m", "Dropdown Choices"), + ("d", "Date"), + ("h", "Date Time"), + ], + default="t", + max_length=1, + ), + ), + migrations.AlterField( + model_name="customfield", + name="mask", + field=models.CharField( + blank=True, + help_text="You may leave blank. For user Jquery Mask, ex: '00/00/0000' for date.", + max_length=5000, + ), + ), + ] diff --git a/custom_field/models.py b/custom_field/models.py index ccfd29e..8b86293 100644 --- a/custom_field/models.py +++ b/custom_field/models.py @@ -1,12 +1,12 @@ +import sys + from django import forms +from django.contrib.contenttypes import fields from django.contrib.contenttypes.models import ContentType from django.db import models -from django.contrib.contenttypes import fields from django.utils.encoding import python_2_unicode_compatible -import sys - -if sys.version < '3': +if sys.version < "3": text_type = unicode else: text_type = str @@ -18,30 +18,34 @@ class CustomField(models.Model): A field abstract -- it describe what the field is. There are one of these for each custom field the user configures. """ + name = models.CharField(max_length=150) - content_type = models.ForeignKey(ContentType) + content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) field_type = models.CharField( max_length=1, choices=( - ('t', 'Text'), - ('a', 'Large Text Field'), - ('i', 'Integer'), - ('f', 'Floating point decimal'), - ('b', 'Boolean (Yes/No)'), - ('m', 'Dropdown Choices'), - ('d', 'Date'), - ('h', 'Date Time'), + ("t", "Text"), + ("a", "Large Text Field"), + ("i", "Integer"), + ("f", "Floating point decimal"), + ("b", "Boolean (Yes/No)"), + ("m", "Dropdown Choices"), + ("d", "Date"), + ("h", "Date Time"), ), - default='t') + default="t", + ) default_value = models.CharField( max_length=5000, blank=True, - help_text="You may leave blank. For Boolean use True or False") + help_text="You may leave blank. For Boolean use True or False", + ) is_required = models.BooleanField(default=False) mask = models.CharField( max_length=5000, blank=True, - help_text="You may leave blank. For user Jquery Mask, ex: '00/00/0000' for date.") + help_text="You may leave blank. For user Jquery Mask, ex: '00/00/0000' for date.", + ) field_choices = models.CharField( max_length=2000, blank=True, @@ -50,17 +54,15 @@ class CustomField(models.Model): ) def get_value_for_object(self, obj): - return CustomFieldValue.objects.get_or_create( - field=self, - object_id=obj.id)[0] + return CustomFieldValue.objects.get_or_create(field=self, object_id=obj.id)[0] def __str__(self): return self.name def get_form_field(self): universal_kwargs = { - 'initial': self.default_value, - 'required': self.is_required, + "initial": self.default_value, + "required": self.is_required, } if self.field_type == "b": return forms.BooleanField(**universal_kwargs) @@ -71,15 +73,14 @@ def get_form_field(self): elif self.field_type == "a": return forms.CharField(widget=forms.Textarea, **universal_kwargs) elif self.field_type == "m": - choices = self.field_choices.split(',') + choices = self.field_choices.split(",") if self.is_required is True: select_choices = () else: - select_choices = (('', '---------'),) + select_choices = (("", "---------"),) for choice in choices: select_choices = select_choices + ((choice, choice),) - return forms.ChoiceField( - choices=select_choices, **universal_kwargs) + return forms.ChoiceField(choices=select_choices, **universal_kwargs) elif self.field_type == "d": return forms.DateField(**universal_kwargs) elif self.field_type == "h": @@ -87,7 +88,7 @@ def get_form_field(self): return forms.CharField(**universal_kwargs) class Meta: - unique_together = ('name', 'content_type') + unique_together = ("name", "content_type") @python_2_unicode_compatible @@ -96,11 +97,16 @@ class CustomFieldValue(models.Model): A field instance -- contains the actual data. There are many of these, for each value that corresponds to a CustomField for a given model. """ - field = models.ForeignKey(CustomField, related_name='instance') + + field = models.ForeignKey( + CustomField, related_name="instance", on_delete=models.CASCADE + ) value = models.CharField(max_length=5000, blank=True, null=True) object_id = models.PositiveIntegerField() - content_type = models.ForeignKey(ContentType, blank=True, null=True) - content_object = fields.GenericForeignKey('content_type', 'object_id') + content_type = models.ForeignKey( + ContentType, blank=True, null=True, on_delete=models.CASCADE + ) + content_object = fields.GenericForeignKey("content_type", "object_id") def __str__(self): return text_type(self.value) @@ -120,4 +126,4 @@ def get_form_field(self): return self.field.get_form_field() class Meta: - unique_together = ('field', 'object_id') + unique_together = ("field", "object_id") diff --git a/custom_field/templates/admin/includes/custom_field_fieldset.html b/custom_field/templates/admin/includes/custom_field_fieldset.html index 456f89a..92c377b 100644 --- a/custom_field/templates/admin/includes/custom_field_fieldset.html +++ b/custom_field/templates/admin/includes/custom_field_fieldset.html @@ -3,7 +3,7 @@
{% endif %} {% endspaceless %} - diff --git a/custom_field/tests.py b/custom_field/tests.py index 6f3ce1f..c0932e0 100644 --- a/custom_field/tests.py +++ b/custom_field/tests.py @@ -2,18 +2,18 @@ from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ValidationError from django.test import TestCase +from django.urls import reverse + from .models import CustomField, CustomFieldValue -from django.core import urlresolvers class CustomFieldTest(TestCase): def setUp(self): - custom_field_ct = ContentType.objects.get(app_label="custom_field", - model="customfield") + custom_field_ct = ContentType.objects.get( + app_label="custom_field", model="customfield" + ) self.custom_field = CustomField.objects.create( - name="test_field", - content_type=custom_field_ct, - field_type="i", + name="test_field", content_type=custom_field_ct, field_type="i", ) self.user_custom_field = CustomField.objects.create( name="test_user_field", @@ -21,34 +21,30 @@ def setUp(self): field_type="i", default_value=42, ) - user = User.objects.create_user('temporary', 'temporary@gmail.com', - 'temporary') + user = User.objects.create_user("temporary", "temporary@gmail.com", "temporary") user.is_staff = True user.is_superuser = True user.save() - self.client.login(username='temporary', password='temporary') + self.client.login(username="temporary", password="temporary") def test_validation(self): custom_value = CustomFieldValue.objects.create( - field=self.custom_field, - value='5', - object_id=self.custom_field.id, + field=self.custom_field, value="5", object_id=self.custom_field.id, ) custom_value.clean() custom_value.save() - self.assertEquals(custom_value.value, '5') - custom_value.value = 'fdsf' + self.assertEquals(custom_value.value, "5") + custom_value.value = "fdsf" try: custom_value.clean() - self.fail('Was able to save string as custom integer field!') + self.fail("Was able to save string as custom integer field!") except ValidationError: pass def test_admin(self): - change_url = urlresolvers.reverse( - 'admin:custom_field_customfield_change', args=[1]) + change_url = reverse("admin:custom_field_customfield_change", args=[1]) response = self.client.get(change_url) - self.assertContains(response, '42') + self.assertContains(response, "42") response = self.client.get(change_url) # Make sure we aren't adding it on each get - self.assertContains(response, '42') + self.assertContains(response, "42") diff --git a/setup.py b/setup.py index ff2daa3..073dc61 100644 --- a/setup.py +++ b/setup.py @@ -1,26 +1,30 @@ -from setuptools import setup, find_packages +from setuptools import find_packages, setup setup( - name = "django-custom-field", - version = "2.9", - author = "David Burke", - author_email = "wward@warddevelopment.com", - description = ("End user custom fields for Django including contrib.admin support"), - license = "MIT", - keywords = "django admin", - url = "http://github.com/willseward/django-custom-field", + name="django-custom-field", + version="2.9", + author="David Burke", + author_email="wward@warddevelopment.com", + description=("End user custom fields for Django including contrib.admin support"), + license="MIT", + keywords="django admin", + url="http://github.com/willseward/django-custom-field", packages=find_packages(), include_package_data=True, - install_requires = ['django'], + install_requires=["django"], classifiers=[ "Development Status :: 5 - Production/Stable", - 'Environment :: Web Environment', - 'Framework :: Django', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.4', - 'Intended Audience :: Developers', - 'Intended Audience :: System Administrators', + "Environment :: Web Environment", + "Framework :: Django", + "Programming Language :: Python", + "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3.4", + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Intended Audience :: Developers", + "Intended Audience :: System Administrators", "License :: OSI Approved :: MIT License", ], ) diff --git a/test_settings.py b/test_settings.py index 8223c8f..4c4b4bb 100644 --- a/test_settings.py +++ b/test_settings.py @@ -1,52 +1,50 @@ import os + PROJECT_DIR = os.path.dirname(__file__) -STATIC_URL = PROJECT_DIR + '/static/' +STATIC_URL = PROJECT_DIR + "/static/" -SECRET_KEY='thisisatestingkey' +SECRET_KEY = "thisisatestingkey" -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': 'testdb', - } -} +DATABASES = {"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": "testdb"}} INSTALLED_APPS = ( - 'custom_field', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.sites', - 'django.contrib.staticfiles', - 'django.contrib.admin', + "custom_field", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.messages", + "django.contrib.sessions", + "django.contrib.sites", + "django.contrib.staticfiles", + "django.contrib.admin", ) TEMPLATES = [ { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.contrib.auth.context_processors.auth', - 'django.template.context_processors.i18n', - 'django.template.context_processors.request', - 'django.template.context_processors.media', - 'django.template.context_processors.static', - 'django.template.context_processors.request', + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + "django.template.context_processors.i18n", + "django.template.context_processors.request", + "django.template.context_processors.media", + "django.template.context_processors.static", + "django.template.context_processors.request", ] }, }, ] -MIDDLEWARE_CLASSES = ( - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', +MIDDLEWARE = ( + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", ) SITE_ID = 1 diff --git a/test_urls.py b/test_urls.py index ec71a4b..483e1a1 100644 --- a/test_urls.py +++ b/test_urls.py @@ -1,8 +1,8 @@ -from django.conf.urls import include, url - +from django.conf.urls import url from django.contrib import admin + admin.autodiscover() urlpatterns = [ - url(r'^admin/', include(admin.site.urls)), + url(r"^admin/", admin.site.urls), ] diff --git a/tox.ini b/tox.ini index 506209a..a19d9b4 100644 --- a/tox.ini +++ b/tox.ini @@ -1,12 +1,13 @@ [tox] -envlist = py27-django{18,19,110}, - py34-django{18,19,110} +envlist = py27-django111, + py{35,36,37}-django{20,21,22} [testenv] commands = - python manage.py test + python manage.py test deps = - django18: Django>=1.8, <1.9 - django19: Django>=1.9, <1.10 - django110: Django==1.10 \ No newline at end of file + django111: Django>=1.11, <1.12 + django20: Django>=2.0,<2.1 + django21: Django>=2.1,<2.2 + django22: Django>=2.2,<2.3