Minor modifications for Django 1.5.x and 1.4.x backward-compatibility.

Added new sample project.
Improved documentation.
Refactored CSS selector for inlines that are sortable.
This commit is contained in:
Brandon Taylor
2013-03-10 20:53:05 -04:00
parent ac2cecf291
commit a54e81434f
39 changed files with 561 additions and 3702 deletions
+24 -23
View File
@@ -1,23 +1,24 @@
from django.contrib import admin
from adminsortable.admin import SortableAdmin, SortableTabularInline, SortableStackedInline
from app.models import Category, Project, Credit, Note
admin.site.register(Category, SortableAdmin)
class CreditInline(SortableTabularInline):
model = Credit
class NoteInline(SortableStackedInline):
model = Note
extra = 0
class ProjectAdmin(SortableAdmin):
inlines = [CreditInline, NoteInline]
list_display = ['__unicode__', 'category']
admin.site.register(Project, ProjectAdmin)
from django.contrib import admin
from adminsortable.admin import (SortableAdmin, SortableTabularInline,
SortableStackedInline)
from app.models import Category, Project, Credit, Note
admin.site.register(Category, SortableAdmin)
class CreditInline(SortableTabularInline):
model = Credit
class NoteInline(SortableStackedInline):
model = Note
extra = 0
class ProjectAdmin(SortableAdmin):
inlines = [CreditInline, NoteInline]
list_display = ['__unicode__', 'category']
admin.site.register(Project, ProjectAdmin)
-136
View File
@@ -1,136 +0,0 @@
[
{
"pk": 1,
"model": "app.category",
"fields": {
"order": 2,
"title": "Category 1"
}
},
{
"pk": 2,
"model": "app.category",
"fields": {
"order": 1,
"title": "Category 2"
}
},
{
"pk": 3,
"model": "app.category",
"fields": {
"order": 3,
"title": "Category 3"
}
},
{
"pk": 1,
"model": "app.project",
"fields": {
"category": 1,
"description": "Sample category 1",
"order": 1,
"title": "Sample Project 1"
}
},
{
"pk": 2,
"model": "app.project",
"fields": {
"category": 1,
"description": "Another sample project.",
"order": 2,
"title": "Sample Project 2"
}
},
{
"pk": 3,
"model": "app.project",
"fields": {
"category": 3,
"description": "Yest another sample project.",
"order": 3,
"title": "Sample Project 3"
}
},
{
"pk": 1,
"model": "app.credit",
"fields": {
"project": 1,
"first_name": "Bob",
"last_name": "Smith",
"order": 2
}
},
{
"pk": 2,
"model": "app.credit",
"fields": {
"project": 1,
"first_name": "Sally",
"last_name": "Smith",
"order": 1
}
},
{
"pk": 3,
"model": "app.credit",
"fields": {
"project": 1,
"first_name": "Johnny",
"last_name": "Smith",
"order": 3
}
},
{
"pk": 4,
"model": "app.credit",
"fields": {
"project": 3,
"first_name": "Sally Ann",
"last_name": "Smith",
"order": 4
}
},
{
"pk": 5,
"model": "app.credit",
"fields": {
"project": 3,
"first_name": "George",
"last_name": "Smith",
"order": 5
}
},
{
"pk": 6,
"model": "app.credit",
"fields": {
"project": 2,
"first_name": "Bob",
"last_name": "Smith",
"order": 6
}
},
{
"pk": 7,
"model": "app.credit",
"fields": {
"project": 2,
"first_name": "George",
"last_name": "Smith",
"order": 7
}
},
{
"pk": 8,
"model": "app.credit",
"fields": {
"project": 2,
"first_name": "Sally",
"last_name": "Smith",
"order": 8
}
}
]
+60 -60
View File
@@ -1,60 +1,60 @@
from django.db import models
from adminsortable.fields import SortableForeignKey
from adminsortable.models import Sortable
class SimpleModel(models.Model):
class Meta:
abstract = True
title = models.CharField(max_length=50)
def __unicode__(self):
return self.title
#a model that is sortable
class Category(SimpleModel, Sortable):
class Meta(Sortable.Meta):
"""
Classes that inherit from Sortable must define an inner
Meta class that inherits from Sortable.Meta or ordering
won't work as expected
"""
verbose_name_plural = 'Categories'
#a model that is sortable relative to a foreign key that is also sortable
#uses SortableForeignKey field. Works with versions 1.3+
class Project(SimpleModel, Sortable):
class Meta(Sortable.Meta):
pass
category = SortableForeignKey(Category)
description = models.TextField()
#registered as a tabular inline on `Project`
class Credit(Sortable):
class Meta(Sortable.Meta):
pass
project = models.ForeignKey(Project)
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
def __unicode__(self):
return '{0} {1}'.format(self.first_name, self.last_name)
#registered as a stacked inline on `Project`
class Note(Sortable):
class Meta(Sortable.Meta):
pass
project = models.ForeignKey(Project)
text = models.CharField(max_length=100)
def __unicode__(self):
return self.text
from django.db import models
from adminsortable.fields import SortableForeignKey
from adminsortable.models import Sortable
class SimpleModel(models.Model):
class Meta:
abstract = True
title = models.CharField(max_length=50)
def __unicode__(self):
return self.title
#a model that is sortable
class Category(SimpleModel, Sortable):
class Meta(Sortable.Meta):
"""
Classes that inherit from Sortable must define an inner
Meta class that inherits from Sortable.Meta or ordering
won't work as expected
"""
verbose_name_plural = 'Categories'
#a model that is sortable relative to a foreign key that is also sortable
#uses SortableForeignKey field. Works with versions 1.3+
class Project(SimpleModel, Sortable):
class Meta(Sortable.Meta):
pass
category = SortableForeignKey(Category)
description = models.TextField()
#registered as a tabular inline on `Project`
class Credit(Sortable):
class Meta(Sortable.Meta):
pass
project = models.ForeignKey(Project)
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
def __unicode__(self):
return '{0} {1}'.format(self.first_name, self.last_name)
#registered as a stacked inline on `Project`
class Note(Sortable):
class Meta(Sortable.Meta):
pass
project = models.ForeignKey(Project)
text = models.CharField(max_length=100)
def __unicode__(self):
return self.text
+157 -140
View File
@@ -1,140 +1,157 @@
import httplib
import json
from django.contrib.auth.models import User
from django.core.urlresolvers import reverse
from django.db import models
from django.test import TestCase
from django.test.client import Client, RequestFactory
from adminsortable.fields import SortableForeignKey
from adminsortable.models import Sortable
from app.models import Category, Credit, Note
class BadSortableModel(models.Model):
note = SortableForeignKey(Note)
credit = SortableForeignKey(Credit)
class TestSortableModel(Sortable):
title = models.CharField(max_length=100)
def __unicode__(self):
return self.title
class SortableTestCase(TestCase):
def setUp(self):
self.client = Client()
self.factory = RequestFactory()
self.user_raw_password = 'admin'
self.user = User.objects.create_user('admin', 'admin@admin.com',
self.user_raw_password)
self.user.is_staff = True
self.user.is_superuser = True
self.user.save()
def create_category(self, title='Category 1'):
category = Category.objects.create(title=title)
return category
def test_new_user_is_authenticated(self):
self.assertEqual(self.user.is_authenticated(), True, 'User is not authenticated')
def test_new_user_is_staff(self):
self.assertEqual(self.user.is_staff, True, 'User is not staff')
def test_is_not_sortable(self):
"""
A model should only become sortable if it has more than
record to sort.
"""
self.create_category()
self.assertFalse(Category.is_sortable(),
'Category only has one record. It should not be sortable.')
def test_is_sortable(self):
self.create_category()
self.create_category(title='Category 2')
self.assertTrue(Category.is_sortable(),
'Category has more than one record. It should be sortable.')
def test_save_order_incremented(self):
category1 = self.create_category()
self.assertEqual(category1.order, 1, 'Category 1 order should be 1.')
category2 = self.create_category(title='Category 2')
self.assertEqual(category2.order, 2, 'Category 2 order should be 2.')
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/')
self.assertEquals(response.status_code, httplib.OK, 'Unable to reach sort view.')
def make_test_categories(self):
category1 = self.create_category()
category2 = self.create_category(title='Category 2')
category3 = self.create_category(title='Category 3')
return category1, category2, category3
def get_sorting_url(self):
return reverse('admin:app_do_sorting', args=(),
kwargs={'model_type_id': Category.model_type_id()})
def get_category_indexes(self, *categories):
return {'indexes': ','.join([str(c.id) for c in categories])}
def test_adminsortable_changelist_templates(self):
logged_in = self.client.login(username=self.user.username, password=self.user_raw_password)
self.assertTrue(logged_in, 'User is not logged in')
response = self.client.get(reverse('admin:app_sort'))
self.assertEqual(response.status_code, httplib.OK, u'Admin sort request failed.')
#assert adminsortable change list templates are used
template_names = [t.name for t in response.templates]
self.assertTrue('adminsortable/change_list.html' in template_names,
u'adminsortable/change_list.html was not rendered')
self.assertTrue('adminsortable/shared/javascript_includes.html' in template_names,
u'JavaScript includes for adminsortable change list were not rendered')
def test_adminsortable_change_list_sorting_fails_if_not_ajax(self):
logged_in = self.client.login(username=self.user.username, password=self.user_raw_password)
self.assertTrue(logged_in, 'User is not logged in')
category1, category2, category3 = self.make_test_categories()
#make a normal POST
response = self.client.post(self.get_sorting_url(),
data=self.get_category_indexes(category1, category2, category3))
content = json.loads(response.content)
self.assertFalse(content.get('objects_sorted'), u'Objects should not have been sorted. An ajax post is required.')
def test_adminsortable_change_list_sorting_successful(self):
logged_in = self.client.login(username=self.user.username, password=self.user_raw_password)
self.assertTrue(logged_in, 'User is not logged in')
#make categories
category1, category2, category3 = self.make_test_categories()
#make an Ajax POST
response = self.client.post(self.get_sorting_url(),
data=self.get_category_indexes(category3, category2, category1),
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
content = json.loads(response.content)
self.assertTrue(content.get('objects_sorted'), u'Objects should have been sorted.')
#assert order is correct
categories = Category.objects.all()
cat1 = categories[0]
cat2 = categories[1]
cat3 = categories[2]
self.assertEqual(cat1.order, 1, u'First category returned should have order == 1')
self.assertEqual(cat1.pk, 3, u'Category ID 3 should have been first in queryset')
self.assertEqual(cat2.order, 2, u'Second category returned should have order == 2')
self.assertEqual(cat2.pk, 2, u'Category ID 2 should have been second in queryset')
self.assertEqual(cat3.order, 3, u'Third category returned should have order == 3')
self.assertEqual(cat3.pk, 1, u'Category ID 1 should have been third in queryset')
import httplib
import json
from django.contrib.auth.models import User
from django.core.urlresolvers import reverse
from django.db import models
from django.test import TestCase
from django.test.client import Client, RequestFactory
from adminsortable.fields import SortableForeignKey
from adminsortable.models import Sortable
from app.models import Category, Credit, Note
class BadSortableModel(models.Model):
note = SortableForeignKey(Note)
credit = SortableForeignKey(Credit)
class TestSortableModel(Sortable):
title = models.CharField(max_length=100)
def __unicode__(self):
return self.title
class SortableTestCase(TestCase):
def setUp(self):
self.client = Client()
self.factory = RequestFactory()
self.user_raw_password = 'admin'
self.user = User.objects.create_user('admin', 'admin@admin.com',
self.user_raw_password)
self.user.is_staff = True
self.user.is_superuser = True
self.user.save()
def create_category(self, title='Category 1'):
category = Category.objects.create(title=title)
return category
def test_new_user_is_authenticated(self):
self.assertEqual(self.user.is_authenticated(), True,
'User is not authenticated')
def test_new_user_is_staff(self):
self.assertEqual(self.user.is_staff, True, 'User is not staff')
def test_is_not_sortable(self):
"""
A model should only become sortable if it has more than
record to sort.
"""
self.create_category()
self.assertFalse(Category.is_sortable(),
'Category only has one record. It should not be sortable.')
def test_is_sortable(self):
self.create_category()
self.create_category(title='Category 2')
self.assertTrue(Category.is_sortable(),
'Category has more than one record. It should be sortable.')
def test_save_order_incremented(self):
category1 = self.create_category()
self.assertEqual(category1.order, 1, 'Category 1 order should be 1.')
category2 = self.create_category(title='Category 2')
self.assertEqual(category2.order, 2, 'Category 2 order should be 2.')
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/')
self.assertEquals(response.status_code, httplib.OK,
'Unable to reach sort view.')
def make_test_categories(self):
category1 = self.create_category()
category2 = self.create_category(title='Category 2')
category3 = self.create_category(title='Category 3')
return category1, category2, category3
def get_sorting_url(self):
return reverse('admin:app_do_sorting', args=(),
kwargs={'model_type_id': Category.model_type_id()})
def get_category_indexes(self, *categories):
return {'indexes': ','.join([str(c.id) for c in categories])}
def test_adminsortable_changelist_templates(self):
logged_in = self.client.login(username=self.user.username,
password=self.user_raw_password)
self.assertTrue(logged_in, 'User is not logged in')
response = self.client.get(reverse('admin:app_sort'))
self.assertEqual(response.status_code, httplib.OK,
'Admin sort request failed.')
#assert adminsortable change list templates are used
template_names = [t.name for t in response.templates]
self.assertTrue('adminsortable/change_list.html' in template_names,
'adminsortable/change_list.html was not rendered')
self.assertTrue('adminsortable/shared/javascript_includes.html'
in template_names,
'JavaScript includes for adminsortable change list '
'were not rendered')
def test_adminsortable_change_list_sorting_fails_if_not_ajax(self):
logged_in = self.client.login(username=self.user.username,
password=self.user_raw_password)
self.assertTrue(logged_in, 'User is not logged in')
category1, category2, category3 = self.make_test_categories()
#make a normal POST
response = self.client.post(self.get_sorting_url(),
data=self.get_category_indexes(category1, category2, category3))
content = json.loads(response.content)
self.assertFalse(content.get('objects_sorted'),
'Objects should not have been sorted. An ajax post is required.')
def test_adminsortable_change_list_sorting_successful(self):
logged_in = self.client.login(username=self.user.username,
password=self.user_raw_password)
self.assertTrue(logged_in, 'User is not logged in')
#make categories
category1, category2, category3 = self.make_test_categories()
#make an Ajax POST
response = self.client.post(self.get_sorting_url(),
data=self.get_category_indexes(category3, category2, category1),
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
content = json.loads(response.content)
self.assertTrue(content.get('objects_sorted'),
'Objects should have been sorted.')
#assert order is correct
categories = Category.objects.all()
cat1 = categories[0]
cat2 = categories[1]
cat3 = categories[2]
self.assertEqual(cat1.order, 1,
'First category returned should have order == 1')
self.assertEqual(cat1.pk, 3,
'Category ID 3 should have been first in queryset')
self.assertEqual(cat2.order, 2,
'Second category returned should have order == 2')
self.assertEqual(cat2.pk, 2,
'Category ID 2 should have been second in queryset')
self.assertEqual(cat3.order, 3,
'Third category returned should have order == 3')
self.assertEqual(cat3.pk, 1,
'Category ID 1 should have been third in queryset')