Added sorting icons.
Refactored determination of sortability of classes referenced as sortable foreign keys in admin in a more reliable way.master
parent
de823d912c
commit
5dee27e077
|
|
@ -27,8 +27,9 @@ from django.http import HttpResponse
|
|||
from django.shortcuts import render
|
||||
from django.template.defaultfilters import capfirst
|
||||
|
||||
from adminsortable.utils import get_is_sortable, check_model_is_sortable
|
||||
from adminsortable.fields import SortableForeignKey
|
||||
from adminsortable.models import SortableMixin
|
||||
from adminsortable.utils import get_is_sortable, check_model_is_sortable
|
||||
|
||||
STATIC_URL = settings.STATIC_URL
|
||||
|
||||
|
|
@ -145,9 +146,17 @@ class SortableAdmin(SortableAdminBase, ModelAdmin):
|
|||
# Legacy support for 'sortable_by' defined as a model property
|
||||
sortable_by_property = getattr(self.model, 'sortable_by', None)
|
||||
|
||||
# `sortable_by` defined as a SortableForeignKey
|
||||
sortable_by_fk = self.model.sortable_foreign_key
|
||||
sortable_by_class_is_sortable = check_model_is_sortable(sortable_by_fk)
|
||||
# see if our model is sortable by a SortableForeignKey field
|
||||
# and that the number of objects available is >= 2
|
||||
sortable_by_fk = None
|
||||
sortable_by_field_name = None
|
||||
sortable_by_class_is_sortable = False
|
||||
|
||||
for field in self.model._meta.fields:
|
||||
if isinstance(field, SortableForeignKey):
|
||||
sortable_by_fk = field.rel.to
|
||||
sortable_by_field_name = field.name.lower()
|
||||
sortable_by_class_is_sortable = sortable_by_fk.objects.count() >= 2
|
||||
|
||||
if sortable_by_property:
|
||||
# backwards compatibility for < 1.1.1, where sortable_by was a
|
||||
|
|
@ -165,10 +174,9 @@ class SortableAdmin(SortableAdminBase, ModelAdmin):
|
|||
elif sortable_by_fk:
|
||||
# get sortable by properties from the SortableForeignKey
|
||||
# field - supported in 1.3+
|
||||
sortable_by_class_display_name = sortable_by_fk.rel.to \
|
||||
._meta.verbose_name_plural
|
||||
sortable_by_class = sortable_by_fk.rel.to
|
||||
sortable_by_expression = sortable_by_fk.name.lower()
|
||||
sortable_by_class_display_name = sortable_by_fk._meta.verbose_name_plural
|
||||
sortable_by_class = sortable_by_fk
|
||||
sortable_by_expression = sortable_by_field_name
|
||||
|
||||
else:
|
||||
# model is not sortable by another model
|
||||
|
|
|
|||
|
|
@ -42,7 +42,16 @@
|
|||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.sortable-help-text {
|
||||
color: #999;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.sortable a:hover
|
||||
{
|
||||
color: #003366;
|
||||
}
|
||||
|
||||
.sortable .fa {
|
||||
margin-right: 7px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,17 @@
|
|||
.sortable.has_original {
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.sortable .inline-related .module.aligned .fa,
|
||||
.sortable.inline-group .module .fa {
|
||||
display: block;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.sortable .inline-related .module.aligned .fa {
|
||||
margin: 9px 10px 0 0;
|
||||
}
|
||||
|
||||
.sortable.inline-group .module .fa {
|
||||
margin: 34px -10px 0 10px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,17 +8,36 @@
|
|||
items : 'li',
|
||||
stop : function(event, ui)
|
||||
{
|
||||
var indexes = [];
|
||||
ui.item.parent().children('li').each(function(i)
|
||||
var indexes = [],
|
||||
lineItems = ui.item.parent().find('> li');
|
||||
|
||||
lineItems.each(function(i)
|
||||
{
|
||||
indexes.push($(this).find(':hidden[name="pk"]').val());
|
||||
});
|
||||
|
||||
$.ajax({
|
||||
url: ui.item.find('a.admin_sorting_url').attr('href'),
|
||||
type: 'POST',
|
||||
data: { indexes: indexes.join(',') },
|
||||
success: function()
|
||||
{
|
||||
// set icons based on position
|
||||
lineItems.each(function(index, element) {
|
||||
var icon = $(element).find('> a .fa');
|
||||
icon.removeClass('fa-sort-desc fa-sort-asc fa-sort');
|
||||
|
||||
if (index === 0) {
|
||||
icon.addClass('fa fa-sort-desc');
|
||||
}
|
||||
else if (index == lineItems.length - 1) {
|
||||
icon.addClass('fa fa-sort-asc');
|
||||
}
|
||||
else {
|
||||
icon.addClass('fa fa-sort');
|
||||
}
|
||||
});
|
||||
|
||||
ui.item.effect('highlight', {}, 1000);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -41,7 +41,24 @@
|
|||
data: { indexes : indexes.join(',') },
|
||||
success: function() {
|
||||
var fieldsets = ui.item.find('fieldset'),
|
||||
highlightedSelector = fieldsets.filter('.collapsed').length === fieldsets.length ? 'h3' : '.form-row';
|
||||
highlightedSelector = fieldsets.filter('.collapsed').length === fieldsets.length ? 'h3' : '.form-row',
|
||||
icons = ui.item.parent().find(highlightedSelector).find('.fa');
|
||||
|
||||
// set icons based on position
|
||||
icons.removeClass('fa-sort-desc fa-sort-asc fa-sort');
|
||||
icons.each(function(index, element) {
|
||||
var icon = $(element);
|
||||
if (index === 0) {
|
||||
icon.addClass('fa fa-sort-desc');
|
||||
}
|
||||
else if (index == icons.length - 1) {
|
||||
icon.addClass('fa fa-sort-asc');
|
||||
}
|
||||
else {
|
||||
icon.addClass('fa fa-sort');
|
||||
}
|
||||
});
|
||||
|
||||
ui.item.find(highlightedSelector).effect('highlight', {}, 1000);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -38,6 +38,22 @@
|
|||
type: 'POST',
|
||||
data: { indexes : indexes.join(',') },
|
||||
success: function() {
|
||||
// set icons based on position
|
||||
var icons = ui.item.parent().find('.fa');
|
||||
icons.removeClass('fa-sort-desc fa-sort-asc fa-sort');
|
||||
icons.each(function(index, element) {
|
||||
var icon = $(element);
|
||||
if (index === 0) {
|
||||
icon.addClass('fa fa-sort-desc');
|
||||
}
|
||||
else if (index == icons.length - 1) {
|
||||
icon.addClass('fa fa-sort-asc');
|
||||
}
|
||||
else {
|
||||
icon.addClass('fa fa-sort');
|
||||
}
|
||||
});
|
||||
|
||||
// highlight sorted row, then re-stripe table
|
||||
ui.item.effect('highlight', {}, 1000);
|
||||
tabular_inline_rows.removeClass('row1 row2');
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
{{ block.super }}
|
||||
{% if has_sortable_tabular_inlines or has_sortable_stacked_inlines %}
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'adminsortable/css/admin.sortable.inline.css' %}" />
|
||||
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
{% block extrastyle %}
|
||||
{{ block.super }}
|
||||
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">
|
||||
<link rel="stylesheet" href="{% static 'adminsortable/css/admin.sortable.css' %}" />
|
||||
{% endblock %}
|
||||
|
||||
|
|
@ -40,8 +41,8 @@
|
|||
{% blocktrans with opts.verbose_name_plural|capfirst as model %}Drag and drop {{ model }} to change their order.{% endblocktrans %}
|
||||
{% endif %}
|
||||
</h1>
|
||||
{% if sortable_by_class.is_sortable %}
|
||||
<p>
|
||||
{% if sortable_by_class_is_sortable %}
|
||||
<p class="sortable-help-text">
|
||||
{% blocktrans %}You may also drag and drop {{ sortable_by_class_display_name }} to change their order.{% endblocktrans %}
|
||||
</p>
|
||||
{% endif %}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
</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" %}
|
||||
{% include "admin/includes/fieldset.html" with inline_admin_form_forloop=forloop.parentloop %}
|
||||
{% endfor %}
|
||||
{% if inline_admin_form.has_auto_field %}{{ inline_admin_form.pk_field.field }}{% endif %}
|
||||
{{ inline_admin_form.fk_field.field }}
|
||||
|
|
|
|||
|
|
@ -12,13 +12,13 @@
|
|||
</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" %}
|
||||
{% include "adminsortable/shared/fieldset.html" with inline_admin_form_forloop=forloop.parentloop initial_forms_count=inline_admin_formset.formset.management_form.initial.INITIAL_FORMS %}
|
||||
{% endfor %}
|
||||
{% if inline_admin_form.needs_explicit_pk_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 'admin:admin_do_sorting' inline_admin_form.original.model_type_id %}" />
|
||||
{% endif %}
|
||||
{% if inline_admin_form.needs_explicit_pk_field %}{{ inline_admin_form.pk_field.field }}{% endif %}
|
||||
{{ inline_admin_form.fk_field.field }}
|
||||
</div>{% endfor %}
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@
|
|||
{% for line in fieldset %}
|
||||
{% for field in line %}
|
||||
<td class="{{ field.field.name }}">
|
||||
{% if inline_admin_form.original and forloop.parentloop.counter == 1 %}<i class="fa fa-arrows-v"></i>{% endif %}
|
||||
{% if field.is_readonly %}
|
||||
<p>{{ field.contents }}</p>
|
||||
{% else %}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,11 @@
|
|||
<tr class="form-row {% 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">
|
||||
{% with initial_forms=inline_admin_form.formset.management_form.initial.INITIAL_FORMS %}
|
||||
{% if forloop.counter <= initial_forms %}
|
||||
<i class="fa fa-{% if forloop.first %}sort-desc{% elif forloop.counter == initial_forms %}sort-asc{% else %}sort{% endif %}"></i>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
{% if inline_admin_form.original or inline_admin_form.show_url %}<p>
|
||||
{% if inline_admin_form.original %}
|
||||
{{ inline_admin_form.original }}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
{# overrides admin/includes/fieldset.html: https://github.com/django/django/blob/master/django/contrib/admin/templates/admin/includes/fieldset.html #}
|
||||
<fieldset class="module aligned {{ fieldset.classes }}">
|
||||
{% if fieldset.name %}<h2>{{ fieldset.name }}</h2>{% endif %}
|
||||
{% if fieldset.description %}
|
||||
<div class="description">{{ fieldset.description|safe }}</div>
|
||||
{% endif %}
|
||||
{% for line in fieldset %}
|
||||
<div class="form-row{% if line.fields|length_is:'1' and line.errors %} errors{% endif %}{% if not line.has_visible_field %} hidden{% endif %}{% for field in line %}{% if field.field.name %} field-{{ field.field.name }}{% endif %}{% endfor %}">
|
||||
{% if line.fields|length_is:'1' %}{{ line.errors }}{% endif %}
|
||||
{% for field in line %}
|
||||
<div{% if not line.fields|length_is:'1' %} class="field-box{% if field.field.name %} field-{{ field.field.name }}{% endif %}{% if not field.is_readonly and field.errors %} errors{% endif %}{% if field.field.is_hidden %} hidden{% endif %}"{% elif field.is_checkbox %} class="checkbox-row"{% endif %}>
|
||||
{% if not line.fields|length_is:'1' and not field.is_readonly %}{{ field.errors }}{% endif %}
|
||||
{% if forloop.first %}
|
||||
{% if inline_admin_form_forloop.counter <= initial_forms_count %}
|
||||
<i class="fa fa-{% if inline_admin_form_forloop.first %}sort-desc{% elif inline_admin_form_forloop.counter == initial_forms_count %}sort-asc{% else %}sort{% endif %}"></i>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if field.is_checkbox %}
|
||||
{{ field.field }}{{ field.label_tag }}
|
||||
{% else %}
|
||||
{{ field.label_tag }}
|
||||
{% if field.is_readonly %}
|
||||
<p>{{ field.contents }}</p>
|
||||
{% else %}
|
||||
{{ field.field }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if field.field.help_text %}
|
||||
<p class="help">{{ field.field.help_text|safe }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</fieldset>
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
{% for object in list_objects %}
|
||||
<li>
|
||||
{% if list_objects_length > 1 %}
|
||||
{% render_object_rep object %}
|
||||
{% render_object_rep object forloop %}
|
||||
{% else %}
|
||||
{{ object }}
|
||||
{% endif %}
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
{% for regrouped_object in regrouped_objects %}
|
||||
{% with object=regrouped_object.grouper %}
|
||||
{% if object %}
|
||||
<li>{% if sortable_by_class_is_sortable %}
|
||||
{% render_object_rep object %}
|
||||
<li class="parent">{% if sortable_by_class_is_sortable %}
|
||||
{% render_object_rep object forloop %}
|
||||
{% else %}
|
||||
{{ object }}
|
||||
{% endif %}
|
||||
|
|
|
|||
|
|
@ -3,4 +3,4 @@
|
|||
<form>
|
||||
<input name="pk" type="hidden" value="{{ object.pk }}" />
|
||||
</form>
|
||||
<a href="{% url 'admin:admin_do_sorting' object.model_type_id %}" class="admin_sorting_url">{{ object }}</a>
|
||||
<a href="{% url 'admin:admin_do_sorting' object.model_type_id %}" class="admin_sorting_url"><i class="fa fa-{% if forloop.first %}sort-desc{% elif forloop.last %}sort-asc{% else %}sort{% endif %}"></i> {{ object }}</a>
|
||||
|
|
|
|||
|
|
@ -28,8 +28,8 @@ def render_list_items(context, list_objects,
|
|||
|
||||
|
||||
@register.simple_tag(takes_context=True)
|
||||
def render_object_rep(context, obj,
|
||||
def render_object_rep(context, obj, forloop,
|
||||
sortable_object_rep_template='adminsortable/shared/object_rep.html'):
|
||||
context.update({'object': obj})
|
||||
context.update({'object': obj, 'forloop': forloop})
|
||||
tmpl = template.loader.get_template(sortable_object_rep_template)
|
||||
return tmpl.render(context)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
from .models import SortableMixin, SortableForeignKey
|
||||
|
||||
|
||||
def check_inheritance(obj):
|
||||
return issubclass(type(obj), SortableMixin)
|
||||
def check_inheritance(cls):
|
||||
print 'check_inheritance: {}'.format(issubclass(type(cls), SortableMixin))
|
||||
return issubclass(type(cls), SortableMixin)
|
||||
|
||||
|
||||
def get_is_sortable(objects):
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ class CreditInline(SortableTabularInline):
|
|||
|
||||
class NoteInline(SortableStackedInline):
|
||||
model = Note
|
||||
extra = 0
|
||||
extra = 2
|
||||
|
||||
|
||||
class GenericNoteInline(SortableGenericStackedInline):
|
||||
|
|
|
|||
Binary file not shown.
Loading…
Reference in New Issue