Skip to content

Instantly share code, notes, and snippets.

@joshourisman
Created July 2, 2009 13:53
Show Gist options
  • Save joshourisman/139475 to your computer and use it in GitHub Desktop.
Save joshourisman/139475 to your computer and use it in GitHub Desktop.
Code to enable drag and drop reordering on a change_list in the Django admin. Currently causes a 'too many open files' error for some reason.
{% extends "reversion/change_list.html" %}
{% load my_admin i18n %}
{% block extrahead %}
<script type="text/javascript" src="/media/static/js/jquery-1.3.2.js" />
<script type="text/javascript" src="/media/static/js/jquery-ui-1.7.2.custom.min.js" />
<script type="text/javascript" src="/media/static/js/change_list_sort.js" />
{% endblock %}
{% block object-tools %}
<ul class="object-tools">
<li><a href="#apply" id="apply_order">Apply re-ordering</a></li>
<li><a href="" id="cancel_order">Cancel re-ordering</a></li>
<li><a href="recover/" class="recoverlink">{% blocktrans with cl.opts.verbose_name_plural|escape as name %}Recover deleted {{name}}{% endblocktrans %}</a></li>
{% if has_add_permission %}
<li><a href="add/{% if is_popup %}?_popup=1{% endif %}" class="addlink">{% blocktrans with cl.opts.verbose_name|escape as name %}Add {{name}}{% endblocktrans %}</a></li>
{% endif %}
</ul>
{% endblock %}
{% block result_list %}{% result_list cl %}{% endblock %}
{% if results %}
<table cellspacing="0">
<thead>
<tr>
{% for header in result_headers %}<th{{ header.class_attrib }}>
{% if header.sortable %}<a href="{{ header.url }}">{% endif %}
{{ header.text|capfirst }}
{% if header.sortable %}</a>{% endif %}</th>{% endfor %}
</tr>
</thead>
<tbody>
{% for result in results %}
<tr class="{% cycle 'row1' 'row2' %}">{% for item in result %}{{ item }}{% endfor %}</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
$(document).ready(function($) {
$('#apply_order').parents('li').hide();
$('#cancel_order').parents('li').hide();
$('tbody').sortable({
containment: 'parent',
opacity: 0.5,
items: 'tr',
update: function(event, ui) {
$(this).find('tr').each(function(i) {
$(this).children('td.order').text(i+1);
});
$('#apply_order').parents('li').show();
$('#cancel_order').parents('li').show();
}
});
$('tr').css('cursor', 'move');
$('#apply_order').click(function() {
var ordering = [];
$('td.order').each(function() {
ordering.push($(this).attr('id'))
});
$.post('reorder/', { 'ordering': ordering });
$('#apply_order').parents('li').hide();
$('#cancel_order').parents('li').hide();
});
});
from django.http import HttpResponse
from django.db.models import get_model
def reorder(request, app_name, model_name):
model = get_model(app_name, model_name)
ordering = request.POST.lists()[0][1]
ordering = [int(order) for order in ordering]
for id in ordering:
object = model.objects.get(id=id)
object.order = ordering.index(object.id)+1
print "%s: %s" % (object, object.order)
object.save()
return HttpResponse()
from django.contrib.admin.templatetags.admin_list import result_headers
from django.db import models
from django.utils import dateformat
from django.utils.html import escape, conditional_escape
from django.utils.text import capfirst
from django.utils.safestring import mark_safe
from django.utils.translation import get_date_formats
from django.utils.encoding import force_unicode
from django.template import Library
register = Library()
def items_for_result(cl, result):
first = True
pk = cl.lookup_opts.pk.attname
for field_name in cl.list_display:
if field_name == "order":
row_class = ' class="order" id="%d"' % getattr(result, 'id')
else:
row_class = ''
try:
f = cl.lookup_opts.get_field(field_name)
except models.FieldDoesNotExist:
# For non-field list_display values, the value is either a method,
# property or returned via a callable.
try:
if callable(field_name):
attr = field_name
value = attr(result)
elif hasattr(cl.model_admin, field_name) and \
not field_name == '__str__' and not field_name == '__unicode__':
attr = getattr(cl.model_admin, field_name)
value = attr(result)
else:
attr = getattr(result, field_name)
if callable(attr):
value = attr()
else:
value = attr
allow_tags = getattr(attr, 'allow_tags', False)
boolean = getattr(attr, 'boolean', False)
if boolean:
allow_tags = True
result_repr = _boolean_icon(value)
else:
result_repr = smart_unicode(value)
except (AttributeError, ObjectDoesNotExist):
result_repr = EMPTY_CHANGELIST_VALUE
else:
# Strip HTML tags in the resulting text, except if the
# function has an "allow_tags" attribute set to True.
if not allow_tags:
result_repr = escape(result_repr)
else:
result_repr = mark_safe(result_repr)
else:
field_val = getattr(result, f.attname)
if isinstance(f.rel, models.ManyToOneRel):
if field_val is not None:
result_repr = escape(getattr(result, f.name))
else:
result_repr = EMPTY_CHANGELIST_VALUE
# Dates and times are special: They're formatted in a certain way.
elif isinstance(f, models.DateField) or isinstance(f, models.TimeField):
if field_val:
(date_format, datetime_format, time_format) = get_date_formats()
if isinstance(f, models.DateTimeField):
result_repr = capfirst(dateformat.format(field_val, datetime_format))
elif isinstance(f, models.TimeField):
result_repr = capfirst(dateformat.time_format(field_val, time_format))
else:
result_repr = capfirst(dateformat.format(field_val, date_format))
else:
result_repr = EMPTY_CHANGELIST_VALUE
row_class = ' class="nowrap"'
# Booleans are special: We use images.
elif isinstance(f, models.BooleanField) or isinstance(f, models.NullBooleanField):
result_repr = _boolean_icon(field_val)
# DecimalFields are special: Zero-pad the decimals.
elif isinstance(f, models.DecimalField):
if field_val is not None:
result_repr = ('%%.%sf' % f.decimal_places) % field_val
else:
result_repr = EMPTY_CHANGELIST_VALUE
# Fields with choices are special: Use the representation
# of the choice.
elif f.choices:
result_repr = dict(f.choices).get(field_val, EMPTY_CHANGELIST_VALUE)
else:
result_repr = escape(field_val)
if force_unicode(result_repr) == '':
result_repr = mark_safe('&nbsp;')
# If list_display_links not defined, add the link tag to the first field
if (first and not cl.list_display_links) or field_name in cl.list_display_links:
table_tag = {True:'th', False:'td'}[first]
first = False
url = cl.url_for_result(result)
# Convert the pk to something that can be used in Javascript.
# Problem cases are long ints (23L) and non-ASCII strings.
if cl.to_field:
attr = str(cl.to_field)
else:
attr = pk
result_id = repr(force_unicode(getattr(result, attr)))[1:]
yield mark_safe(u'<%s%s><a href="%s"%s>%s</a></%s>' % \
(table_tag, row_class, url, (cl.is_popup and ' onclick="opener.dismissRelatedLookupPopup(window, %s); return false;"' % result_id or ''), conditional_escape(result_repr), table_tag))
else:
yield mark_safe(u'<td%s>%s</td>' % (row_class, conditional_escape(result_repr)))
def results(cl):
for res in cl.result_list:
yield list(items_for_result(cl,res))
def result_list(cl):
return {'cl': cl,
'result_headers': list(result_headers(cl)),
'results': list(results(cl))}
result_list = register.inclusion_tag("core/change_list_results.html")(result_list)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment