From f4daaeb232110efa30bedf514838972929d78f1b Mon Sep 17 00:00:00 2001 From: Brandon Taylor Date: Mon, 4 Dec 2017 21:29:55 -0500 Subject: [PATCH] Restructured sample project for Django 2. Refactored database and changed "app" to "samples" so name didn't conflict with "AppConfig". Replaced deprecated assignment_tag with simple_tag. Updated unit tests. --- README.md | 4 +-- README.rst | 6 ++-- adminsortable/__init__.py | 2 +- .../templatetags/django_template_additions.py | 2 +- sample_project/database/test_project.sqlite | Bin 89088 -> 90112 bytes sample_project/sample_project/settings.py | 34 ++++++++++++------ sample_project/sample_project/urls.py | 29 ++++++++------- sample_project/sample_project/wsgi.py | 29 ++++----------- sample_project/{app => samples}/__init__.py | 0 sample_project/{app => samples}/admin.py | 2 +- sample_project/samples/apps.py | 5 +++ sample_project/{app => samples}/models.py | 0 sample_project/{app => samples}/tests.py | 21 +++++------ 13 files changed, 68 insertions(+), 66 deletions(-) rename sample_project/{app => samples}/__init__.py (100%) rename sample_project/{app => samples}/admin.py (97%) create mode 100644 sample_project/samples/apps.py rename sample_project/{app => samples}/models.py (100%) rename sample_project/{app => samples}/tests.py (96%) 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 b09cb9cd68c90c9398061e43e1f1c18f1669c402..ebb7162f0cdfe2e10641aebe5985349b72901631 100644 GIT binary patch delta 5212 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 N6$M2Zlh;ZA{{mY>oM!+4 delta 3345 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 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.')