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
parent
10188ec8ac
commit
f4daaeb232
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ Download django-admin-sortable from
|
||||||
`source <https://github.com/iambrandontaylor/django-admin-sortable/archive/master.zip>`__
|
`source <https://github.com/iambrandontaylor/django-admin-sortable/archive/master.zip>`__
|
||||||
|
|
||||||
1. Unzip the directory and cd into the uncompressed project directory
|
1. Unzip the directory and cd into the uncompressed project directory
|
||||||
2.
|
2.
|
||||||
|
|
||||||
- Optional: Enable your virtualenv
|
- Optional: Enable your virtualenv
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
~~~~~~
|
~~~~~~
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
VERSION = (2, 1, 1)
|
VERSION = (2, 1, 2)
|
||||||
DEV_N = None
|
DEV_N = None
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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]}
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -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',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
|
||||||
|
|
@ -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),
|
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class SamplesConfig(AppConfig):
|
||||||
|
name = 'samples'
|
||||||
|
|
@ -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.')
|
||||||
|
|
||||||
Loading…
Reference in New Issue