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
117
118
119
120
121 | # -------- the usage (the good stuff first)
# models.py
class Category(OrderedModel):
name = models.CharField(max_length=50)
def __unicode__(self):
return self.name
# admin.py
class CategoryAdmin(admin.ModelAdmin):
list_display = ('name', 'order', 'order_link') # notice the order_link !!
admin.site.register(Category, CategoryAdmin)
# -------- the metaclass
from django.contrib.contenttypes.models import ContentType
from django.core.urlresolvers import reverse
from django.db import models
class OrderedModel(models.Model):
order = models.PositiveIntegerField(editable=False)
def save(self):
if not self.id:
try:
self.order = self.__class__.objects.all().order_by("-order")[0].order + 1
except IndexError:
self.order = 0
super(OrderedModel, self).save()
def order_link(self):
model_type_id = ContentType.objects.get_for_model(self.__class__).id
model_id = self.id
kwargs = {"direction": "up", "model_type_id": model_type_id, "model_id": model_id}
url_up = reverse("admin-move", kwargs=kwargs)
kwargs["direction"] = "down"
url_down = reverse("admin-move", kwargs=kwargs)
return '<a href="%s">up</a> | <a href="%s">down</a>' % (url_up, url_down)
order_link.allow_tags = True
order_link.short_description = 'Move'
order_link.admin_order_field = 'order'
@staticmethod
def move_down(model_type_id, model_id):
try:
ModelClass = ContentType.objects.get(id=model_type_id).model_class()
lower_model = ModelClass.objects.get(id=model_id)
higher_model = ModelClass.objects.filter(order__gt=lower_model.order)[0]
lower_model.order, higher_model.order = higher_model.order, lower_model.order
higher_model.save()
lower_model.save()
except IndexError:
pass
except ModelClass.DoesNotExist:
pass
@staticmethod
def move_up(model_type_id, model_id):
try:
ModelClass = ContentType.objects.get(id=model_type_id).model_class()
higher_model = ModelClass.objects.get(id=model_id)
lower_model = ModelClass.objects.filter(order__lt=higher_model.order)[0]
lower_model.order, higher_model.order = higher_model.order, lower_model.order
higher_model.save()
lower_model.save()
except IndexError:
pass
except ModelClass.DoesNotExist:
pass
class Meta:
ordering = ["order"]
abstract = True
# -------- the view
from django.contrib.admin.views.decorators import staff_member_required
from django.contrib.contenttypes.models import ContentType
from django.http import HttpResponseRedirect
from models import OrderedModel
from django.db import transaction
@staff_member_required
@transaction.commit_on_success
def admin_move_ordered_model(request, direction, model_type_id, model_id):
if direction == "up":
OrderedModel.move_up(model_type_id, model_id)
else:
OrderedModel.move_down(model_type_id, model_id)
ModelClass = ContentType.objects.get(id=model_type_id).model_class()
app_label = ModelClass._meta.app_label
model_name = ModelClass.__name__.lower()
url = "/admin/%s/%s/" % (app_label, model_name)
return HttpResponseRedirect(url)
# -------- the url config
urlpatterns = patterns('',
# ...
url(r'^admin/orderedmove/(?P<direction>up|down)/(?P<model_type_id>\d+)/(?P<model_id>\d+)/$', 'your.views.admin_move_ordered_model', name="admin-move"),
# ...
)
|
Comments
Some very nice ideas here. It would be nice to combine it with http://www.djangosnippets.org/snippets/568/ and have the reordering via Ajax.
Even nicer would be if I could figure out how to get this working for inline forms... Hmmmmmm.
#