1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116 | # models.py
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.core.urlresolvers import reverse
class MenuItemManager(models.Manager):
def get_query_set(self):
return super(MenuItemManager, self).get_query_set().order_by("order", "id")
class MenuItem(models.Model):
name = models.CharField(max_length=100, blank=True, verbose_name="Name")
order = models.IntegerField(blank = True, null = True)
objects = MenuItemManager()
order_field = 'order' # You can specify your own field for sorting, but it's 'order' by default
class Meta:
db_table = u"menu"
def __unicode__(self):
return u"%s" % self.name
def order_link(self):
model_type_id = ContentType.objects.get_for_model(self.__class__).id
obj_id = self.id
kwargs = {"model_type_id": model_type_id}
url = reverse("admin_order", kwargs=kwargs)
return '<a href="%s" class="order_link">%s</a>' % (url, str(self.pk) or '')
order_link.allow_tags = True
order_link.short_description = 'Order' # If you change this you should change admin_sorting.js too
# urls.py
(r'^order/(?P<model_type_id>\d+)/$', 'path.to.view', {}, 'admin_order'),
# views.py
from django.http import HttpResponseRedirect, HttpResponse
from django.contrib.contenttypes.models import ContentType
def order(request, model_type_id=None):
if not request.is_ajax() or not request.method == "POST":
return HttpResponse("BAD")
try:
indexes = request.POST.get('indexes', []).split(",")
klass = ContentType.objects.get(id=model_type_id).model_class()
order_field = getattr(klass, 'order_field', 'order')
objects_dict = dict([(obj.pk, obj) for obj in klass.objects.filter(pk__in=indexes)])
min_index = min(objects_dict.values(), key=lambda x: getattr(x, order_field))
min_index = getattr(min_index, order_field) or 0
for index in indexes:
obj = objects_dict[int(index)]
setattr(obj, order_field, min_index)
obj.save()
min_index += 1
except IndexError:
pass
except klass.DoesNotExist:
pass
except AttributeError:
pass
return HttpResponse()
# admin_sorting.js
$(function() {
$("#content-main").sortable({
axis: 'y',
items: '.row1, .row2',
stop: function(event, ui) {
var indexes = Array();
var url = "";
$("#content-main").find(".row1, .row2").each(function(i){
var id = $(this).find('.order_link').html();
// the bad way i know.
url = $(this).find('.order_link').attr('href');
indexes.push(id);
});
$.ajax({
url: url,
type: 'POST',
data: {
indexes: indexes.join(","),
}
});
},
});
$('#content-main table tbody').find('.order_link').parent('td').hide();
/*
:contains(value) — value here must be the same as in model's
order_link.short_description
*/
$('#content-main table thead').find('th:contains("Order")').hide();
$("#content-main table tbody").disableSelection();
});
# admin.py
class MenuItemAdmin(admin.ModelAdmin):
list_display = ('id', 'name', 'order_link')
list_display_links = ('id', 'name')
ordering = ('order','id')
class Media:
js = ("js/jquery-1.4.2.min.js",
"js/jqueryui-custom-1.7.2.min.js",
"js/admin_sorting.js",)
admin.site.register(MenuItem, MenuItemAdmin)
|
Comments
Thanks!
bug on line 9: s/MenuManager/MenuItemManager/
It'd also be helpful to put a comma at the end of the urls.py line (when copying it's easy to neglect to put in ones own comma). Thanks again.
#
Thank you for reply. i've fixed typos :)
#
how do I get rid of "CSRF verification failed. Request aborted." error? I tried decorating the order view with @csrf_exempt - no cure :(
#
Never mind my comment :) Fixed that.
#