# -------- 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 'up | down' % (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/(?Pup|down)/(?P\d+)/(?P\d+)/$', 'your.views.admin_move_ordered_model', name="admin-move"),
# ...
)