diff --git a/README.md b/README.md index 00e2c00..777e14f 100644 --- a/README.md +++ b/README.md @@ -531,8 +531,8 @@ ordering on top of that just seemed a little much in my opinion. ### Status django-admin-sortable is currently used in production. -### What's new in 2.1.1? -- Touch support +### What's new in 2.1.2? +- Django 2 compatibility ### Future - Better template support for foreign keys that are self referential. If someone would like to take on rendering recursive sortables, that would be super. diff --git a/README.rst b/README.rst index e120259..204ff68 100644 --- a/README.rst +++ b/README.rst @@ -51,7 +51,7 @@ Download django-admin-sortable from `source `__ 1. Unzip the directory and cd into the uncompressed project directory -2. +2. - Optional: Enable your virtualenv @@ -657,10 +657,10 @@ Status django-admin-sortable is currently used in production. -What's new in 2.1.1? +What's new in 2.1.2? ~~~~~~~~~~~~~~~~~~~~ -- Touch support +- Django 2 compatibility Future ~~~~~~ diff --git a/adminsortable/__init__.py b/adminsortable/__init__.py index 1a5d0c1..22f864f 100644 --- a/adminsortable/__init__.py +++ b/adminsortable/__init__.py @@ -1,4 +1,4 @@ -VERSION = (2, 1, 1) +VERSION = (2, 1, 2) DEV_N = None diff --git a/adminsortable/templatetags/django_template_additions.py b/adminsortable/templatetags/django_template_additions.py index 1866b68..0729bb3 100644 --- a/adminsortable/templatetags/django_template_additions.py +++ b/adminsortable/templatetags/django_template_additions.py @@ -76,7 +76,7 @@ def dynamic_regroup(parser, token): return DynamicRegroupNode(target, parser, expression, var_name) -@register.assignment_tag +@register.simple_tag def get_django_version(): version = django.VERSION return {'major': version[0], 'minor': version[1]} diff --git a/sample_project/database/test_project.sqlite b/sample_project/database/test_project.sqlite index b09cb9c..ebb7162 100644 Binary files a/sample_project/database/test_project.sqlite and b/sample_project/database/test_project.sqlite differ diff --git a/sample_project/sample_project/settings.py b/sample_project/sample_project/settings.py index 7327e75..151d35e 100644 --- a/sample_project/sample_project/settings.py +++ b/sample_project/sample_project/settings.py @@ -93,13 +93,12 @@ STATICFILES_FINDERS = ( SECRET_KEY = '8**a!c8$1x)p@j2pj0yq!*v+dzp24g*$918ws#x@k+gf%0%rct' MIDDLEWARE = [ - 'django.middleware.common.CommonMiddleware', + 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', - # Uncomment the next line for simple clickjacking protection: 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] @@ -115,17 +114,14 @@ TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [ - map_path('templates') + map_path('templates'), ], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ - 'django.contrib.auth.context_processors.auth', 'django.template.context_processors.debug', - 'django.template.context_processors.i18n', - 'django.template.context_processors.media', - 'django.template.context_processors.static', - 'django.template.context_processors.tz', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, @@ -143,7 +139,7 @@ INSTALLED_APPS = ( 'django.contrib.admindocs', 'adminsortable', - 'app', + 'samples', ) # A sample logging configuration. The only tangible logging @@ -174,3 +170,21 @@ LOGGING = { }, } } + +# Password validation +# https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] diff --git a/sample_project/sample_project/urls.py b/sample_project/sample_project/urls.py index 3e2dcfe..2071700 100644 --- a/sample_project/sample_project/urls.py +++ b/sample_project/sample_project/urls.py @@ -1,18 +1,21 @@ -from django.conf.urls import include, url +"""django2_app URL Configuration -# Uncomment the next two lines to enable the admin: +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/2.0/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" from django.contrib import admin -admin.autodiscover() - +from django.urls import path urlpatterns = [ - # Examples: - # url(r'^$', 'test_project.views.home', name='home'), - # url(r'^test_project/', include('test_project.foo.urls')), - - # Uncomment the admin/doc line below to enable admin documentation: - url(r'^admin/doc/', include('django.contrib.admindocs.urls')), - - # Uncomment the next line to enable the admin: - url(r'^admin/', admin.site.urls), + path('admin/', admin.site.urls), ] diff --git a/sample_project/sample_project/wsgi.py b/sample_project/sample_project/wsgi.py index 43ff066..c54b86f 100644 --- a/sample_project/sample_project/wsgi.py +++ b/sample_project/sample_project/wsgi.py @@ -1,32 +1,15 @@ """ -WSGI config for test_project project. +WSGI config for django2_app project. -This module contains the WSGI application used by Django's development server -and any production WSGI deployments. It should expose a module-level variable -named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover -this application via the ``WSGI_APPLICATION`` setting. - -Usually you will have the standard Django WSGI application here, but it also -might make sense to replace the whole Django WSGI application with a custom one -that later delegates to the Django one. For example, you could introduce WSGI -middleware here, or combine a Django application with an application of another -framework. +It exposes the WSGI callable as a module-level variable named ``application``. +For more information on this file, see +https://docs.djangoproject.com/en/2.0/howto/deployment/wsgi/ """ import os -# 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", "test_project.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 + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test_project.settings") application = get_wsgi_application() -# Apply WSGI middleware here. -# from helloworld.wsgi import HelloWorldApplication -# application = HelloWorldApplication(application) diff --git a/sample_project/app/__init__.py b/sample_project/samples/__init__.py similarity index 100% rename from sample_project/app/__init__.py rename to sample_project/samples/__init__.py diff --git a/sample_project/app/admin.py b/sample_project/samples/admin.py similarity index 97% rename from sample_project/app/admin.py rename to sample_project/samples/admin.py index 9da0c19..ec18418 100644 --- a/sample_project/app/admin.py +++ b/sample_project/samples/admin.py @@ -4,7 +4,7 @@ from adminsortable.admin import (SortableAdmin, SortableTabularInline, SortableStackedInline, SortableGenericStackedInline, NonSortableParentAdmin) from adminsortable.utils import get_is_sortable -from app.models import (Category, Widget, Project, Credit, Note, GenericNote, +from .models import (Category, Widget, Project, Credit, Note, GenericNote, Component, Person, NonSortableCategory, SortableCategoryWidget, SortableNonInlineCategory, NonSortableCredit, NonSortableNote, CustomWidget, CustomWidgetComponent, BackwardCompatibleWidget) diff --git a/sample_project/samples/apps.py b/sample_project/samples/apps.py new file mode 100644 index 0000000..ea7abac --- /dev/null +++ b/sample_project/samples/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class SamplesConfig(AppConfig): + name = 'samples' diff --git a/sample_project/app/models.py b/sample_project/samples/models.py similarity index 100% rename from sample_project/app/models.py rename to sample_project/samples/models.py diff --git a/sample_project/app/tests.py b/sample_project/samples/tests.py similarity index 96% rename from sample_project/app/tests.py rename to sample_project/samples/tests.py index 0278331..60715f5 100644 --- a/sample_project/app/tests.py +++ b/sample_project/samples/tests.py @@ -15,7 +15,7 @@ from django.test.client import Client from adminsortable.models import SortableMixin from adminsortable.utils import get_is_sortable -from app.models import Category, Person, Project +from .models import Category, Person, Project class TestSortableModel(SortableMixin): @@ -113,7 +113,7 @@ class SortableTestCase(TestCase): def test_adminsortable_change_list_view(self): self.client.login(username=self.user.username, password=self.user_raw_password) - response = self.client.get('/admin/app/category/sort/') + response = self.client.get('/admin/samples/category/sort/') self.assertEqual(response.status_code, httplib.OK, 'Unable to reach sort view.') @@ -124,7 +124,7 @@ class SortableTestCase(TestCase): return category1, category2, category3 def get_sorting_url(self, model): - return '/admin/app/project/sort/do-sorting/{0}/'.format( + return '/admin/samples/project/sort/do-sorting/{0}/'.format( model.model_type_id()) def get_category_indexes(self, *categories): @@ -135,7 +135,7 @@ class SortableTestCase(TestCase): password=self.user_raw_password) self.assertTrue(logged_in, 'User is not logged in') - response = self.client.get('/admin/app/category/sort/') + response = self.client.get('/admin/samples/category/sort/') self.assertEqual(response.status_code, httplib.OK, 'Admin sort request failed.') @@ -185,8 +185,7 @@ class SortableTestCase(TestCase): # make a normal POST response = self.client.post(self.get_sorting_url(Category), data=self.get_category_indexes(category1, category2, category3)) - content = json.loads(response.content.decode(encoding='UTF-8'), - 'latin-1') + content = json.loads(response.content.decode(encoding='UTF-8')) self.assertFalse(content.get('objects_sorted'), 'Objects should not have been sorted. An ajax post is required.') @@ -202,8 +201,7 @@ class SortableTestCase(TestCase): response = self.client.post(self.get_sorting_url(Category), data=self.get_category_indexes(category3, category2, category1), HTTP_X_REQUESTED_WITH='XMLHttpRequest') - content = json.loads(response.content.decode(encoding='UTF-8'), - 'latin-1') + content = json.loads(response.content.decode(encoding='UTF-8')) self.assertTrue(content.get('objects_sorted'), 'Objects should have been sorted.') @@ -256,7 +254,7 @@ class SortableTestCase(TestCase): self.client.login(username=self.user.username, password=self.user_raw_password) - response = self.client.get('/admin/app/project/sort/') + response = self.client.get('/admin/samples/project/sort/') self.assertEqual(response.status_code, httplib.OK, 'Unable to reach sort view.') @@ -266,7 +264,7 @@ class SortableTestCase(TestCase): self.client.login(username=self.staff.username, password=self.staff_raw_password) - response = self.client.get('/admin/app/project/sort/') + response = self.client.get('/admin/samples/project/sort/') self.assertEqual(response.status_code, httplib.FORBIDDEN, 'Sort view must be forbidden.') @@ -291,8 +289,7 @@ class SortableTestCase(TestCase): response.status_code, httplib.OK, 'Note inline must be sortable in ProjectAdmin') - content = json.loads(response.content.decode(encoding='UTF-8'), - 'latin-1') + content = json.loads(response.content.decode(encoding='UTF-8')) self.assertTrue(content.get('objects_sorted'), 'Objects should have been sorted.')