Use this abstract model if you want to add "order" to a given model. Once you do, you will get automatic "up" and "down" links for each model row.
One problem is that if the user sorts by another row, the up and down links are still there, but now don't make a lot of sense.
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"),
# ...
)
|
More like this
- Template tag - list punctuation for a list of items by shapiromatron 11 months ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months, 1 week ago
- Serializer factory with Django Rest Framework by julio 1 year, 6 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
- Help text hyperlinks by sa2812 1 year, 7 months ago
Comments
I'm sorry for the silly question, but i'm a beginner: Where i should insert the metaclass code? And the view code is only for the admin, right? Again, sorry for the stupid question, but i wish to have this functionality on a website that i'm developing. Thanks
#
Thanks for this! I went looking for how to order user data in ModelAdmin and was led here. I agree w/andybak, this would be nice to be ajax-y. It would be really nice w/some up/down triangles too.
It looks like the metaclass needs to stay w/the models, unless you splay it out to another file, in which case you would need to also import it. Yes, the view is only for the admin. good luck w/your django site, mdgart!
#
Thanks for the snippet! Found a bug in move_up() though...
With the code as is, whenever you try to move something up, it goes to the top of the list. This is because you're querying the database for the first element in the collection of elements whose order is less than the current element. Since we aren't ordering, the first element will always have order 0.
Change the lower_model assignment to this:
(note the order_by) and it should work properly.
#
that's a nice one! thanks!
#
Please login first before commenting.