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.
master
Brandon Taylor 2017-12-04 21:29:55 -05:00
parent 10188ec8ac
commit f4daaeb232
13 changed files with 68 additions and 66 deletions

View File

@ -531,8 +531,8 @@ ordering on top of that just seemed a little much in my opinion.
### Status ### Status
django-admin-sortable is currently used in production. 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 ### 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. - Better template support for foreign keys that are self referential. If someone would like to take on rendering recursive sortables, that would be super.

View File

@ -657,10 +657,10 @@ Status
django-admin-sortable is currently used in production. 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 Future
~~~~~~ ~~~~~~

View File

@ -1,4 +1,4 @@
VERSION = (2, 1, 1) VERSION = (2, 1, 2)
DEV_N = None DEV_N = None

View File

@ -76,7 +76,7 @@ def dynamic_regroup(parser, token):
return DynamicRegroupNode(target, parser, expression, var_name) return DynamicRegroupNode(target, parser, expression, var_name)
@register.assignment_tag @register.simple_tag
def get_django_version(): def get_django_version():
version = django.VERSION version = django.VERSION
return {'major': version[0], 'minor': version[1]} return {'major': version[0], 'minor': version[1]}

View File

@ -93,13 +93,12 @@ STATICFILES_FINDERS = (
SECRET_KEY = '8**a!c8$1x)p@j2pj0yq!*v+dzp24g*$918ws#x@k+gf%0%rct' SECRET_KEY = '8**a!c8$1x)p@j2pj0yq!*v+dzp24g*$918ws#x@k+gf%0%rct'
MIDDLEWARE = [ MIDDLEWARE = [
'django.middleware.common.CommonMiddleware', 'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
# Uncomment the next line for simple clickjacking protection:
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
] ]
@ -115,17 +114,14 @@ TEMPLATES = [
{ {
'BACKEND': 'django.template.backends.django.DjangoTemplates', 'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [ 'DIRS': [
map_path('templates') map_path('templates'),
], ],
'APP_DIRS': True, 'APP_DIRS': True,
'OPTIONS': { 'OPTIONS': {
'context_processors': [ 'context_processors': [
'django.contrib.auth.context_processors.auth',
'django.template.context_processors.debug', 'django.template.context_processors.debug',
'django.template.context_processors.i18n', 'django.template.context_processors.request',
'django.template.context_processors.media', 'django.contrib.auth.context_processors.auth',
'django.template.context_processors.static',
'django.template.context_processors.tz',
'django.contrib.messages.context_processors.messages', 'django.contrib.messages.context_processors.messages',
], ],
}, },
@ -143,7 +139,7 @@ INSTALLED_APPS = (
'django.contrib.admindocs', 'django.contrib.admindocs',
'adminsortable', 'adminsortable',
'app', 'samples',
) )
# A sample logging configuration. The only tangible logging # 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',
},
]

View File

@ -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 from django.contrib import admin
admin.autodiscover() from django.urls import path
urlpatterns = [ urlpatterns = [
# Examples: path('admin/', admin.site.urls),
# 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),
] ]

View File

@ -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 It exposes the WSGI callable as a module-level variable named ``application``.
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.
For more information on this file, see
https://docs.djangoproject.com/en/2.0/howto/deployment/wsgi/
""" """
import os 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 from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test_project.settings")
application = get_wsgi_application() application = get_wsgi_application()
# Apply WSGI middleware here.
# from helloworld.wsgi import HelloWorldApplication
# application = HelloWorldApplication(application)

View File

@ -4,7 +4,7 @@ from adminsortable.admin import (SortableAdmin, SortableTabularInline,
SortableStackedInline, SortableGenericStackedInline, SortableStackedInline, SortableGenericStackedInline,
NonSortableParentAdmin) NonSortableParentAdmin)
from adminsortable.utils import get_is_sortable 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, Component, Person, NonSortableCategory, SortableCategoryWidget,
SortableNonInlineCategory, NonSortableCredit, NonSortableNote, SortableNonInlineCategory, NonSortableCredit, NonSortableNote,
CustomWidget, CustomWidgetComponent, BackwardCompatibleWidget) CustomWidget, CustomWidgetComponent, BackwardCompatibleWidget)

View File

@ -0,0 +1,5 @@
from django.apps import AppConfig
class SamplesConfig(AppConfig):
name = 'samples'

View File

@ -15,7 +15,7 @@ from django.test.client import Client
from adminsortable.models import SortableMixin from adminsortable.models import SortableMixin
from adminsortable.utils import get_is_sortable from adminsortable.utils import get_is_sortable
from app.models import Category, Person, Project from .models import Category, Person, Project
class TestSortableModel(SortableMixin): class TestSortableModel(SortableMixin):
@ -113,7 +113,7 @@ class SortableTestCase(TestCase):
def test_adminsortable_change_list_view(self): def test_adminsortable_change_list_view(self):
self.client.login(username=self.user.username, self.client.login(username=self.user.username,
password=self.user_raw_password) 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, self.assertEqual(response.status_code, httplib.OK,
'Unable to reach sort view.') 'Unable to reach sort view.')
@ -124,7 +124,7 @@ class SortableTestCase(TestCase):
return category1, category2, category3 return category1, category2, category3
def get_sorting_url(self, model): 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()) model.model_type_id())
def get_category_indexes(self, *categories): def get_category_indexes(self, *categories):
@ -135,7 +135,7 @@ class SortableTestCase(TestCase):
password=self.user_raw_password) password=self.user_raw_password)
self.assertTrue(logged_in, 'User is not logged in') 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, self.assertEqual(response.status_code, httplib.OK,
'Admin sort request failed.') 'Admin sort request failed.')
@ -185,8 +185,7 @@ class SortableTestCase(TestCase):
# make a normal POST # make a normal POST
response = self.client.post(self.get_sorting_url(Category), response = self.client.post(self.get_sorting_url(Category),
data=self.get_category_indexes(category1, category2, category3)) data=self.get_category_indexes(category1, category2, category3))
content = json.loads(response.content.decode(encoding='UTF-8'), content = json.loads(response.content.decode(encoding='UTF-8'))
'latin-1')
self.assertFalse(content.get('objects_sorted'), self.assertFalse(content.get('objects_sorted'),
'Objects should not have been sorted. An ajax post is required.') '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), response = self.client.post(self.get_sorting_url(Category),
data=self.get_category_indexes(category3, category2, category1), data=self.get_category_indexes(category3, category2, category1),
HTTP_X_REQUESTED_WITH='XMLHttpRequest') HTTP_X_REQUESTED_WITH='XMLHttpRequest')
content = json.loads(response.content.decode(encoding='UTF-8'), content = json.loads(response.content.decode(encoding='UTF-8'))
'latin-1')
self.assertTrue(content.get('objects_sorted'), self.assertTrue(content.get('objects_sorted'),
'Objects should have been sorted.') 'Objects should have been sorted.')
@ -256,7 +254,7 @@ class SortableTestCase(TestCase):
self.client.login(username=self.user.username, self.client.login(username=self.user.username,
password=self.user_raw_password) 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, self.assertEqual(response.status_code, httplib.OK,
'Unable to reach sort view.') 'Unable to reach sort view.')
@ -266,7 +264,7 @@ class SortableTestCase(TestCase):
self.client.login(username=self.staff.username, self.client.login(username=self.staff.username,
password=self.staff_raw_password) 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, self.assertEqual(response.status_code, httplib.FORBIDDEN,
'Sort view must be forbidden.') 'Sort view must be forbidden.')
@ -291,8 +289,7 @@ class SortableTestCase(TestCase):
response.status_code, response.status_code,
httplib.OK, httplib.OK,
'Note inline must be sortable in ProjectAdmin') 'Note inline must be sortable in ProjectAdmin')
content = json.loads(response.content.decode(encoding='UTF-8'), content = json.loads(response.content.decode(encoding='UTF-8'))
'latin-1')
self.assertTrue(content.get('objects_sorted'), self.assertTrue(content.get('objects_sorted'),
'Objects should have been sorted.') 'Objects should have been sorted.')