Refactored sorting_filters into a tuple and moved logic for retrieving sorting filters into the sort_view versus changelist_view.

Updated readme.
Updated sample project to leverage new sorting_filters on Person model and admin.
Removed custom change list template for Person admin.
master
Brandon Taylor 2014-03-05 09:10:47 -05:00
parent af6c8f2e15
commit 80bdaafdc3
9 changed files with 70 additions and 51 deletions

View File

@ -1,6 +1,6 @@
# Django Admin Sortable # Django Admin Sortable
Current version: 1.6.5 Current version: 1.6.6
This project makes it easy to add drag-and-drop ordering to any model in This project makes it easy to add drag-and-drop ordering to any model in
Django admin. Inlines for a sortable model may also be made sortable, Django admin. Inlines for a sortable model may also be made sortable,
@ -12,6 +12,8 @@ For Django 1.5.x or higher, use the latest version of django-admin-sortable.
django-admin-sortable 1.5.2 introduced backward-incompatible changes for Django 1.4.x django-admin-sortable 1.5.2 introduced backward-incompatible changes for Django 1.4.x
django-admin-sortable 1.6.6 introduced a backward-incompatible change for the `sorting_filters` attribute. Please convert your attributes to the new tuple-based format.
## Installation ## Installation
1. pip install django-admin-sortable 1. pip install django-admin-sortable
@ -31,7 +33,7 @@ Download django-admin-sortable from [source](https://github.com/iambrandontaylor
### Static Media ### Static Media
Preferred: Preferred:
Use the [staticfiles app](https://docs.djangoproject.com/en/1.4/ref/contrib/staticfiles/) Use the [staticfiles app](https://docs.djangoproject.com/en/1.6/ref/contrib/staticfiles/)
Alternate: Alternate:
Copy the `adminsortable` folder from the `static` folder to the Copy the `adminsortable` folder from the `static` folder to the
@ -184,10 +186,13 @@ may change, and adminsortable won't be able to automatically determine
if the inline model is sortable from here, which is why we have to set the if the inline model is sortable from here, which is why we have to set the
`is_sortable` property of the model in this method. `is_sortable` property of the model in this method.
#### Sorting a subset of objects #### Sorting subsets of objects
It is also possible to sort a subset of objects in your model by adding a `sorting_filters` dictionary. This dictionary works exactly the same as `.filter()` on a QuerySet, and is applied *after* `get_queryset()` on the admin class, allowing you to override the queryset as you would normally in admin but apply additional filters for sorting. It is also possible to sort a subset of objects in your model by adding a `sorting_filters` tuple. This works exactly the same as `.filter()` on a QuerySet, and is applied *after* `get_queryset()` on the admin class, allowing you to override the queryset as you would normally in admin but apply additional filters for sorting. The text "Change Order of" will appear before each filter in the Change List template, and the filter groups are displayed from left to right in the order listed. If no `sorting_filters` are specified, the text "Change Order" will be displayed for the link.
This is useful when you need to have some objects orderable via drag-and-drop, and others not. An example would be a "Board of Directors". In this use case, you have a list of "People". Some of these people are on the Board of Directors, and you need to sort them at will. Other people need to be sorted alphabetically. #####Important!
django-admin-sortable 1.6.6 introduces a backwards-incompatible change for `sorting_filters`. Previously this attribute was defined as a dictionary, so you'll need to change your values over to the new tuple-based format.
An example of sorting subsets would be a "Board of Directors". In this use case, you have a list of "People" objects. Some of these people are on the Board of Directors and some not, and you need to sort them independently.
class Person(Sortable): class Person(Sortable):
class Meta(Sortable.Meta): class Meta(Sortable.Meta):
@ -195,9 +200,12 @@ This is useful when you need to have some objects orderable via drag-and-drop, a
first_name = models.CharField(max_length=50) first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50)
is_board_member = models.BooleanField(default=False) is_board_member = models.BooleanField('Board Member', default=False)
sorting_filters = {'is_board_member': True} sorting_filters = (
('Board Members', {'is_board_member': True}),
('Non-Board Members', {'is_board_member': False}),
)
def __unicode__(self): def __unicode__(self):
return '{} {}'.format(self.first_name, self.last_name) return '{} {}'.format(self.first_name, self.last_name)
@ -208,12 +216,12 @@ Django admin's standard templates. Sometimes you need to have a custom change
form or change list, but also need adminsortable's CSS and JavaScript for form or change list, but also need adminsortable's CSS and JavaScript for
inline models that are sortable for example. inline models that are sortable for example.
SortableAdmin has two properties you can override for this use case: SortableAdmin has two attributes you can override for this use case:
change_form_template_extends change_form_template_extends
change_list_template_extends change_list_template_extends
These properties have default values of: These attributes have default values of:
change_form_template_extends = 'admin/change_form.html' change_form_template_extends = 'admin/change_form.html'
change_list_template_extends = 'admin/change_list.html' change_list_template_extends = 'admin/change_list.html'
@ -308,8 +316,8 @@ ordering on top of that just seemed a little much in my opinion.
django-admin-sortable is currently used in production. django-admin-sortable is currently used in production.
### What's new in 1.6.5? ### What's new in 1.6.6?
- Namespace fixes for jQuery in Django admin - Multiple sorting_filters
### Future ### Future

View File

@ -1,4 +1,4 @@
VERSION = (1, 6, 5) # following PEP 386 VERSION = (1, 6, 6) # following PEP 386
DEV_N = None DEV_N = None

View File

@ -28,8 +28,6 @@ STATIC_URL = settings.STATIC_URL
class SortableAdminBase(object): class SortableAdminBase(object):
filtered_objects = []
def changelist_view(self, request, extra_context=None): def changelist_view(self, request, extra_context=None):
""" """
If the model that inherits Sortable has more than one object, If the model that inherits Sortable has more than one object,
@ -37,11 +35,10 @@ class SortableAdminBase(object):
object_tools block to take people to the view to change the sorting. object_tools block to take people to the view to change the sorting.
""" """
# Apply any additional filters to create a subset of sortable objects # get sort group index from querystring
self.filtered_objects = self.queryset(request).filter( sort_filter_index = request.GET.get('sort_filter')
**self.model.sorting_filters)
if get_is_sortable(self.filtered_objects): if get_is_sortable(self.queryset(request)):
self.change_list_template = \ self.change_list_template = \
self.sortable_change_list_with_sort_link_template self.sortable_change_list_with_sort_link_template
self.is_sortable = True self.is_sortable = True
@ -50,7 +47,8 @@ class SortableAdminBase(object):
extra_context = {} extra_context = {}
extra_context.update({ extra_context.update({
'change_list_template_extends': self.change_list_template_extends 'change_list_template_extends': self.change_list_template_extends,
'sorting_filters': [sort_filter[0] for sort_filter in self.model.sorting_filters]
}) })
return super(SortableAdminBase, self).changelist_view(request, return super(SortableAdminBase, self).changelist_view(request,
extra_context=extra_context) extra_context=extra_context)
@ -106,7 +104,18 @@ class SortableAdmin(SortableAdminBase, ModelAdmin):
has_perm = request.user.has_perm('{0}.{1}'.format(opts.app_label, has_perm = request.user.has_perm('{0}.{1}'.format(opts.app_label,
opts.get_change_permission())) opts.get_change_permission()))
objects = self.filtered_objects # get sort group index from querystring if present
sort_filter_index = request.GET.get('sort_filter')
filters = {}
if sort_filter_index:
try:
filters = self.model.sorting_filters[int(sort_filter_index)][1]
except IndexError, ValueError:
pass
# Apply any sort filters to create a subset of sortable objects
objects = self.queryset(request).filter(**filters)
# Determine if we need to regroup objects relative to a # Determine if we need to regroup objects relative to a
# foreign key specified on the model class that is extending Sortable. # foreign key specified on the model class that is extending Sortable.
@ -191,9 +200,11 @@ class SortableAdmin(SortableAdminBase, ModelAdmin):
}) })
for klass in self.inlines: for klass in self.inlines:
if issubclass(klass, SortableTabularInline) or issubclass(klass, SortableGenericTabularInline): if issubclass(klass, SortableTabularInline) or issubclass(klass,
SortableGenericTabularInline):
self.has_sortable_tabular_inlines = True self.has_sortable_tabular_inlines = True
if issubclass(klass, SortableStackedInline) or issubclass(klass, SortableGenericStackedInline): if issubclass(klass, SortableStackedInline) or issubclass(klass,
SortableGenericStackedInline):
self.has_sortable_stacked_inlines = True self.has_sortable_stacked_inlines = True
if self.has_sortable_tabular_inlines or \ if self.has_sortable_tabular_inlines or \

View File

@ -14,9 +14,6 @@ class MultipleSortableForeignKeyException(Exception):
class Sortable(models.Model): class Sortable(models.Model):
""" """
Unfortunately, Django doesn't support using more than one AutoField
in a model or this class could be simplified.
`is_sortable` determines whether or not the Model is sortable by `is_sortable` determines whether or not the Model is sortable by
determining if the last value of `order` is greater than the default determining if the last value of `order` is greater than the default
of 1, which should be present if there is only one object. of 1, which should be present if there is only one object.
@ -31,7 +28,7 @@ class Sortable(models.Model):
order = models.PositiveIntegerField(editable=False, default=1, order = models.PositiveIntegerField(editable=False, default=1,
db_index=True) db_index=True)
is_sortable = False is_sortable = False
sorting_filters = {} sorting_filters = ()
# legacy support # legacy support
sortable_by = None sortable_by = None

View File

@ -2,8 +2,14 @@
{% load i18n %} {% load i18n %}
{% block object-tools-items %} {% block object-tools-items %}
{% for sorting_filter in sorting_filters %}
<li>
<a href="./sort/?sort_filter={{ forloop.counter0 }}">{% trans 'Change Order of' %} {{ sorting_filter }}</a>
</li>
{% empty %}
<li> <li>
<a href="./sort/">{% trans 'Change Order' %}</a> <a href="./sort/">{% trans 'Change Order' %}</a>
</li> </li>
{% endfor %}
{{ block.super }} {{ block.super }}
{% endblock %} {% endblock %}

View File

@ -61,7 +61,6 @@ admin.site.register(Project, ProjectAdmin)
class PersonAdmin(SortableAdmin): class PersonAdmin(SortableAdmin):
sortable_change_list_with_sort_link_template = 'app/person/admin/change_list_with_sort_link.html' list_display = ['__unicode__', 'is_board_member']
admin.site.register(Person, PersonAdmin) admin.site.register(Person, PersonAdmin)

View File

@ -83,7 +83,7 @@ class GenericNote(SimpleModel, Sortable):
pass pass
def __unicode__(self): def __unicode__(self):
return u'{0} : {1}'.format(self.title, self.content_object) return u'{}: {}'.format(self.title, self.content_object)
# An model registered as an inline that has a custom queryset # An model registered as an inline that has a custom queryset
@ -103,9 +103,16 @@ class Person(Sortable):
first_name = models.CharField(max_length=50) first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50)
is_board_member = models.BooleanField(default=False) is_board_member = models.BooleanField('Board Member', default=False)
sorting_filters = {'is_board_member': True} # 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 __unicode__(self): def __unicode__(self):
return '{} {}'.format(self.first_name, self.last_name) return '{} {}'.format(self.first_name, self.last_name)

View File

@ -1,9 +0,0 @@
{% extends change_list_template_extends %}
{% load i18n %}
{% block object-tools-items %}
<li>
<a href="./sort/">{% trans 'Change Order of Board Members' %}</a>
</li>
{{ block.super }}
{% endblock %}