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
parent
af6c8f2e15
commit
80bdaafdc3
38
README.md
38
README.md
|
|
@ -1,6 +1,6 @@
|
|||
# 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
|
||||
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.6.6 introduced a backward-incompatible change for the `sorting_filters` attribute. Please convert your attributes to the new tuple-based format.
|
||||
|
||||
|
||||
## Installation
|
||||
1. pip install django-admin-sortable
|
||||
|
|
@ -31,7 +33,7 @@ Download django-admin-sortable from [source](https://github.com/iambrandontaylor
|
|||
|
||||
### Static Media
|
||||
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:
|
||||
Copy the `adminsortable` folder from the `static` folder to the
|
||||
|
|
@ -184,23 +186,29 @@ 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
|
||||
`is_sortable` property of the model in this method.
|
||||
|
||||
#### Sorting a subset 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.
|
||||
#### Sorting subsets of objects
|
||||
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 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(default=False)
|
||||
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 = {'is_board_member': True}
|
||||
sorting_filters = (
|
||||
('Board Members', {'is_board_member': True}),
|
||||
('Non-Board Members', {'is_board_member': False}),
|
||||
)
|
||||
|
||||
def __unicode__(self):
|
||||
return '{} {}'.format(self.first_name, self.last_name)
|
||||
def __unicode__(self):
|
||||
return '{} {}'.format(self.first_name, self.last_name)
|
||||
|
||||
#### Extending custom templates
|
||||
By default, adminsortable's change form and change list views inherit from
|
||||
|
|
@ -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
|
||||
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_list_template_extends
|
||||
|
||||
These properties have default values of:
|
||||
These attributes have default values of:
|
||||
|
||||
change_form_template_extends = 'admin/change_form.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.
|
||||
|
||||
|
||||
### What's new in 1.6.5?
|
||||
- Namespace fixes for jQuery in Django admin
|
||||
### What's new in 1.6.6?
|
||||
- Multiple sorting_filters
|
||||
|
||||
|
||||
### Future
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
VERSION = (1, 6, 5) # following PEP 386
|
||||
VERSION = (1, 6, 6) # following PEP 386
|
||||
DEV_N = None
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -28,8 +28,6 @@ STATIC_URL = settings.STATIC_URL
|
|||
|
||||
|
||||
class SortableAdminBase(object):
|
||||
filtered_objects = []
|
||||
|
||||
def changelist_view(self, request, extra_context=None):
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
|
||||
# Apply any additional filters to create a subset of sortable objects
|
||||
self.filtered_objects = self.queryset(request).filter(
|
||||
**self.model.sorting_filters)
|
||||
# get sort group index from querystring
|
||||
sort_filter_index = request.GET.get('sort_filter')
|
||||
|
||||
if get_is_sortable(self.filtered_objects):
|
||||
if get_is_sortable(self.queryset(request)):
|
||||
self.change_list_template = \
|
||||
self.sortable_change_list_with_sort_link_template
|
||||
self.is_sortable = True
|
||||
|
|
@ -50,7 +47,8 @@ class SortableAdminBase(object):
|
|||
extra_context = {}
|
||||
|
||||
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,
|
||||
extra_context=extra_context)
|
||||
|
|
@ -106,7 +104,18 @@ class SortableAdmin(SortableAdminBase, ModelAdmin):
|
|||
has_perm = request.user.has_perm('{0}.{1}'.format(opts.app_label,
|
||||
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
|
||||
# foreign key specified on the model class that is extending Sortable.
|
||||
|
|
@ -191,9 +200,11 @@ class SortableAdmin(SortableAdminBase, ModelAdmin):
|
|||
})
|
||||
|
||||
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
|
||||
if issubclass(klass, SortableStackedInline) or issubclass(klass, SortableGenericStackedInline):
|
||||
if issubclass(klass, SortableStackedInline) or issubclass(klass,
|
||||
SortableGenericStackedInline):
|
||||
self.has_sortable_stacked_inlines = True
|
||||
|
||||
if self.has_sortable_tabular_inlines or \
|
||||
|
|
|
|||
|
|
@ -14,9 +14,6 @@ class MultipleSortableForeignKeyException(Exception):
|
|||
|
||||
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
|
||||
determining if the last value of `order` is greater than the default
|
||||
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,
|
||||
db_index=True)
|
||||
is_sortable = False
|
||||
sorting_filters = {}
|
||||
sorting_filters = ()
|
||||
|
||||
# legacy support
|
||||
sortable_by = None
|
||||
|
|
|
|||
|
|
@ -2,8 +2,14 @@
|
|||
{% load i18n %}
|
||||
|
||||
{% block object-tools-items %}
|
||||
<li>
|
||||
<a href="./sort/">{% trans 'Change Order' %}</a>
|
||||
</li>
|
||||
{{ block.super }}
|
||||
{% for sorting_filter in sorting_filters %}
|
||||
<li>
|
||||
<a href="./sort/?sort_filter={{ forloop.counter0 }}">{% trans 'Change Order of' %} {{ sorting_filter }}</a>
|
||||
</li>
|
||||
{% empty %}
|
||||
<li>
|
||||
<a href="./sort/">{% trans 'Change Order' %}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
{{ block.super }}
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -61,7 +61,6 @@ admin.site.register(Project, ProjectAdmin)
|
|||
|
||||
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ class GenericNote(SimpleModel, Sortable):
|
|||
pass
|
||||
|
||||
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
|
||||
|
|
@ -103,9 +103,16 @@ class Person(Sortable):
|
|||
|
||||
first_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):
|
||||
return '{} {}'.format(self.first_name, self.last_name)
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -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 %}
|
||||
Loading…
Reference in New Issue