diff --git a/.gitignore b/.gitignore
index ec828ad..e47f13e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,6 @@
*.un~
/build
/dist
+.DS_Store
+._*
+.svn
diff --git a/positions/__init__.py b/positions/__init__.py
index 4de1f3e..3df263f 100644
--- a/positions/__init__.py
+++ b/positions/__init__.py
@@ -1,2 +1,3 @@
from positions.fields import PositionField
from positions.managers import PositionManager
+from positions.admin import PositionAdmin
\ No newline at end of file
diff --git a/positions/admin.py b/positions/admin.py
new file mode 100644
index 0000000..30e12e7
--- /dev/null
+++ b/positions/admin.py
@@ -0,0 +1,45 @@
+from django.conf import settings
+from django.contrib import admin
+from django.shortcuts import get_object_or_404
+from django.utils.functional import update_wrapper
+from django.conf.urls.defaults import patterns, url
+from django.contrib.admin.util import unquote
+from django.http import HttpResponseRedirect
+
+
+class PositionAdmin(admin.ModelAdmin):
+ def get_urls(self):
+ def wrap(view):
+ def wrapper(*args, **kwargs):
+ return self.admin_site.admin_view(view)(*args, **kwargs)
+ return update_wrapper(wrapper, view)
+
+ info = self.model._meta.app_label, self.model._meta.module_name
+ return patterns('',
+ url(r'^(.+)/position-(up)/$', wrap(self.position_view), name='%s_%s_position_up' % info),
+ url(r'^(.+)/position-(down)/$', wrap(self.position_view), name='%s_%s_position_down' % info),
+ ) + super(PositionAdmin, self).get_urls()
+
+ def position_view(self, request, object_id, direction):
+ obj = get_object_or_404(self.model, pk=unquote(object_id))
+ if direction == 'up':
+ obj.position_up()
+ else:
+ obj.position_down()
+ return HttpResponseRedirect('../../')
+
+ def position_up_down_links(self, obj):
+ return '''
+
+
''' % {
+ 'app_label': self.model._meta.app_label,
+ 'module_name': self.model._meta.module_name,
+ 'object_id': obj.id,
+ 'STATIC_URL': settings.STATIC_URL,
+ }
+
+ position_up_down_links.allow_tags = True
+
+ position_up_down_links.short_description = 'Position'
+
+
diff --git a/positions/fields.py b/positions/fields.py
index 3d6fa52..777120b 100644
--- a/positions/fields.py
+++ b/positions/fields.py
@@ -1,5 +1,6 @@
import datetime
import warnings
+import types
from django.db import models
from django.db.models.signals import post_delete, post_save, pre_delete
@@ -39,8 +40,8 @@ def __init__(self, verbose_name=None, name=None, default=-1, collection=None, pa
self.collection = collection
self.parent_link = parent_link
self._collection_changed = None
-
- def contribute_to_class(self, cls, name):
+
+ def contribute_to_class(self, cls, name ):
super(PositionField, self).contribute_to_class(cls, name)
for constraint in cls._meta.unique_together:
if self.name in constraint:
@@ -50,6 +51,34 @@ def contribute_to_class(self, cls, name):
if getattr(field, 'auto_now', False):
self.auto_now_fields.append(field)
setattr(cls, self.name, self)
+
+ def _position_move(instance, up):
+ collection = self.get_collection(instance)
+ cache_name = self.get_cache_name()
+ position = getattr(instance, cache_name)[0]
+ if up:
+ collection = collection.filter(**{'%s__gt' % self.name: position})
+ else:
+ collection = collection.order_by('-%s' % self.name).filter(**{'%s__lt' % self.name: position})
+ try:
+ replacement = collection[0]
+ except IndexError:
+ return
+ current, updated = getattr(instance, cache_name), getattr(replacement, cache_name)
+ setattr(instance, cache_name, updated)
+ setattr(replacement, cache_name, current)
+ instance.save()
+ replacement.save()
+
+ def position_down(self):
+ return _position_move(self, up=False)
+
+ def position_up(self):
+ return _position_move(self, up=True)
+
+ setattr(cls, 'position_down', position_down )
+ setattr(cls, 'position_up', position_up )
+
pre_delete.connect(self.prepare_delete, sender=cls)
post_delete.connect(self.update_on_delete, sender=cls)
post_save.connect(self.update_on_save, sender=cls)
@@ -167,8 +196,19 @@ def get_next_sibling(self, instance):
Returns the next sibling of this instance.
"""
try:
- return self.get_collection(instance).filter(**{'%s__gt' % self.name: getattr(instance, self.get_cache_name())[0]})[0]
- except:
+ position = getattr(instance, self.get_cache_name())[0]
+ return self.get_collection(instance).filter(**{'%s__gt' % self.name: position}).order_by('%s' % self.name)[0]
+ except IndexError:
+ return None
+
+ def get_previous_sibling(self, instance):
+ """
+ Returns the previous sibling of this instance.
+ """
+ try:
+ position = getattr(instance, self.get_cache_name())[0]
+ return self.get_collection(instance).filter(**{'%s__lt' % self.name: position}).order_by('-%s' % self.name)[0]
+ except IndexError:
return None
def remove_from_collection(self, instance):
diff --git a/positions/static/positions/img/arrow-down.gif b/positions/static/positions/img/arrow-down.gif
new file mode 100644
index 0000000..a967b9f
Binary files /dev/null and b/positions/static/positions/img/arrow-down.gif differ
diff --git a/positions/static/positions/img/arrow-up.gif b/positions/static/positions/img/arrow-up.gif
new file mode 100644
index 0000000..3fe4851
Binary files /dev/null and b/positions/static/positions/img/arrow-up.gif differ