Merge pull request #168 from jaap3/modernize

Remove fallbacks for Django < 1.8
master
Brandon Taylor 2017-03-21 06:38:17 -04:00 committed by GitHub
commit 2ec2722c67
12 changed files with 105 additions and 369 deletions

View File

@ -3,24 +3,11 @@ import json
from django import VERSION
from django.conf import settings
try:
from django.conf.urls import url
except ImportError:
# Django < 1.4
from django.conf.urls.defaults import url
from django.conf.urls import url
from django.contrib.admin import ModelAdmin, TabularInline, StackedInline
from django.contrib.admin.options import InlineModelAdmin
try:
from django.contrib.contenttypes.admin import (GenericStackedInline,
GenericTabularInline)
except:
# Django < 1.7
from django.contrib.contenttypes.generic import (GenericStackedInline,
GenericTabularInline)
from django.contrib.contenttypes.admin import (GenericStackedInline,
GenericTabularInline)
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import PermissionDenied
from django.http import HttpResponse, Http404
@ -51,13 +38,7 @@ class SortableAdminBase(object):
its sort order can be changed. This view adds a link to the
object_tools block to take people to the view to change the sorting.
"""
try:
qs_method = getattr(self, 'get_queryset', self.queryset)
except AttributeError:
qs_method = self.get_queryset
if get_is_sortable(qs_method(request)):
if get_is_sortable(self.get_queryset(request)):
self.change_list_template = \
self.sortable_change_list_with_sort_link_template
self.is_sortable = True
@ -101,12 +82,7 @@ class SortableAdmin(SortableAdminBase, ModelAdmin):
def get_urls(self):
urls = super(SortableAdmin, self).get_urls()
opts = self.model._meta
try:
info = opts.app_label, opts.model_name
except AttributeError:
# Django < 1.7
info = opts.app_label, opts.model_name
info = self.model._meta.app_label, self.model._meta.model_name
# this ajax view changes the order of instances of the model type
admin_do_sorting_url = url(
@ -150,11 +126,7 @@ class SortableAdmin(SortableAdminBase, ModelAdmin):
pass
# Apply any sort filters to create a subset of sortable objects
try:
qs_method = getattr(self, 'get_queryset', self.queryset)
except AttributeError:
qs_method = self.get_queryset
objects = qs_method(request).filter(**filters)
objects = self.get_queryset(request).filter(**filters)
# Determine if we need to regroup objects relative to a
# foreign key specified on the model class that is extending Sortable.
@ -169,7 +141,11 @@ class SortableAdmin(SortableAdminBase, ModelAdmin):
for field in self.model._meta.fields:
if isinstance(field, SortableForeignKey):
sortable_by_fk = field.rel.to
try:
sortable_by_fk = field.remote_field.model
except AttributeError:
# Django < 1.9
sortable_by_fk = field.rel.to
sortable_by_field_name = field.name.lower()
sortable_by_class_is_sortable = sortable_by_fk.objects.count() >= 2
@ -206,9 +182,6 @@ class SortableAdmin(SortableAdminBase, ModelAdmin):
try:
order_field_name = opts.model._meta.ordering[0]
except (AttributeError, IndexError):
# for Django 1.5.x
order_field_name = opts.ordering[0]
except (AttributeError, IndexError):
order_field_name = 'order'
@ -328,27 +301,18 @@ class SortableInlineBase(SortableAdminBase, InlineModelAdmin):
' (or Sortable for legacy implementations)')
def get_queryset(self, request):
if VERSION < (1, 6):
qs = super(SortableInlineBase, self).queryset(request)
else:
qs = super(SortableInlineBase, self).get_queryset(request)
qs = super(SortableInlineBase, self).get_queryset(request)
if get_is_sortable(qs):
self.model.is_sortable = True
else:
self.model.is_sortable = False
return qs
if VERSION < (1, 6):
queryset = get_queryset
class SortableTabularInline(TabularInline, SortableInlineBase):
"""Custom template that enables sorting for tabular inlines"""
if VERSION >= (1, 10):
template = 'adminsortable/edit_inline/tabular-1.10.x.html'
elif VERSION < (1, 6):
template = 'adminsortable/edit_inline/tabular-1.5.x.html'
else:
template = 'adminsortable/edit_inline/tabular.html'
@ -357,8 +321,6 @@ class SortableStackedInline(StackedInline, SortableInlineBase):
"""Custom template that enables sorting for stacked inlines"""
if VERSION >= (1, 10):
template = 'adminsortable/edit_inline/stacked-1.10.x.html'
elif VERSION < (1, 6):
template = 'adminsortable/edit_inline/stacked-1.5.x.html'
else:
template = 'adminsortable/edit_inline/stacked.html'
@ -367,8 +329,6 @@ class SortableGenericTabularInline(GenericTabularInline, SortableInlineBase):
"""Custom template that enables sorting for tabular inlines"""
if VERSION >= (1, 10):
template = 'adminsortable/edit_inline/tabular-1.10.x.html'
elif VERSION < (1, 6):
template = 'adminsortable/edit_inline/tabular-1.5.x.html'
else:
template = 'adminsortable/edit_inline/tabular.html'
@ -377,7 +337,5 @@ class SortableGenericStackedInline(GenericStackedInline, SortableInlineBase):
"""Custom template that enables sorting for stacked inlines"""
if VERSION >= (1, 10):
template = 'adminsortable/edit_inline/stacked-1.10.x.html'
elif VERSION < (1, 6):
template = 'adminsortable/edit_inline/stacked-1.5.x.html'
else:
template = 'adminsortable/edit_inline/stacked.html'

View File

@ -7,14 +7,4 @@ class SortableForeignKey(ForeignKey):
This field replaces previous functionality where `sortable_by` was
defined as a model property that specified another model class.
"""
def south_field_triple(self):
try:
from south.modelsinspector import introspector
cls_name = '{0}.{1}'.format(
self.__class__.__module__,
self.__class__.__name__)
args, kwargs = introspector(self)
return cls_name, args, kwargs
except ImportError:
pass
pass

View File

@ -1,92 +0,0 @@
{% load i18n admin_modify adminsortable_tags admin_urls %}
{% load static from staticfiles %}
<div class="inline-group" id="{{ inline_admin_formset.formset.prefix }}-group">
<h2>{{ inline_admin_formset.opts.verbose_name_plural|title }} {% if inline_admin_formset.formset.initial_form_count > 1 %} - {% trans "drag and drop to change order" %}{% endif %}</h2>
{{ inline_admin_formset.formset.management_form }}
{{ inline_admin_formset.formset.non_form_errors }}
{% for inline_admin_form in inline_admin_formset %}<div class="inline-related{% if inline_admin_form.original %} has_original{% endif %}{% if forloop.last %} empty-form last-related{% endif %}" id="{{ inline_admin_formset.formset.prefix }}-{% if not forloop.last %}{{ forloop.counter0 }}{% else %}empty{% endif %}">
<h3>
{% if inline_admin_form.original %}
{% with initial_forms_count=inline_admin_formset.formset.management_form.INITIAL_FORMS.value %}
<i class="fa fa-{% if forloop.first %}sort-desc{% elif forloop.counter == initial_forms_count %}sort-asc{% else %}sort{% endif %}"></i>
{% endwith %}
{% endif %}
<b>{{ inline_admin_formset.opts.verbose_name|title }}:</b>&nbsp;<span class="inline_label">{% if inline_admin_form.original %}{{ inline_admin_form.original }}{% else %}#{{ forloop.counter }}{% endif %}</span>
{% if inline_admin_form.show_url %}<a href="../../../r/{{ inline_admin_form.original_content_type_id }}/{{ inline_admin_form.original.id }}/">{% trans "View on site" %}</a>{% endif %}
{% if inline_admin_formset.formset.can_delete and inline_admin_form.original %}<span class="delete">{{ inline_admin_form.deletion_field.field }} {{ inline_admin_form.deletion_field.label_tag }}</span>{% endif %}
</h3>
{% if inline_admin_form.form.non_field_errors %}{{ inline_admin_form.form.non_field_errors }}{% endif %}
{% for fieldset in inline_admin_form %}
{% include "admin/includes/fieldset.html" %}
{% endfor %}
{% if inline_admin_form.has_auto_field %}{{ inline_admin_form.pk_field.field }}{% endif %}
{{ inline_admin_form.fk_field.field }}
{% if inline_admin_form.original %}
<input type="hidden" name="admin_sorting_url" value="{% url opts|admin_urlname:'do_sorting' inline_admin_form.original.model_type_id %}" />
{% endif %}
</div>{% endfor %}
</div>
<script type="text/javascript">
(function($) {
$(document).ready(function() {
var rows = "#{{ inline_admin_formset.formset.prefix }}-group .inline-related";
var updateInlineLabel = function(row) {
$(rows).find(".inline_label").each(function(i) {
var count = i + 1;
$(this).html($(this).html().replace(/(#\d+)/g, "#" + count));
});
}
var reinitDateTimeShortCuts = function() {
// Reinitialize the calendar and clock widgets by force, yuck.
if (typeof DateTimeShortcuts != "undefined") {
$(".datetimeshortcuts").remove();
DateTimeShortcuts.init();
}
}
var updateSelectFilter = function() {
// If any SelectFilter widgets were added, instantiate a new instance.
if (typeof SelectFilter != "undefined"){
$(".selectfilter").each(function(index, value){
var namearr = value.name.split('-');
SelectFilter.init(value.id, namearr[namearr.length-1], false, "{% static 'admin/' %}");
});
$(".selectfilterstacked").each(function(index, value){
var namearr = value.name.split('-');
SelectFilter.init(value.id, namearr[namearr.length-1], true, "{% static 'admin/' %}");
});
}
}
var initPrepopulatedFields = function(row) {
row.find('.prepopulated_field').each(function() {
var field = $(this);
var input = field.find('input, select, textarea');
var dependency_list = input.data('dependency_list') || [];
var dependencies = [];
$.each(dependency_list, function(i, field_name) {
dependencies.push('#' + row.find(field_name).find('input, select, textarea').attr('id'));
});
if (dependencies.length) {
input.prepopulate(dependencies, input.attr('maxlength'));
}
});
}
$(rows).formset({
prefix: "{{ inline_admin_formset.formset.prefix }}",
addText: "{% blocktrans with inline_admin_formset.opts.verbose_name|title as verbose_name %}Add another {{ verbose_name }}{% endblocktrans %}",
formCssClass: "dynamic-{{ inline_admin_formset.formset.prefix }}",
deleteCssClass: "inline-deletelink",
deleteText: "{% trans "Remove" %}",
emptyCssClass: "empty-form",
removed: updateInlineLabel,
added: (function(row) {
initPrepopulatedFields(row);
reinitDateTimeShortCuts();
updateSelectFilter();
updateInlineLabel(row);
})
});
});
})(django.jQuery);
</script>

View File

@ -1,136 +0,0 @@
{% load i18n admin_modify adminsortable_tags admin_urls %}
{% load static from staticfiles %}
<div class="inline-group" id="{{ inline_admin_formset.formset.prefix }}-group">
<div class="tabular inline-related {% if forloop.last %}last-related{% endif %}">
{{ inline_admin_formset.formset.management_form }}
<fieldset class="module">
<h2>{{ inline_admin_formset.opts.verbose_name_plural|capfirst }} {% if inline_admin_formset.formset.initial_form_count > 1 %} - {% trans "drag and drop to change order" %}{% endif %}</h2>
{{ inline_admin_formset.formset.non_form_errors }}
<table>
<thead><tr>
{% for field in inline_admin_formset.fields %}
{% if not field.widget.is_hidden %}
<th{% if forloop.first %} colspan="2"{% endif %}{% if field.required %} class="required"{% endif %}>{{ field.label|capfirst }}</th>
{% endif %}
{% endfor %}
{% if inline_admin_formset.formset.can_delete %}<th>{% trans "Delete?" %}</th>{% endif %}
</tr></thead>
<tbody>
{% for inline_admin_form in inline_admin_formset %}
{% if inline_admin_form.form.non_field_errors %}
<tr><td colspan="{{ inline_admin_form|cell_count }}">{{ inline_admin_form.form.non_field_errors }}</td></tr>
{% endif %}
<tr class="{% cycle "row1" "row2" %} {% if inline_admin_form.original or inline_admin_form.show_url %}has_original{% endif %}{% if forloop.last %} empty-form{% endif %}"
id="{{ inline_admin_formset.formset.prefix }}-{% if not forloop.last %}{{ forloop.counter0 }}{% else %}empty{% endif %}">
<td class="original">
{% if inline_admin_form.original or inline_admin_form.show_url %}<p>
{% with initial_forms_count=inline_admin_form.formset.management_form.INITIAL_FORMS.value %}
<i class="fa fa-{% if forloop.first %}sort-desc{% elif forloop.counter == initial_forms_count %}sort-asc{% else %}sort{% endif %}"></i>
{% endwith %}
{% if inline_admin_form.original %} {{ inline_admin_form.original }}{% endif %}
{% if inline_admin_form.show_url %}<a href="../../../r/{{ inline_admin_form.original_content_type_id }}/{{ inline_admin_form.original.id }}/">{% trans "View on site" %}</a>{% endif %}
</p>{% endif %}
{% if inline_admin_form.has_auto_field %}{{ inline_admin_form.pk_field.field }}{% endif %}
{{ inline_admin_form.fk_field.field }}
{% spaceless %}
{% for fieldset in inline_admin_form %}
{% for line in fieldset %}
{% for field in line %}
{% if field.is_hidden %} {{ field.field }} {% endif %}
{% endfor %}
{% endfor %}
{% endfor %}
{% endspaceless %}
{% if inline_admin_form.original %}
<input type="hidden" name="admin_sorting_url" value="{% url opts|admin_urlname:'do_sorting' inline_admin_form.original.model_type_id %}" />
{% endif %}
</td>
{% for fieldset in inline_admin_form %}
{% for line in fieldset %}
{% for field in line %}
<td class="{{ field.field.name }}">
{% if field.is_readonly %}
<p>{{ field.contents }}</p>
{% else %}
{{ field.field.errors.as_ul }}
{{ field.field }}
{% endif %}
</td>
{% endfor %}
{% endfor %}
{% endfor %}
{% if inline_admin_formset.formset.can_delete %}
<td class="delete">{% if inline_admin_form.original %}{{ inline_admin_form.deletion_field.field }}{% endif %}</td>
{% endif %}
</tr>
{% endfor %}
</tbody>
</table>
</fieldset>
</div>
</div>
<script type="text/javascript">
(function($) {
$(document).ready(function($) {
var rows = "#{{ inline_admin_formset.formset.prefix }}-group .tabular.inline-related tbody tr";
var alternatingRows = function(row) {
$(rows).not(".add-row").removeClass("row1 row2")
.filter(":even").addClass("row1").end()
.filter(rows + ":odd").addClass("row2");
}
var reinitDateTimeShortCuts = function() {
// Reinitialize the calendar and clock widgets by force
if (typeof DateTimeShortcuts != "undefined") {
$(".datetimeshortcuts").remove();
DateTimeShortcuts.init();
}
}
var updateSelectFilter = function() {
// If any SelectFilter widgets are a part of the new form,
// instantiate a new SelectFilter instance for it.
if (typeof SelectFilter != "undefined"){
$(".selectfilter").each(function(index, value){
var namearr = value.name.split('-');
SelectFilter.init(value.id, namearr[namearr.length-1], false, "{% static 'admin/' %}");
});
$(".selectfilterstacked").each(function(index, value){
var namearr = value.name.split('-');
SelectFilter.init(value.id, namearr[namearr.length-1], true, "{% static 'admin/' %}");
});
}
}
var initPrepopulatedFields = function(row) {
row.find('.prepopulated_field').each(function() {
var field = $(this);
var input = field.find('input, select, textarea');
var dependency_list = input.data('dependency_list') || [];
var dependencies = [];
$.each(dependency_list, function(i, field_name) {
dependencies.push('#' + row.find(field_name).find('input, select, textarea').attr('id'));
});
if (dependencies.length) {
input.prepopulate(dependencies, input.attr('maxlength'));
}
});
}
$(rows).formset({
prefix: "{{ inline_admin_formset.formset.prefix }}",
addText: "{% blocktrans with inline_admin_formset.opts.verbose_name|title as verbose_name %}Add another {{ verbose_name }}{% endblocktrans %}",
formCssClass: "dynamic-{{ inline_admin_formset.formset.prefix }}",
deleteCssClass: "inline-deletelink",
deleteText: "{% trans "Remove" %}",
emptyCssClass: "empty-form",
removed: alternatingRows,
added: (function(row) {
initPrepopulatedFields(row);
reinitDateTimeShortCuts();
updateSelectFilter();
alternatingRows(row);
})
});
});
})(django.jQuery);
</script>

View File

@ -2,11 +2,6 @@ from itertools import groupby
import django
from django import template
try:
from django import TemplateSyntaxError
except ImportError:
#support for django 1.3
from django.template.base import TemplateSyntaxError
register = template.Library()
@ -64,14 +59,15 @@ def dynamic_regroup(parser, token):
"""
firstbits = token.contents.split(None, 3)
if len(firstbits) != 4:
raise TemplateSyntaxError("'regroup' tag takes five arguments")
raise template.TemplateSyntaxError("'regroup' tag takes five arguments")
target = parser.compile_filter(firstbits[1])
if firstbits[2] != 'by':
raise TemplateSyntaxError("second argument to 'regroup' tag must be 'by'")
raise template.TemplateSyntaxError(
"second argument to 'regroup' tag must be 'by'")
lastbits_reversed = firstbits[3][::-1].split(None, 2)
if lastbits_reversed[1][::-1] != 'as':
raise TemplateSyntaxError("next-to-last argument to 'regroup' tag must"
" be 'as'")
raise template.TemplateSyntaxError(
"next-to-last argument to 'regroup' tag must be 'as'")
expression = lastbits_reversed[2][::-1]
var_name = lastbits_reversed[0][::-1]

View File

@ -26,8 +26,8 @@ class ComponentInline(SortableStackedInline):
# )
model = Component
def queryset(self, request):
qs = super(ComponentInline, self).queryset(
def get_queryset(self, request):
qs = super(ComponentInline, self).get_queryset(
request).exclude(title__icontains='2')
if get_is_sortable(qs):
self.model.is_sortable = True
@ -37,14 +37,14 @@ class ComponentInline(SortableStackedInline):
class WidgetAdmin(SortableAdmin):
def queryset(self, request):
def get_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)
qs = super(WidgetAdmin, self).get_queryset(request)
return qs.filter(id__lte=3)
inlines = [ComponentInline]

View File

@ -1,10 +1,4 @@
from django import VERSION
if VERSION < (1, 9):
from django.contrib.contenttypes.generic import GenericForeignKey
else:
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.utils.encoding import python_2_unicode_compatible
@ -51,7 +45,7 @@ class Project(SimpleModel, SortableMixin):
class Meta:
ordering = ['order']
category = SortableForeignKey(Category)
category = SortableForeignKey(Category, on_delete=models.CASCADE)
description = models.TextField()
order = models.PositiveIntegerField(default=0, editable=False)
@ -63,7 +57,7 @@ class Credit(SortableMixin):
class Meta:
ordering = ['order']
project = models.ForeignKey(Project)
project = models.ForeignKey(Project, on_delete=models.CASCADE)
first_name = models.CharField(max_length=30, help_text="Given name")
last_name = models.CharField(max_length=30, help_text="Family name")
@ -79,7 +73,7 @@ class Note(SortableMixin):
class Meta:
ordering = ['order']
project = models.ForeignKey(Project)
project = models.ForeignKey(Project, on_delete=models.CASCADE)
text = models.CharField(max_length=100)
order = models.PositiveIntegerField(default=0, editable=False)
@ -91,7 +85,7 @@ class Note(SortableMixin):
# Registered as a tabular inline on `Project` which can't be sorted
@python_2_unicode_compatible
class NonSortableCredit(models.Model):
project = models.ForeignKey(Project)
project = models.ForeignKey(Project, on_delete=models.CASCADE)
first_name = models.CharField(max_length=30, help_text="Given name")
last_name = models.CharField(max_length=30, help_text="Family name")
@ -102,7 +96,7 @@ class NonSortableCredit(models.Model):
# Registered as a stacked inline on `Project` which can't be sorted
@python_2_unicode_compatible
class NonSortableNote(models.Model):
project = models.ForeignKey(Project)
project = models.ForeignKey(Project, on_delete=models.CASCADE)
text = models.CharField(max_length=100)
def __str__(self):
@ -112,7 +106,7 @@ class NonSortableNote(models.Model):
# A generic bound model
@python_2_unicode_compatible
class GenericNote(SimpleModel, SortableMixin):
content_type = models.ForeignKey(ContentType,
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE,
verbose_name=u"Content type", related_name="generic_notes")
object_id = models.PositiveIntegerField(u"Content id")
content_object = GenericForeignKey(ct_field='content_type',
@ -133,7 +127,7 @@ class Component(SimpleModel, SortableMixin):
class Meta:
ordering = ['order']
widget = SortableForeignKey(Widget)
widget = SortableForeignKey(Widget, on_delete=models.CASCADE)
order = models.PositiveIntegerField(default=0, editable=False)
@ -183,7 +177,8 @@ class SortableCategoryWidget(SimpleModel, SortableMixin):
verbose_name = 'Sortable Category Widget'
verbose_name_plural = 'Sortable Category Widgets'
non_sortable_category = SortableForeignKey(NonSortableCategory)
non_sortable_category = SortableForeignKey(
NonSortableCategory, on_delete=models.CASCADE)
order = models.PositiveIntegerField(default=0, editable=False)
@ -197,7 +192,8 @@ class SortableNonInlineCategory(SimpleModel, SortableMixin):
that is *not* sortable, and is also not defined as an inline of the
SortableForeignKey field."""
non_sortable_category = SortableForeignKey(NonSortableCategory)
non_sortable_category = SortableForeignKey(
NonSortableCategory, on_delete=models.CASCADE)
order = models.PositiveIntegerField(default=0, editable=False)
@ -229,7 +225,7 @@ class CustomWidget(SortableMixin, SimpleModel):
@python_2_unicode_compatible
class CustomWidgetComponent(SortableMixin, SimpleModel):
custom_widget = models.ForeignKey(CustomWidget)
custom_widget = models.ForeignKey(CustomWidget, on_delete=models.CASCADE)
# custom field for ordering
widget_order = models.PositiveIntegerField(default=0, db_index=True,

View File

@ -1,16 +1,13 @@
try:
import httplib
import httplib # Python 2
except ImportError:
import http.client as httplib
from django import VERSION
if VERSION > (1, 8):
import uuid
import http.client as httplib # Python 3
import json
import uuid
import django
from django import VERSION
from django.contrib.auth.models import User
from django.db import models
from django.test import TestCase
@ -33,13 +30,12 @@ class TestSortableModel(SortableMixin):
return self.title
if VERSION > (1, 8):
class TestNonAutoFieldModel(SortableMixin):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
order = models.PositiveIntegerField(editable=False, db_index=True)
class TestNonAutoFieldModel(SortableMixin):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
order = models.PositiveIntegerField(editable=False, db_index=True)
class Meta:
ordering = ['order']
class Meta:
ordering = ['order']
class SortableTestCase(TestCase):
@ -79,8 +75,12 @@ class SortableTestCase(TestCase):
return category
def test_new_user_is_authenticated(self):
self.assertEqual(self.user.is_authenticated(), True,
'User is not authenticated')
if django.VERSION < (1, 10):
self.assertEqual(self.user.is_authenticated(), True,
'User is not authenticated')
else:
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')
@ -114,7 +114,7 @@ class SortableTestCase(TestCase):
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,
self.assertEqual(response.status_code, httplib.OK,
'Unable to reach sort view.')
def make_test_categories(self):
@ -257,7 +257,7 @@ class SortableTestCase(TestCase):
self.client.login(username=self.user.username,
password=self.user_raw_password)
response = self.client.get('/admin/app/project/sort/')
self.assertEquals(response.status_code, httplib.OK,
self.assertEqual(response.status_code, httplib.OK,
'Unable to reach sort view.')
def test_adminsortable_change_list_view_permission_denied(self):
@ -267,8 +267,8 @@ class SortableTestCase(TestCase):
self.client.login(username=self.staff.username,
password=self.staff_raw_password)
response = self.client.get('/admin/app/project/sort/')
self.assertEquals(response.status_code, httplib.FORBIDDEN,
'Sort view must be forbidden.')
self.assertEqual(response.status_code, httplib.FORBIDDEN,
'Sort view must be forbidden.')
def test_adminsortable_inline_changelist_success(self):
self.client.login(username=self.user.username,
@ -317,8 +317,5 @@ class SortableTestCase(TestCase):
self.assertEqual(notes, expected_notes)
def test_save_non_auto_field_model(self):
if VERSION > (1, 8):
model = TestNonAutoFieldModel()
model.save()
else:
pass
model = TestNonAutoFieldModel()
model.save()

View File

@ -1,6 +1,8 @@
# Django settings for test_project project.
import os
import django
def map_path(directory_name):
return os.path.join(os.path.dirname(__file__),
@ -8,7 +10,6 @@ def map_path(directory_name):
DEBUG = True
TEMPLATE_DEBUG = DEBUG
ADMINS = (
# ('Your Name', 'your_email@example.com'),
@ -91,35 +92,31 @@ STATICFILES_FINDERS = (
# Make this unique, and don't share it with anybody.
SECRET_KEY = '8**a!c8$1x)p@j2pj0yq!*v+dzp24g*$918ws#x@k+gf%0%rct'
# List of callables that know how to import templates from various sources.
TEMPLATE_LOADERS = (
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
)
MIDDLEWARE_CLASSES = (
MIDDLEWARE = [
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'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',
)
]
if django.VERSION < (1, 10):
MIDDLEWARE_CLASSES = MIDDLEWARE
ROOT_URLCONF = 'sample_project.urls'
# Python dotted path to the WSGI application used by Django's runserver.
WSGI_APPLICATION = 'sample_project.wsgi.application'
TEMPLATE_DIRS = (
map_path('templates'),
)
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': TEMPLATE_DIRS,
'DIRS': [
map_path('templates')
],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [

View File

@ -14,5 +14,5 @@ urlpatterns = [
url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
# Uncomment the next line to enable the admin:
url(r'^admin/', include(admin.site.urls)),
url(r'^admin/', admin.site.urls),
]

View File

@ -1,6 +0,0 @@
import os
def map_path(directory_name):
return os.path.join(os.path.dirname(__file__),
'../' + directory_name).replace('\\', '/')

36
tox.ini 100644
View File

@ -0,0 +1,36 @@
[tox]
envlist = django{1.8,1.9,1.10,1.11}-{py27,py34,py35},coverage
[testenv]
deps =
coverage
django1.8: Django>=1.8,<1.9
django1.9: Django>=1.9,<1.10
django1.10: Django>=1.10,<1.11
django1.11: Django>=1.11a1,<1.12
whitelist_externals = cd
setenv =
PYTHONPATH = {toxinidir}/sample_project
PYTHONWARNINGS = module
PYTHONDONTWRITEBYTECODE = 1
commands =
coverage run -p sample_project/manage.py test app
[testenv:coverage]
deps = coverage
skip_install = true
commands =
coverage combine
coverage report
coverage html
[coverage:run]
branch = True
parallel = True
source =
adminsortable
sample_project
[coverage:report]
exclude_lines =
if __name__ == .__main__.: