diff --git a/adminsortable/utils.py b/adminsortable/utils.py index 82141d9..85237bd 100644 --- a/adminsortable/utils.py +++ b/adminsortable/utils.py @@ -1,9 +1,13 @@ from .models import Sortable, SortableForeignKey +def check_inheritance(obj): + return issubclass(type(obj), Sortable) + + def get_is_sortable(objects): if objects: - if issubclass(type(objects[0]), Sortable): + if check_inheritance(objects[0]): if objects.count() > 1: return True return False @@ -11,7 +15,8 @@ def get_is_sortable(objects): def is_self_referential(cls): cls_type = type(cls) - sortable_subclass = issubclass(cls_type, Sortable) + sortable_subclass = check_inheritance(cls_type) + # sortable_subclass = issubclass(cls_type, Sortable) sortable_foreign_key_subclass = issubclass(cls_type, SortableForeignKey) if sortable_foreign_key_subclass and not sortable_subclass: return True @@ -20,11 +25,12 @@ def is_self_referential(cls): def check_model_is_sortable(cls): if cls: - if is_self_referential(cls): - objects = cls.model.objects - else: - objects = cls.objects + if check_inheritance(cls): + if is_self_referential(cls): + objects = cls.model.objects + else: + objects = cls.objects - if objects.count() > 1: - return True + if objects.count() > 1: + return True return False diff --git a/python3_sample_project/app/__init__.py b/python3_sample_project/app/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/python3_sample_project/app/admin.py b/python3_sample_project/app/admin.py deleted file mode 100644 index 580bf5a..0000000 --- a/python3_sample_project/app/admin.py +++ /dev/null @@ -1,84 +0,0 @@ -from django.contrib import admin - -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, - Component, Person, NonSortableCategory, SortableCategoryWidget, - SortableNonInlineCategory, SelfReferentialCategory) - - -admin.site.register(Category, SortableAdmin) - - -class ComponentInline(SortableStackedInline): - model = Component - - def queryset(self, request): - qs = super(ComponentInline, self).queryset( - request).exclude(title__icontains='2') - if get_is_sortable(qs): - self.model.is_sortable = True - else: - self.model.is_sortable = False - return qs - - -class WidgetAdmin(SortableAdmin): - def queryset(self, request): - """ - A simple example demonstrating that adminsortable works even in - situations where you need to filter the queryset in admin. Here, - we are just filtering out `widget` instances with an pk higher - than 3 - """ - qs = super(WidgetAdmin, self).queryset(request) - return qs.filter(id__lte=3) - - inlines = [ComponentInline] - -admin.site.register(Widget, WidgetAdmin) - - -class CreditInline(SortableTabularInline): - model = Credit - extra = 1 - - -class NoteInline(SortableStackedInline): - model = Note - extra = 0 - - -class GenericNoteInline(SortableGenericStackedInline): - model = GenericNote - extra = 0 - - -class ProjectAdmin(SortableAdmin): - inlines = [CreditInline, NoteInline, GenericNoteInline] - list_display = ['__str__', 'category'] - -admin.site.register(Project, ProjectAdmin) - - -class PersonAdmin(SortableAdmin): - list_display = ['__str__', 'is_board_member'] - -admin.site.register(Person, PersonAdmin) - - -class SortableCategoryWidgetInline(SortableStackedInline): - model = SortableCategoryWidget - extra = 0 - - -class NonSortableCategoryAdmin(NonSortableParentAdmin): - inlines = [SortableCategoryWidgetInline] - -admin.site.register(NonSortableCategory, NonSortableCategoryAdmin) - -admin.site.register(SortableNonInlineCategory, SortableAdmin) - -admin.site.register(SelfReferentialCategory, SortableAdmin) diff --git a/python3_sample_project/app/fixtures/data.json b/python3_sample_project/app/fixtures/data.json deleted file mode 100644 index 25f6d7d..0000000 --- a/python3_sample_project/app/fixtures/data.json +++ /dev/null @@ -1,52 +0,0 @@ -[ -{ - "fields": { - "is_board_member": true, - "order": 1, - "first_name": "Brandon", - "last_name": "Taylor" - }, - "pk": 1, - "model": "app.person" -}, -{ - "fields": { - "is_board_member": true, - "order": 2, - "first_name": "Kerri", - "last_name": "Taylor" - }, - "pk": 2, - "model": "app.person" -}, -{ - "fields": { - "is_board_member": false, - "order": 3, - "first_name": "Sarah", - "last_name": "Taylor" - }, - "pk": 3, - "model": "app.person" -}, -{ - "fields": { - "is_board_member": false, - "order": 4, - "first_name": "Renna", - "last_name": "Taylor" - }, - "pk": 4, - "model": "app.person" -}, -{ - "fields": { - "is_board_member": false, - "order": 5, - "first_name": "Jake", - "last_name": "Taylor" - }, - "pk": 5, - "model": "app.person" -} -] diff --git a/python3_sample_project/app/migrations/0001_initial.py b/python3_sample_project/app/migrations/0001_initial.py deleted file mode 100644 index ab0527a..0000000 --- a/python3_sample_project/app/migrations/0001_initial.py +++ /dev/null @@ -1,146 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations -import adminsortable.fields - - -class Migration(migrations.Migration): - - dependencies = [ - ('contenttypes', '0001_initial'), - ] - - operations = [ - migrations.CreateModel( - name='Category', - fields=[ - ('id', models.AutoField(auto_created=True, serialize=False, primary_key=True, verbose_name='ID')), - ('order', models.PositiveIntegerField(editable=False, default=1, db_index=True)), - ('title', models.CharField(max_length=50)), - ], - options={ - 'ordering': ['order'], - 'verbose_name_plural': 'Categories', - 'abstract': False, - }, - bases=(models.Model,), - ), - migrations.CreateModel( - name='Component', - fields=[ - ('id', models.AutoField(auto_created=True, serialize=False, primary_key=True, verbose_name='ID')), - ('order', models.PositiveIntegerField(editable=False, default=1, db_index=True)), - ('title', models.CharField(max_length=50)), - ], - options={ - 'ordering': ['order'], - 'abstract': False, - }, - bases=(models.Model,), - ), - migrations.CreateModel( - name='Credit', - fields=[ - ('id', models.AutoField(auto_created=True, serialize=False, primary_key=True, verbose_name='ID')), - ('order', models.PositiveIntegerField(editable=False, default=1, db_index=True)), - ('first_name', models.CharField(max_length=30)), - ('last_name', models.CharField(max_length=30)), - ], - options={ - 'ordering': ['order'], - 'abstract': False, - }, - bases=(models.Model,), - ), - migrations.CreateModel( - name='GenericNote', - fields=[ - ('id', models.AutoField(auto_created=True, serialize=False, primary_key=True, verbose_name='ID')), - ('order', models.PositiveIntegerField(editable=False, default=1, db_index=True)), - ('title', models.CharField(max_length=50)), - ('object_id', models.PositiveIntegerField(verbose_name='Content id')), - ('content_type', models.ForeignKey(related_name='generic_notes', verbose_name='Content type', to='contenttypes.ContentType')), - ], - options={ - 'ordering': ['order'], - 'abstract': False, - }, - bases=(models.Model,), - ), - migrations.CreateModel( - name='Note', - fields=[ - ('id', models.AutoField(auto_created=True, serialize=False, primary_key=True, verbose_name='ID')), - ('order', models.PositiveIntegerField(editable=False, default=1, db_index=True)), - ('text', models.CharField(max_length=100)), - ], - options={ - 'ordering': ['order'], - 'abstract': False, - }, - bases=(models.Model,), - ), - migrations.CreateModel( - name='Person', - fields=[ - ('id', models.AutoField(auto_created=True, serialize=False, primary_key=True, verbose_name='ID')), - ('order', models.PositiveIntegerField(editable=False, default=1, db_index=True)), - ('first_name', models.CharField(max_length=50)), - ('last_name', models.CharField(max_length=50)), - ('is_board_member', models.BooleanField(verbose_name='Board Member', default=False)), - ], - options={ - 'ordering': ['order'], - 'verbose_name_plural': 'People', - 'abstract': False, - }, - bases=(models.Model,), - ), - migrations.CreateModel( - name='Project', - fields=[ - ('id', models.AutoField(auto_created=True, serialize=False, primary_key=True, verbose_name='ID')), - ('order', models.PositiveIntegerField(editable=False, default=1, db_index=True)), - ('title', models.CharField(max_length=50)), - ('description', models.TextField()), - ('category', adminsortable.fields.SortableForeignKey(to='app.Category')), - ], - options={ - 'ordering': ['order'], - 'abstract': False, - }, - bases=(models.Model,), - ), - migrations.CreateModel( - name='Widget', - fields=[ - ('id', models.AutoField(auto_created=True, serialize=False, primary_key=True, verbose_name='ID')), - ('order', models.PositiveIntegerField(editable=False, default=1, db_index=True)), - ('title', models.CharField(max_length=50)), - ], - options={ - 'ordering': ['order'], - 'abstract': False, - }, - bases=(models.Model,), - ), - migrations.AddField( - model_name='note', - name='project', - field=models.ForeignKey(to='app.Project'), - preserve_default=True, - ), - migrations.AddField( - model_name='credit', - name='project', - field=models.ForeignKey(to='app.Project'), - preserve_default=True, - ), - migrations.AddField( - model_name='component', - name='widget', - field=adminsortable.fields.SortableForeignKey(to='app.Widget'), - preserve_default=True, - ), - ] diff --git a/python3_sample_project/app/migrations/0002_nonsortablecategory_sortablecategorywidget.py b/python3_sample_project/app/migrations/0002_nonsortablecategory_sortablecategorywidget.py deleted file mode 100644 index 53db365..0000000 --- a/python3_sample_project/app/migrations/0002_nonsortablecategory_sortablecategorywidget.py +++ /dev/null @@ -1,44 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations -import adminsortable.fields - - -class Migration(migrations.Migration): - - dependencies = [ - ('app', '0001_initial'), - ] - - operations = [ - migrations.CreateModel( - name='NonSortableCategory', - fields=[ - ('id', models.AutoField(serialize=False, auto_created=True, verbose_name='ID', primary_key=True)), - ('title', models.CharField(max_length=50)), - ], - options={ - 'verbose_name': 'Non-Sortable Category', - 'verbose_name_plural': 'Non-Sortable Categories', - 'abstract': False, - }, - bases=(models.Model,), - ), - migrations.CreateModel( - name='SortableCategoryWidget', - fields=[ - ('id', models.AutoField(serialize=False, auto_created=True, verbose_name='ID', primary_key=True)), - ('order', models.PositiveIntegerField(db_index=True, editable=False, default=1)), - ('title', models.CharField(max_length=50)), - ('non_sortable_category', adminsortable.fields.SortableForeignKey(to='app.NonSortableCategory')), - ], - options={ - 'ordering': ['order'], - 'verbose_name': 'Sortable Category Widget', - 'abstract': False, - 'verbose_name_plural': 'Sortable Category Widgets', - }, - bases=(models.Model,), - ), - ] diff --git a/python3_sample_project/app/migrations/0003_selfreferentialcategory.py b/python3_sample_project/app/migrations/0003_selfreferentialcategory.py deleted file mode 100644 index bd9a878..0000000 --- a/python3_sample_project/app/migrations/0003_selfreferentialcategory.py +++ /dev/null @@ -1,28 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations -import adminsortable.fields - - -class Migration(migrations.Migration): - - dependencies = [ - ('app', '0002_nonsortablecategory_sortablecategorywidget'), - ] - - operations = [ - migrations.CreateModel( - name='SelfReferentialCategory', - fields=[ - ('id', models.AutoField(serialize=False, auto_created=True, primary_key=True, verbose_name='ID')), - ('order', models.PositiveIntegerField(db_index=True, editable=False, default=1)), - ('title', models.CharField(max_length=50)), - ('child', adminsortable.fields.SortableForeignKey(to='app.SelfReferentialCategory')), - ], - options={ - 'abstract': False, - }, - bases=(models.Model,), - ), - ] diff --git a/python3_sample_project/app/migrations/0004_auto_20141029_1612.py b/python3_sample_project/app/migrations/0004_auto_20141029_1612.py deleted file mode 100644 index 1f7e76f..0000000 --- a/python3_sample_project/app/migrations/0004_auto_20141029_1612.py +++ /dev/null @@ -1,24 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations -import adminsortable.fields - - -class Migration(migrations.Migration): - - dependencies = [ - ('app', '0003_selfreferentialcategory'), - ] - - operations = [ - migrations.AlterModelOptions( - name='selfreferentialcategory', - options={'verbose_name_plural': 'Sortable Referential Categories', 'verbose_name': 'Sortable Referential Category'}, - ), - migrations.AlterField( - model_name='selfreferentialcategory', - name='child', - field=adminsortable.fields.SortableForeignKey(to='app.SelfReferentialCategory', null=True, blank=True), - ), - ] diff --git a/python3_sample_project/app/migrations/__init__.py b/python3_sample_project/app/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/python3_sample_project/app/models.py b/python3_sample_project/app/models.py deleted file mode 100644 index 34127b1..0000000 --- a/python3_sample_project/app/models.py +++ /dev/null @@ -1,169 +0,0 @@ -from django.contrib.contenttypes import generic -from django.contrib.contenttypes.models import ContentType -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' - - def __str__(self): - return self.title - - -# A model with an override of its queryset for admin -class Widget(SimpleModel, Sortable): - class Meta(Sortable.Meta): - pass - - def __str__(self): - return self.title - - -# 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 __str__(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 __str__(self): - return self.text - - -# A generic bound model -class GenericNote(SimpleModel, Sortable): - content_type = models.ForeignKey(ContentType, - verbose_name=u"Content type", related_name="generic_notes") - object_id = models.PositiveIntegerField(u"Content id") - content_object = generic.GenericForeignKey(ct_field='content_type', - fk_field='object_id') - - class Meta(Sortable.Meta): - pass - - def __str__(self): - return u'{0}: {1}'.format(self.title, self.content_object) - - -# An model registered as an inline that has a custom queryset -class Component(SimpleModel, Sortable): - class Meta(Sortable.Meta): - pass - - widget = SortableForeignKey(Widget) - - def __str__(self): - return self.title - - -class Person(Sortable): - class Meta(Sortable.Meta): - verbose_name_plural = 'People' - - first_name = models.CharField(max_length=50) - last_name = models.CharField(max_length=50) - is_board_member = models.BooleanField('Board Member', default=False) - - # Sorting Filters allow you to set up sub-sets of objects that need - # to have independent sorting. They are listed in order, from left - # to right in the sorting change list. You can use any standard - # Django ORM filter method. - sorting_filters = ( - ('Board Members', {'is_board_member': True}), - ('Non-Board Members', {'is_board_member': False}), - ) - - def __str__(self): - return '{0} {1}'.format(self.first_name, self.last_name) - - -class NonSortableCategory(SimpleModel): - class Meta(SimpleModel.Meta): - verbose_name = 'Non-Sortable Category' - verbose_name_plural = 'Non-Sortable Categories' - - def __str__(self): - return self.title - - -class SortableCategoryWidget(SimpleModel, Sortable): - class Meta(Sortable.Meta): - verbose_name = 'Sortable Category Widget' - verbose_name_plural = 'Sortable Category Widgets' - - non_sortable_category = SortableForeignKey(NonSortableCategory) - - def __str__(self): - return self.title - - -class SortableNonInlineCategory(SimpleModel, Sortable): - """Example of a model that is sortable, but has a SortableForeignKey - that is *not* sortable, and is also not defined as an inline of the - SortableForeignKey field.""" - - class Meta(Sortable.Meta): - verbose_name = 'Sortable Non-Inline Category' - verbose_name_plural = 'Sortable Non-Inline Categories' - - non_sortable_category = SortableForeignKey(NonSortableCategory) - - def __str__(self): - return self.title - - -class SelfReferentialCategory(SimpleModel, Sortable): - class Meta(Sortable.Meta): - verbose_name = 'Sortable Referential Category' - verbose_name_plural = 'Sortable Referential Categories' - - child = SortableForeignKey('self', blank=True, null=True) - - def __str__(self): - if self.child: - return '{} - {}'.format(self.child, self.title) - return self.title diff --git a/python3_sample_project/app/tests.py b/python3_sample_project/app/tests.py deleted file mode 100644 index 66e1a4b..0000000 --- a/python3_sample_project/app/tests.py +++ /dev/null @@ -1,158 +0,0 @@ -import http.client -import json - -from django.contrib.auth.models import User -from django.test import TestCase -from django.test.client import Client, RequestFactory - -from adminsortable.utils import get_is_sortable -from app.models import Category, Person - - -class SortableTestCase(TestCase): - fixtures = ['data.json'] - - 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() - - people = Person.objects.all() - self.first_person = people[0] - self.second_person = people[1] - - 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(get_is_sortable(Category.objects.all()), - '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(get_is_sortable(Category.objects.all()), - '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, http.client.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 '/admin/app/category/sorting/do-sorting/{0}/'.format( - 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('/admin/app/category/sort/') - self.assertEqual(response.status_code, http.client.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') - - 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.decode(encoding='UTF-8'), - 'latin-1') - 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.decode(encoding='UTF-8'), - 'latin-1') - 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') - - def test_get_next(self): - result = self.first_person.get_next() - - self.assertEqual(self.second_person, result, 'Next person should ' - 'be "{}"'.format(self.second_person)) - - def test_get_previous(self): - result = self.second_person.get_previous() - - self.assertEqual(self.first_person, result, 'Previous person should ' - 'be "{}"'.format(self.first_person)) diff --git a/python3_sample_project/database/python3-test-project.sqlite3 b/python3_sample_project/database/python3-test-project.sqlite3 deleted file mode 100644 index 95395d0..0000000 Binary files a/python3_sample_project/database/python3-test-project.sqlite3 and /dev/null differ diff --git a/python3_sample_project/manage.py b/python3_sample_project/manage.py deleted file mode 100644 index 08821aa..0000000 --- a/python3_sample_project/manage.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env python -import os -import sys - -# Adds the adminsortable package from the cloned repository instead of -# site_packages -sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - -if __name__ == "__main__": - os.environ.setdefault("DJANGO_SETTINGS_MODULE", - "python3_sample_project.settings") - - from django.core.management import execute_from_command_line - - execute_from_command_line(sys.argv) diff --git a/python3_sample_project/python3_sample_project/__init__.py b/python3_sample_project/python3_sample_project/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/python3_sample_project/python3_sample_project/settings.py b/python3_sample_project/python3_sample_project/settings.py deleted file mode 100644 index 5d729fe..0000000 --- a/python3_sample_project/python3_sample_project/settings.py +++ /dev/null @@ -1,99 +0,0 @@ -""" -Django settings for python3_sample_project project. - -For more information on this file, see -https://docs.djangoproject.com/en/1.7/topics/settings/ - -For the full list of settings and their values, see -https://docs.djangoproject.com/en/1.7/ref/settings/ -""" - -# Build paths inside the project like this: os.path.join(BASE_DIR, ...) -from .utils import map_path - - -# Quick-start development settings - unsuitable for production -# See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/ - -# SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = 'k^uy@(9tieoj3d%o=09ph$b&gu+5q@9h$f(6l7@h2ak*0@y9%w' - -# SECURITY WARNING: don't run with debug turned on in production! -DEBUG = True - -TEMPLATE_DEBUG = True - -ALLOWED_HOSTS = [] - - -# Application definition - -INSTALLED_APPS = ( - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - - 'adminsortable', - 'app', -) - -MIDDLEWARE_CLASSES = ( - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', -) - -ROOT_URLCONF = 'python3_sample_project.urls' - -WSGI_APPLICATION = 'python3_sample_project.wsgi.application' - - -# Database -# https://docs.djangoproject.com/en/1.7/ref/settings/#databases - -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': map_path('database/python3-test-project.sqlite3'), - }, - 'test': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': map_path('database/test-python3-test-project.sqlite3'), - } -} - -# Internationalization -# https://docs.djangoproject.com/en/1.7/topics/i18n/ - -LANGUAGE_CODE = 'en-us' - -TIME_ZONE = 'UTC' - -USE_I18N = True - -USE_L10N = True - -USE_TZ = True - - -# Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/1.7/howto/static-files/ - -STATIC_URL = '/static/' - -STATICFILES_FINDERS = ( - 'django.contrib.staticfiles.finders.FileSystemFinder', - 'django.contrib.staticfiles.finders.AppDirectoriesFinder', -) - -TEMPLATE_LOADERS = ( - 'django.template.loaders.filesystem.Loader', - 'django.template.loaders.app_directories.Loader', -) diff --git a/python3_sample_project/python3_sample_project/urls.py b/python3_sample_project/python3_sample_project/urls.py deleted file mode 100644 index c4b2ed2..0000000 --- a/python3_sample_project/python3_sample_project/urls.py +++ /dev/null @@ -1,10 +0,0 @@ -from django.conf.urls import patterns, include, url -from django.contrib import admin - -urlpatterns = patterns('', - # Examples: - # url(r'^$', 'python3_sample_project.views.home', name='home'), - # url(r'^blog/', include('blog.urls')), - - url(r'^admin/', include(admin.site.urls)), -) diff --git a/python3_sample_project/python3_sample_project/utils.py b/python3_sample_project/python3_sample_project/utils.py deleted file mode 100644 index 4178ad9..0000000 --- a/python3_sample_project/python3_sample_project/utils.py +++ /dev/null @@ -1,6 +0,0 @@ -import os - - -def map_path(directory_name): - return os.path.join(os.path.dirname(__file__), - '../' + directory_name).replace('\\', '/') diff --git a/python3_sample_project/python3_sample_project/wsgi.py b/python3_sample_project/python3_sample_project/wsgi.py deleted file mode 100644 index 1256fa7..0000000 --- a/python3_sample_project/python3_sample_project/wsgi.py +++ /dev/null @@ -1,14 +0,0 @@ -""" -WSGI config for python3_sample_project project. - -It exposes the WSGI callable as a module-level variable named ``application``. - -For more information on this file, see -https://docs.djangoproject.com/en/1.7/howto/deployment/wsgi/ -""" - -import os -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "python3_sample_project.settings") - -from django.core.wsgi import get_wsgi_application -application = get_wsgi_application() diff --git a/sample_project/app/admin.py b/sample_project/app/admin.py index 3fca3f2..437b052 100644 --- a/sample_project/app/admin.py +++ b/sample_project/app/admin.py @@ -71,13 +71,13 @@ class ProjectAdmin(SortableAdmin): CreditInline, NoteInline, GenericNoteInline, NonSortableCreditInline, NonSortableNoteInline ] - list_display = ['__unicode__', 'category'] + list_display = ['__str__', 'category'] admin.site.register(Project, ProjectAdmin) class PersonAdmin(SortableAdmin): - list_display = ['__unicode__', 'is_board_member'] + list_display = ['__str__', 'is_board_member'] admin.site.register(Person, PersonAdmin) diff --git a/sample_project/app/models.py b/sample_project/app/models.py index d882930..4a9754e 100644 --- a/sample_project/app/models.py +++ b/sample_project/app/models.py @@ -1,18 +1,20 @@ from django.contrib.contenttypes import generic from django.contrib.contenttypes.models import ContentType from django.db import models +from django.utils.encoding import python_2_unicode_compatible from adminsortable.fields import SortableForeignKey from adminsortable.models import Sortable +@python_2_unicode_compatible class SimpleModel(models.Model): class Meta: abstract = True title = models.CharField(max_length=50) - def __unicode__(self): + def __str__(self): return self.title @@ -28,11 +30,12 @@ class Category(SimpleModel, Sortable): # A model with an override of its queryset for admin +@python_2_unicode_compatible class Widget(SimpleModel, Sortable): class Meta(Sortable.Meta): pass - def __unicode__(self): + def __str__(self): return self.title @@ -47,6 +50,7 @@ class Project(SimpleModel, Sortable): # Registered as a tabular inline on `Project` +@python_2_unicode_compatible class Credit(Sortable): class Meta(Sortable.Meta): pass @@ -55,11 +59,12 @@ class Credit(Sortable): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30) - def __unicode__(self): + def __str__(self): return '{0} {1}'.format(self.first_name, self.last_name) # Registered as a stacked inline on `Project` +@python_2_unicode_compatible class Note(Sortable): class Meta(Sortable.Meta): pass @@ -67,30 +72,33 @@ class Note(Sortable): project = models.ForeignKey(Project) text = models.CharField(max_length=100) - def __unicode__(self): + def __str__(self): return self.text # Registered as a tabular inline on `Project` which can't be sorted +@python_2_unicode_compatible class NonSortableCredit(models.Model): project = models.ForeignKey(Project) first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30) - def __unicode__(self): + def __str__(self): return '{0} {1}'.format(self.first_name, self.last_name) # Registered as a stacked inline on `Project` which can't be sorted +@python_2_unicode_compatible class NonSortableNote(models.Model): project = models.ForeignKey(Project) text = models.CharField(max_length=100) - def __unicode__(self): + def __str__(self): return self.text # A generic bound model +@python_2_unicode_compatible class GenericNote(SimpleModel, Sortable): content_type = models.ForeignKey(ContentType, verbose_name=u"Content type", related_name="generic_notes") @@ -101,22 +109,23 @@ class GenericNote(SimpleModel, Sortable): class Meta(Sortable.Meta): pass - def __unicode__(self): + def __str__(self): return u'{0}: {1}'.format(self.title, self.content_object) # An model registered as an inline that has a custom queryset +@python_2_unicode_compatible class Component(SimpleModel, Sortable): class Meta(Sortable.Meta): pass widget = SortableForeignKey(Widget) - def __unicode__(self): + def __str__(self): return self.title - +@python_2_unicode_compatible class Person(Sortable): class Meta(Sortable.Meta): verbose_name_plural = 'People' @@ -134,19 +143,21 @@ class Person(Sortable): ('Non-Board Members', {'is_board_member': False}), ) - def __unicode__(self): + def __str__(self): return '{0} {1}'.format(self.first_name, self.last_name) +@python_2_unicode_compatible class NonSortableCategory(SimpleModel): class Meta(SimpleModel.Meta): verbose_name = 'Non-Sortable Category' verbose_name_plural = 'Non-Sortable Categories' - def __unicode__(self): + def __str__(self): return self.title +@python_2_unicode_compatible class SortableCategoryWidget(SimpleModel, Sortable): class Meta(Sortable.Meta): verbose_name = 'Sortable Category Widget' @@ -154,10 +165,11 @@ class SortableCategoryWidget(SimpleModel, Sortable): non_sortable_category = SortableForeignKey(NonSortableCategory) - def __unicode__(self): + def __str__(self): return self.title +@python_2_unicode_compatible class SortableNonInlineCategory(SimpleModel, Sortable): """Example of a model that is sortable, but has a SortableForeignKey that is *not* sortable, and is also not defined as an inline of the @@ -169,5 +181,5 @@ class SortableNonInlineCategory(SimpleModel, Sortable): non_sortable_category = SortableForeignKey(NonSortableCategory) - def __unicode__(self): + def __str__(self): return self.title diff --git a/sample_project/app/tests.py b/sample_project/app/tests.py index 5c5b1e2..9871a43 100644 --- a/sample_project/app/tests.py +++ b/sample_project/app/tests.py @@ -1,4 +1,8 @@ -import httplib +try: + import httplib +except ImportError: + import http.client as httplib + import json from django.contrib.auth.models import User @@ -118,7 +122,8 @@ class SortableTestCase(TestCase): #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) + content = json.loads(response.content.decode(encoding='UTF-8'), + 'latin-1') self.assertFalse(content.get('objects_sorted'), 'Objects should not have been sorted. An ajax post is required.') @@ -134,7 +139,8 @@ class SortableTestCase(TestCase): 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) + content = json.loads(response.content.decode(encoding='UTF-8'), + 'latin-1') self.assertTrue(content.get('objects_sorted'), 'Objects should have been sorted.') diff --git a/sample_project/database/test_project.sqlite b/sample_project/database/test_project.sqlite index 6bbcbec..0fe8110 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 e421ffb..f70e113 100644 --- a/sample_project/sample_project/settings.py +++ b/sample_project/sample_project/settings.py @@ -1,5 +1,11 @@ # Django settings for test_project project. -from utils import map_path +import os + + +def map_path(directory_name): + return os.path.join(os.path.dirname(__file__), + '../' + directory_name).replace('\\', '/') + DEBUG = True TEMPLATE_DEBUG = DEBUG