Added support for queryset() overrides on admin classes and inline admin classes.

Updated version to 1.4.5.
Updated README with explanation of requirements for overriding queryset() on inline models.
Added extra models to sample project to demonstrate sortable models with custom querysets.
Improved JavaScript of sortables to be more efficient with better comparison checking.
Fixed highlighting of stacked inlines on sort finish.
This commit is contained in:
Brandon Taylor
2013-04-27 22:58:02 -04:00
parent b6e68fa367
commit 014f6d1660
16 changed files with 477 additions and 68 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
VERSION = (1, 4, 4) # following PEP 386
VERSION = (1, 4, 5) # following PEP 386
DEV_N = None
+53 -33
View File
@@ -21,13 +21,30 @@ from django.shortcuts import render
from django.template.defaultfilters import capfirst
from django.views.decorators.csrf import csrf_exempt
from adminsortable.utils import get_is_sortable
from adminsortable.fields import SortableForeignKey
from adminsortable.models import Sortable
STATIC_URL = settings.STATIC_URL
class SortableAdmin(ModelAdmin):
class SortableAdminBase(object):
def changelist_view(self, request, extra_context=None):
"""
If the model that inherits Sortable has more than one 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.
"""
if get_is_sortable(self.queryset(request)):
self.change_list_template = \
self.sortable_change_list_with_sort_link_template
self.is_sortable = True
return super(SortableAdminBase, self).changelist_view(request,
extra_context=extra_context)
class SortableAdmin(SortableAdminBase, ModelAdmin):
"""
Admin class to add template overrides and context objects to enable
drag-and-drop ordering.
@@ -52,18 +69,20 @@ class SortableAdmin(ModelAdmin):
break
return sortable_foreign_key
def __init__(self, *args, **kwargs):
super(SortableAdmin, self).__init__(*args, **kwargs)
# def __init__(self, *args, **kwargs):
# super(SortableAdmin, self).__init__(*args, **kwargs)
self.has_sortable_tabular_inlines = False
self.has_sortable_stacked_inlines = False
for klass in self.inlines:
if issubclass(klass, SortableTabularInline):
if klass.model.is_sortable():
self.has_sortable_tabular_inlines = True
if issubclass(klass, SortableStackedInline):
if klass.model.is_sortable():
self.has_sortable_stacked_inlines = True
# self.has_sortable_tabular_inlines = False
# self.has_sortable_stacked_inlines = False
# for klass in self.inlines:
# print type(klass)
# is_sortable = get_is_sortable(
# klass.model._default_manager.get_query_set())
# print is_sortable
# if issubclass(klass, SortableTabularInline) and is_sortable:
# self.has_sortable_tabular_inlines = True
# if issubclass(klass, SortableStackedInline) and is_sortable:
# self.has_sortable_stacked_inlines = True
def get_urls(self):
urls = super(SortableAdmin, self).get_urls()
@@ -88,7 +107,8 @@ class SortableAdmin(ModelAdmin):
opts = self.model._meta
has_perm = request.user.has_perm('{0}.{1}'.format(opts.app_label,
opts.get_change_permission()))
objects = self.model.objects.all()
objects = self.queryset(request)
# Determine if we need to regroup objects relative to a
# foreign key specified on the model class that is extending Sortable.
@@ -97,6 +117,7 @@ class SortableAdmin(ModelAdmin):
# `sortable_by` defined as a SortableForeignKey
sortable_by_fk = self._get_sortable_foreign_key()
sortable_by_class_is_sortable = get_is_sortable(objects)
if sortable_by_property:
# backwards compatibility for < 1.1.1, where sortable_by was a
@@ -110,7 +131,6 @@ class SortableAdmin(ModelAdmin):
sortable_by_class_display_name = sortable_by_class._meta \
.verbose_name_plural
sortable_by_class_is_sortable = sortable_by_class.is_sortable()
elif sortable_by_fk:
# get sortable by properties from the SortableForeignKey
@@ -119,10 +139,6 @@ class SortableAdmin(ModelAdmin):
._meta.verbose_name_plural
sortable_by_class = sortable_by_fk.rel.to
sortable_by_expression = sortable_by_fk.name.lower()
try:
sortable_by_class_is_sortable = sortable_by_class.is_sortable()
except AttributeError:
sortable_by_class_is_sortable = False
else:
# model is not sortable by another model
@@ -132,7 +148,7 @@ class SortableAdmin(ModelAdmin):
if sortable_by_property or sortable_by_fk:
# Order the objects by the property they are sortable by,
#then by the order, otherwise the regroup
# then by the order, otherwise the regroup
# template tag will not show the objects correctly
objects = objects.order_by(sortable_by_expression, 'order')
@@ -157,19 +173,17 @@ class SortableAdmin(ModelAdmin):
}
return render(request, self.sortable_change_list_template, context)
def changelist_view(self, request, extra_context=None):
"""
If the model that inherits Sortable has more than one 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.
"""
if self.model.is_sortable():
self.change_list_template = \
self.sortable_change_list_with_sort_link_template
return super(SortableAdmin, self).changelist_view(request,
extra_context=extra_context)
def change_view(self, request, object_id, extra_context=None):
self.has_sortable_tabular_inlines = False
self.has_sortable_stacked_inlines = False
for klass in self.inlines:
is_sortable = klass.model.is_sortable
if issubclass(klass, SortableTabularInline) and is_sortable:
self.has_sortable_tabular_inlines = True
if issubclass(klass, SortableStackedInline) and is_sortable:
self.has_sortable_stacked_inlines = True
if self.has_sortable_tabular_inlines or \
self.has_sortable_stacked_inlines:
self.change_form_template = self.sortable_change_form_template
@@ -222,7 +236,7 @@ class SortableAdmin(ModelAdmin):
mimetype='application/json')
class SortableInlineBase(InlineModelAdmin):
class SortableInlineBase(SortableAdminBase, InlineModelAdmin):
def __init__(self, *args, **kwargs):
super(SortableInlineBase, self).__init__(*args, **kwargs)
@@ -230,7 +244,13 @@ class SortableInlineBase(InlineModelAdmin):
raise Warning(u'Models that are specified in SortableTabluarInline'
' and SortableStackedInline must inherit from Sortable')
self.is_sortable = self.model.is_sortable()
def queryset(self, request):
qs = super(SortableInlineBase, self).queryset(request)
if get_is_sortable(qs):
self.model.is_sortable = True
else:
self.model.is_sortable = False
return qs
class SortableTabularInline(SortableInlineBase, TabularInline):
+13 -12
View File
@@ -26,14 +26,11 @@ class Sortable(models.Model):
`save` the override of save increments the last/highest value of
order by 1
Override `sortable_by` method to make your model be sortable by a
foreign key field. Set `sortable_by` to the class specified in the
foreign key relationship.
"""
order = models.PositiveIntegerField(editable=False, default=1,
db_index=True)
is_sortable = False
# legacy support
sortable_by = None
@@ -42,14 +39,18 @@ class Sortable(models.Model):
abstract = True
ordering = ['order']
@classmethod
def is_sortable(cls):
try:
max_order = cls.objects.aggregate(
models.Max('order'))['order__max']
except (TypeError, IndexError):
max_order = 0
return True if max_order > 1 else False
# @classmethod
# def determine_if_sortable(cls):
# try:
# max_order = cls.objects.aggregate(
# models.Max('order'))['order__max']
# except (TypeError, IndexError):
# max_order = 0
# if max_order > 1:
# cls.is_sortable = True
# else:
# cls.is_sortable = False
@classmethod
def model_type_id(cls):
@@ -6,7 +6,7 @@ jQuery(function($){
items : 'li',
stop : function(event, ui)
{
var indexes = Array();
var indexes = [];
ui.item.parent().children('li').each(function(i)
{
indexes.push($(this).find(':hidden[name="pk"]').val());
@@ -17,5 +17,7 @@ jQuery(function($){
data: { indexes: indexes.join(',') }
});
}
}).click(function(e){
e.preventDefault();
});
});
@@ -10,21 +10,23 @@ jQuery(function($){
items : '.inline-related',
stop : function(event, ui)
{
var indexes = Array();
var indexes = [];
ui.item.parent().children('.inline-related').each(function(i)
{
index_value = $(this).find(':hidden[name$="-id"]').val();
if (index_value != "" && index_value != undefined)
var index_value = $(this).find(':hidden[name$="-id"]').val();
if (index_value !== "" && index_value !== undefined)
{
indexes.push(index_value);
}
});
$.ajax({
url: ui.item.parent().find(':hidden[name="admin_sorting_url"]').val(),
type: 'POST',
data: { indexes : indexes.join(',') },
success: function()
{
ui.item.effect('highlight', {}, 1000);
ui.item.find('.form-row').effect('highlight', {}, 1000);
}
});
}
@@ -10,14 +10,16 @@ jQuery(function($){
items : 'tr:not(.add-row)',
stop : function(event, ui)
{
var indexes = Array();
var indexes = [];
ui.item.parent().children('tr').each(function(i)
{
index_value = $(this).find('.original :hidden:first').val();
if (index_value != "" && index_value != undefined)
var index_value = $(this).find('.original :hidden:first').val();
if (index_value !== '' && index_value !== undefined)
{
indexes.push(index_value);
}
});
$.ajax({
url: ui.item.parent().find(':hidden[name="admin_sorting_url"]').val(),
type: 'POST',
+4
View File
@@ -0,0 +1,4 @@
def get_is_sortable(objects):
if len(objects) > 1:
return True
return False