[Python-modules-commits] [python-django-ordered-model] 01/06: importing python-django-ordered-model_1.2.0.orig.tar.gz

Michael Fladischer fladi at moszumanska.debian.org
Sat Jul 9 17:29:43 UTC 2016


This is an automated email from the git hooks/post-receive script.

fladi pushed a commit to branch master
in repository python-django-ordered-model.

commit 12859220117d7cc25f1f684d50e072c21e2f1ac3
Author: Michael Fladischer <FladischerMichael at fladi.at>
Date:   Sat Jul 9 16:32:55 2016 +0200

    importing python-django-ordered-model_1.2.0.orig.tar.gz
---
 CHANGES.md                                         |  60 +++
 LICENSE                                            |  24 ++
 MANIFEST.in                                        |   3 +
 PKG-INFO                                           |  17 +
 README.md                                          | 183 +++++++++
 ordered_model/__init__.py                          |   0
 ordered_model/admin.py                             | 262 +++++++++++++
 ordered_model/locale/de/LC_MESSAGES/django.mo      | Bin 0 -> 457 bytes
 ordered_model/locale/de/LC_MESSAGES/django.po      |  23 ++
 ordered_model/locale/pl/LC_MESSAGES/django.mo      | Bin 0 -> 519 bytes
 ordered_model/locale/pl/LC_MESSAGES/django.po      |  24 ++
 ordered_model/models.py                            | 226 +++++++++++
 ordered_model/static/ordered_model/arrow-down.gif  | Bin 0 -> 80 bytes
 ordered_model/static/ordered_model/arrow-up.gif    | Bin 0 -> 838 bytes
 .../ordered_model/admin/order_controls.html        |   5 +
 ordered_model/tests/__init__.py                    |   0
 ordered_model/tests/fixtures/test_items.json       |  66 ++++
 ordered_model/tests/models.py                      |  59 +++
 ordered_model/tests/settings.py                    |  20 +
 ordered_model/tests/tests.py                       | 421 +++++++++++++++++++++
 ordered_model/tests/urls.py                        |   8 +
 setup.py                                           |  32 ++
 22 files changed, 1433 insertions(+)

diff --git a/CHANGES.md b/CHANGES.md
new file mode 100644
index 0000000..a1714b7
--- /dev/null
+++ b/CHANGES.md
@@ -0,0 +1,60 @@
+Change log
+==========
+
+1.2.0 – 2016-07-08
+------------------
+
+ - Remove support for Django <1.8 and Python 2.6
+ - Support for multiple order_with_respect_to fields
+ - Remove usage of deprecated django.conf.urls.patterns
+
+1.1.0 – 2016-01-15
+------------------
+
+ - Add support for many-to-many models.
+ - Add Italian translations.
+
+1.0.0 – 2015-11-24
+------------------
+
+1.0, because why not. Seems to be working alright for everyone. Some little things in this release:
+
+ - Add support for custom order field by inheriting from `OrderedModelBase` and setting `order_field_name`.
+ - Add support for Python 3.
+ - Drop support for Django 1.4.
+
+0.4.2 – 2015-06-02
+------------------
+
+ - Fix admin buttons not working with custom primary keys.
+ - Fix admin using deprecated `get_query_set` method.
+
+0.4.1 – 2015-04-06
+------------------
+
+ - Add support for Django 1.7 and 1.8.
+ - Fix deprecation warning about module\_name.
+ - Add French translations.
+
+0.4.0 – 2014-07-31
+------------------
+
+ - Models can now be moved to any position, not just up and down. `move_up()` and `move_down()` are replaced by `up()` and `down()`. See the readme for the full set of new methods.
+ - Add `order_with_respect_to` option so models can be ordered based on another field.
+ - The admin ordering controls are now rendered using templates.
+ - Ordering now always starts from 0 and has no gaps. Previously, gaps in the ordering numbers could appear when models were deleted, etc.
+ - Fix bug where objects always get the order of "0".
+ - Models with custom primary keys can now be used as ordered models.
+
+
+0.3.0 – 2013-10-25
+------------------
+
+ - Support for Django 1.4, 1.5 and 1.6.
+ - Fix list_filter being deselected when moving in admin
+ - Improve performance of ordering by adding index and using Max aggregate
+
+0.2.0 – 2012-11-14
+------------------
+
+ - First release
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..1929289
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,24 @@
+Copyright (c) 2009, Ben Firshman
+All rights reserved.
+ 
+Redistribution and use in source and binary forms, with or without 
+modification, are permitted provided that the following conditions are met:
+ 
+ * Redistributions of source code must retain the above copyright notice, this 
+   list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, 
+   this list of conditions and the following disclaimer in the documentation 
+   and/or other materials provided with the distribution.
+ * The names of its contributors may not be used to endorse or promote products 
+   derived from this software without specific prior written permission.
+ 
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..5db94f5
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,3 @@
+include MANIFEST.in *.md LICENSE *.sh
+recursive-include ordered_model *.json
+
diff --git a/PKG-INFO b/PKG-INFO
new file mode 100644
index 0000000..7c3384e
--- /dev/null
+++ b/PKG-INFO
@@ -0,0 +1,17 @@
+Metadata-Version: 1.1
+Name: django-ordered-model
+Version: 1.2.0
+Summary: Allows Django models to be ordered and provides a simple admin interface for reordering them.
+Home-page: http://github.com/bfirsh/django-ordered-model
+Author: Ben Firshman
+Author-email: ben at firshman.co.uk
+License: UNKNOWN
+Description: UNKNOWN
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Framework :: Django
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 3
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..5c8af74
--- /dev/null
+++ b/README.md
@@ -0,0 +1,183 @@
+django-ordered-model
+====================
+
+[![Build Status](https://secure.travis-ci.org/bfirsh/django-ordered-model.png?branch=master)](https://travis-ci.org/bfirsh/django-ordered-model)
+
+django-ordered-model allows models to be ordered and provides a simple admin
+interface for reordering them.
+
+Based on https://djangosnippets.org/snippets/998/ and
+https://djangosnippets.org/snippets/259/
+
+Requires:
+
+  * Django >=1.8
+  * Python 2.7 or >=3.3
+
+Installation
+------------
+
+    $ python setup.py install
+
+You can use Pip:
+
+    $ pip install django-ordered-model
+
+Usage
+-----
+
+Add `ordered_model` to your `SETTINGS.INSTALLED_APPS`.
+
+Inherit your model from `OrderedModel` to make it ordered:
+
+    from django.db import models
+    from ordered_model.models import OrderedModel
+
+    class Item(OrderedModel):
+        name = models.CharField(max_length=100)
+
+        class Meta(OrderedModel.Meta):
+            pass
+
+Model instances now have a set of methods to move them relative to each other.
+To demonstrate those methods we create two instances of `Item`:
+
+    foo = Item.objects.create(name="Foo")
+    bar = Item.objects.create(name="Bar")
+
+### Swap positions
+
+    foo.swap(bar)
+
+This swaps the position of two objects.
+
+### Move position up on position
+
+    foo.up()
+    foo.down()
+
+Moving an object up or down just makes it swap its position with the neighouring
+object directly above of below depending on the direction.
+
+### Move to arbitrary position
+
+    foo.to(12)
+    bar.to(13)
+
+Move the object to an arbitrary position in the stack. This essentially sets the
+order value to the specified integer. Objects between the original and the new
+position get their order value increased or decreased according to the direction
+of the move.
+
+### Move object above or below reference
+
+    foo.above(bar)
+    foo.below(bar)
+
+Move the object directly above or below the reference object, increasing or
+decreasing the order value for all objects between the two, depending on the
+direction of the move.
+
+### Move to top of stack
+
+    foo.top()
+
+This sets the order value to the lowest value found in the stack and increases
+the order value of all objects that were above the moved object by one.
+
+### Move to bottom of stack
+
+    foo.bottom()
+
+This sets the order value to the highest value found in the stack and decreases
+the order value of all objects that were below the moved object by one.
+
+## Subset Ordering
+
+In some cases, ordering objects is required only on a subset of objects. For example,
+an application that manages contact lists for users, in a many-to-one/many relationship,
+would like to allow each user to order their contacts regardless of how other users
+choose their order. This option is supported via the `order_with_respect_to` parameter.
+
+A simple example might look like so:
+
+    class Contact(OrderedModel):
+        user = models.ForeignKey(User)
+        phone = models.CharField()
+        order_with_respect_to = 'user'
+
+If objects are ordered with respect to more than one field, `order_with_respect_to` supports
+tuples to define multiple fields:
+
+    class Model(OrderedModel)
+        # ...
+        order_with_respect_to = ('field_a', 'field_b')
+
+In a many-to-many relationship you need to use a seperate through model which is derived from the OrderedModel.
+For example, an application which manages pizzas with toppings.
+
+A simple example might look like so:
+
+    class Topping(models.Model):
+        name = models.CharField(max_length=100)
+
+    class Pizza(models.Model):
+        name = models.CharField(max_length=100)
+        toppings = models.ManyToManyField(Topping, through='PizzaToppingsThroughModel')
+
+    class PizzaToppingsThroughModel(OrderedModel):
+        pizza = models.ForeignKey(Pizza)
+        topping = models.ForeignKey(Topping)
+        order_with_respect_to = 'pizza'
+
+        class Meta:
+            ordering = ('pizza', 'order')
+
+Admin integration
+-----------------
+
+To add arrows in the admin change list page to do reordering, you can use the
+`OrderedModelAdmin` and the `move_up_down_links` field:
+
+    from django.contrib import admin
+    from ordered_model.admin import OrderedModelAdmin
+    from models import Item
+
+    class ItemAdmin(OrderedModelAdmin):
+        list_display = ('name', 'move_up_down_links')
+
+    admin.site.register(Item, ItemAdmin)
+
+
+For a many-to-many relationship you need the following in the admin.py file:
+
+    from django.contrib import admin
+    from ordered_model.admin import OrderedTabularInline
+    from models import Pizza, PizzaToppingsThroughModel
+
+    class PizzaToppingsThroughModelInline(OrderedTabularInline):
+        model = PizzaToppingsThroughModel
+        fields = ('topping', 'order', 'move_up_down_links',)
+        readonly_fields = ('order', 'move_up_down_links',)
+        extra = 1
+        ordering = ('order',)
+
+    class PizzaAdmin(admin.ModelAdmin):
+        list_display = ('name', )
+        inlines = (PizzaToppingsThroughModelInline, )
+
+        def get_urls(self):
+            urls = super(PizzaAdmin, self).get_urls()
+            for inline in self.inlines:
+                if hasattr(inline, 'get_urls'):
+                    urls = inline.get_urls(self) + urls
+            return urls
+
+    admin.site.register(Pizza, PizzaAdmin)
+
+Test suite
+----------
+
+Requires Docker.
+
+    $ script/test
diff --git a/ordered_model/__init__.py b/ordered_model/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/ordered_model/admin.py b/ordered_model/admin.py
new file mode 100644
index 0000000..2940eaa
--- /dev/null
+++ b/ordered_model/admin.py
@@ -0,0 +1,262 @@
+from functools import update_wrapper
+
+from django.conf.urls import url
+from django.core.paginator import Paginator
+from django.core.urlresolvers import reverse
+from django.http import HttpResponseRedirect
+from django.shortcuts import get_object_or_404
+from django.utils.translation import ugettext_lazy as _
+from django.template.loader import render_to_string
+from django.contrib import admin
+from django.contrib.admin.utils import unquote
+from django.contrib.admin.views.main import ChangeList
+
+
+class OrderedModelAdmin(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)
+        return [
+            url(r'^(.+)/move-(up)/$', wrap(self.move_view),
+                name='{app}_{model}_order_up'.format(**self._get_model_info())),
+
+            url(r'^(.+)/move-(down)/$', wrap(self.move_view),
+                name='{app}_{model}_order_down'.format(**self._get_model_info())),
+        ] + super(OrderedModelAdmin, self).get_urls()
+
+    def _get_changelist(self, request):
+        list_display = self.get_list_display(request)
+        list_display_links = self.get_list_display_links(request, list_display)
+
+        cl = ChangeList(request, self.model, list_display,
+                        list_display_links, self.list_filter, self.date_hierarchy,
+                        self.search_fields, self.list_select_related,
+                        self.list_per_page, self.list_max_show_all, self.list_editable,
+                        self)
+
+        return cl
+
+    request_query_string = ''
+
+    def changelist_view(self, request, extra_context=None):
+        cl = self._get_changelist(request)
+        self.request_query_string = cl.get_query_string()
+        return super(OrderedModelAdmin, self).changelist_view(request, extra_context)
+
+    def move_view(self, request, object_id, direction):
+        qs = self._get_changelist(request).get_queryset(request)
+
+        obj = get_object_or_404(self.model, pk=unquote(object_id))
+        obj.move(direction, qs)
+
+        return HttpResponseRedirect('../../%s' % self.request_query_string)
+
+    def move_up_down_links(self, obj):
+        model_info = self._get_model_info()
+        return render_to_string("ordered_model/admin/order_controls.html", {
+            'app_label': model_info['app'],
+            'module_name': model_info['model'],
+            'object_id': obj.pk,
+            'urls': {
+                'up': reverse("admin:{app}_{model}_order_up".format(**model_info), args=[obj.pk, 'up']),
+                'down': reverse("admin:{app}_{model}_order_down".format(**model_info), args=[obj.pk, 'down']),
+            },
+            'query_string': self.request_query_string
+        })
+    move_up_down_links.allow_tags = True
+    move_up_down_links.short_description = _(u'Move')
+
+    def _get_model_info(self):
+        return {
+            'app': self.model._meta.app_label,
+            'model': self.model._meta.model_name,
+        }
+
+
+class OrderedTabularInline(admin.TabularInline):
+
+    ordering = None
+    list_display = ('__str__',)
+    list_display_links = ()
+    list_filter = ()
+    list_select_related = False
+    list_per_page = 100
+    list_max_show_all = 200
+    list_editable = ()
+    search_fields = ()
+    date_hierarchy = None
+    paginator = Paginator
+    preserve_filters = True
+
+    @classmethod
+    def get_model_info(cls):
+        return dict(app=cls.model._meta.app_label,
+                    model=cls.model._meta.model_name)
+
+    @classmethod
+    def get_urls(cls, model_admin):
+        def wrap(view):
+            def wrapper(*args, **kwargs):
+                return model_admin.admin_site.admin_view(view)(*args, **kwargs)
+            return update_wrapper(wrapper, view)
+        return [
+            url(r'^(.+)/{model}/(.+)/move-(up)/$'.format(**cls.get_model_info()), wrap(cls.move_view),
+                name='{app}_{model}_order_up_inline'.format(**cls.get_model_info())),
+            url(r'^(.+)/{model}/(.+)/move-(down)/$'.format(**cls.get_model_info()), wrap(cls.move_view),
+                name='{app}_{model}_order_down_inline'.format(**cls.get_model_info())),
+        ]
+
+    @classmethod
+    def get_list_display(cls, request):
+        """
+        Return a sequence containing the fields to be displayed on the
+        changelist.
+        """
+        return cls.list_display
+
+    @classmethod
+    def get_list_display_links(cls, request, list_display):
+        """
+        Return a sequence containing the fields to be displayed as links
+        on the changelist. The list_display parameter is the list of fields
+        returned by get_list_display().
+        """
+        if cls.list_display_links or not list_display:
+            return cls.list_display_links
+        else:
+            # Use only the first item in list_display as link
+            return list(list_display)[:1]
+
+    @classmethod
+    def _get_changelist(cls, request):
+        list_display = cls.get_list_display(request)
+        list_display_links = cls.get_list_display_links(request, list_display)
+
+        cl = ChangeList(request, cls.model, list_display,
+                        list_display_links, cls.list_filter, cls.date_hierarchy,
+                        cls.search_fields, cls.list_select_related,
+                        cls.list_per_page, cls.list_max_show_all, cls.list_editable,
+                        cls)
+
+        return cl
+
+    request_query_string = ''
+
+    @classmethod
+    def changelist_view(cls, request, extra_context=None):
+        cl = cls._get_changelist(request)
+        cls.request_query_string = cl.get_query_string()
+        return super(OrderedTabularInline, cls).changelist_view(request, extra_context)
+
+    @classmethod
+    def get_queryset(cls, request):
+        """
+        Returns a QuerySet of all model instances that can be edited by the
+        admin site. This is used by changelist_view.
+        """
+        qs = cls.model._default_manager.get_query_set()
+        # TODO: this should be handled by some parameter to the ChangeList.
+        ordering = cls.get_ordering(request)
+        if ordering:
+            qs = qs.order_by(*ordering)
+        return qs
+
+    @classmethod
+    def get_ordering(cls, request):
+        """
+        Hook for specifying field ordering.
+        """
+        return cls.ordering or ()  # otherwise we might try to *None, which is bad ;)
+
+    @classmethod
+    def get_paginator(cls, request, queryset, per_page, orphans=0, allow_empty_first_page=True):
+        return cls.paginator(queryset, per_page, orphans, allow_empty_first_page)
+
+    @classmethod
+    def get_search_fields(cls, request):
+        """
+        Returns a sequence containing the fields to be searched whenever
+        somebody submits a search query.
+        """
+        return cls.search_fields
+
+    @classmethod
+    def get_search_results(cls, request, queryset, search_term):
+        """
+        Returns a tuple containing a queryset to implement the search,
+        and a boolean indicating if the results may contain duplicates.
+        """
+        # Apply keyword searches.
+        def construct_search(field_name):
+            if field_name.startswith('^'):
+                return "%s__istartswith" % field_name[1:]
+            elif field_name.startswith('='):
+                return "%s__iexact" % field_name[1:]
+            elif field_name.startswith('@'):
+                return "%s__search" % field_name[1:]
+            else:
+                return "%s__icontains" % field_name
+
+        use_distinct = False
+        search_fields = cls.get_search_fields(request)
+        if search_fields and search_term:
+            orm_lookups = [construct_search(str(search_field))
+                           for search_field in search_fields]
+            for bit in search_term.split():
+                or_queries = [models.Q(**{orm_lookup: bit})
+                              for orm_lookup in orm_lookups]
+                queryset = queryset.filter(reduce(operator.or_, or_queries))
+            if not use_distinct:
+                for search_spec in orm_lookups:
+                    if lookup_needs_distinct(cls.opts, search_spec):
+                        use_distinct = True
+                        break
+
+        return queryset, use_distinct
+
+    @classmethod
+    def move_view(cls, request, admin_id, object_id, direction):
+        qs = cls._get_changelist(request).get_queryset(request)
+
+        obj = get_object_or_404(cls.model, pk=unquote(object_id))
+        obj.move(direction, qs)
+
+        return HttpResponseRedirect('../../../%s' % cls.request_query_string)
+
+    @classmethod
+    def get_preserved_filters(cls, request):
+        """
+        Returns the preserved filters querystring.
+        """
+        match = request.resolver_match
+        if cls.preserve_filters and match:
+            opts = cls.model._meta
+            current_url = '%s:%s' % (match.app_name, match.url_name)
+            changelist_url = 'admin:%s_%s_changelist' % (opts.app_label, opts.model_name)
+            if current_url == changelist_url:
+                preserved_filters = request.GET.urlencode()
+            else:
+                preserved_filters = request.GET.get('_changelist_filters')
+
+            if preserved_filters:
+                return urlencode({'_changelist_filters': preserved_filters})
+        return ''
+
+    def move_up_down_links(self, obj):
+        if obj.id:
+            return render_to_string("ordered_model/admin/order_controls.html", {
+                'app_label': self.model._meta.app_label,
+                'module_name': self.model._meta.module_name,
+                'object_id': obj.id,
+                'urls': {
+                    'up': reverse("admin:{app}_{model}_order_up_inline".format(**self.get_model_info()), args=[obj._get_order_with_respect_to().id, obj.id, 'up']),
+                    'down': reverse("admin:{app}_{model}_order_down_inline".format(**self.get_model_info()), args=[obj._get_order_with_respect_to().id, obj.id, 'down']),
+                },
+                'query_string': self.request_query_string
+            })
+        else:
+            return ''
+    move_up_down_links.allow_tags = True
+    move_up_down_links.short_description = _(u'Move')
diff --git a/ordered_model/locale/de/LC_MESSAGES/django.mo b/ordered_model/locale/de/LC_MESSAGES/django.mo
new file mode 100644
index 0000000..7275af5
Binary files /dev/null and b/ordered_model/locale/de/LC_MESSAGES/django.mo differ
diff --git a/ordered_model/locale/de/LC_MESSAGES/django.po b/ordered_model/locale/de/LC_MESSAGES/django.po
new file mode 100644
index 0000000..570cac0
--- /dev/null
+++ b/ordered_model/locale/de/LC_MESSAGES/django.po
@@ -0,0 +1,23 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL at ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-06-29 12:49+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
+"Language-Team: LANGUAGE <LL at li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: admin.py:50
+msgid "Move"
+msgstr "Bewegen"
diff --git a/ordered_model/locale/pl/LC_MESSAGES/django.mo b/ordered_model/locale/pl/LC_MESSAGES/django.mo
new file mode 100644
index 0000000..48135db
Binary files /dev/null and b/ordered_model/locale/pl/LC_MESSAGES/django.mo differ
diff --git a/ordered_model/locale/pl/LC_MESSAGES/django.po b/ordered_model/locale/pl/LC_MESSAGES/django.po
new file mode 100644
index 0000000..1c8f4f9
--- /dev/null
+++ b/ordered_model/locale/pl/LC_MESSAGES/django.po
@@ -0,0 +1,24 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL at ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-07-13 08:16+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
+"Language-Team: LANGUAGE <LL at li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
+"|| n%100>=20) ? 1 : 2)\n"
+
+#: admin.py:52
+msgid "Move"
+msgstr "Kolejność"
diff --git a/ordered_model/models.py b/ordered_model/models.py
new file mode 100644
index 0000000..dcfcf50
--- /dev/null
+++ b/ordered_model/models.py
@@ -0,0 +1,226 @@
+import warnings
+from django.contrib.contenttypes.models import ContentType
+from django.core.urlresolvers import reverse
+from django.db import models
+from django.db.models import Max, Min, F
+from django.utils.translation import ugettext as _
+
+
+class OrderedModelBase(models.Model):
+    """
+    An abstract model that allows objects to be ordered relative to each other.
+    Usage (See ``OrderedModel``):
+     - create a model subclassing ``OrderedModelBase``
+     - add an indexed ``PositiveIntegerField`` to the model
+     - set ``order_field_name`` to the name of that field
+     - use the same field name in ``Meta.ordering``
+    """
+
+    order_field_name = None
+    order_with_respect_to = None
+
+    class Meta:
+        abstract = True
+
+    def _get_order_with_respect_to(self):
+        if type(self.order_with_respect_to) is str:
+            self.order_with_respect_to = (self.order_with_respect_to,)
+        return [(field, getattr(self, field)) for field in self.order_with_respect_to]
+
+    def _valid_ordering_reference(self, reference):
+        return self.order_with_respect_to is None or (
+            self._get_order_with_respect_to() == reference._get_order_with_respect_to()
+        )
+
+    def get_ordering_queryset(self, qs=None):
+        qs = qs or self.__class__.objects.all()
+        order_with_respect_to = self.order_with_respect_to
+        if order_with_respect_to:
+            order_values = self._get_order_with_respect_to()
+            qs = qs.filter(*order_values)
+        return qs
+
+    def save(self, *args, **kwargs):
+        if getattr(self, self.order_field_name) is None:
+            c = self.get_ordering_queryset().aggregate(Max(self.order_field_name)).get(self.order_field_name + '__max')
+            setattr(self, self.order_field_name, 0 if c is None else c + 1)
+        super(OrderedModelBase, self).save(*args, **kwargs)
+
+    def delete(self, *args, **kwargs):
+        qs = self.get_ordering_queryset()
+        qs.filter(**{self.order_field_name + '__gt': getattr(self, self.order_field_name)})\
+          .update(**{self.order_field_name: F(self.order_field_name) - 1})
+        super(OrderedModelBase, self).delete(*args, **kwargs)
+
+    def _move(self, up, qs=None):
+        qs = self.get_ordering_queryset(qs)
+
+        if up:
+            qs = qs.order_by('-' + self.order_field_name)\
+                   .filter(**{self.order_field_name + '__lt': getattr(self, self.order_field_name)})
+        else:
+            qs = qs.filter(**{self.order_field_name + '__gt': getattr(self, self.order_field_name)})
+        try:
+            replacement = qs[0]
+        except IndexError:
+            # already first/last
+            return
+        order, replacement_order = getattr(self, self.order_field_name), getattr(replacement, self.order_field_name)
+        setattr(self, self.order_field_name, replacement_order)
+        setattr(replacement, self.order_field_name, order)
+        self.save()
+        replacement.save()
+
+    def move(self, direction, qs=None):
+        warnings.warn(
+            _("The method move() is deprecated and will be removed in the next release."),
+            DeprecationWarning
+        )
+        if direction == 'up':
+            self.up()
+        else:
+            self.down()
+
+    def move_down(self):
+        """
+        Move this object down one position.
+        """
+        warnings.warn(
+            _("The method move_down() is deprecated and will be removed in the next release. Please use down() instead!"),
+            DeprecationWarning
+        )
+        return self.down()
+
+    def move_up(self):
+        """
+        Move this object up one position.
+        """
+        warnings.warn(
+            _("The method move_up() is deprecated and will be removed in the next release. Please use up() instead!"),
+            DeprecationWarning
+        )
+        return self.up()
+
+    def swap(self, qs):
+        """
+        Swap the positions of this object with a reference object.
+        """
+        try:
+            replacement = qs[0]
+        except IndexError:
+            # already first/last
+            return
+        if not self._valid_ordering_reference(replacement):
+            raise ValueError(
+                "%r can only be swapped with instances of %r with equal %s fields." % (
+                    self, self.__class__, ' and '.join(["'{}'".format(o[0]) for o in self._get_order_with_respect_to()])
+                )
+            )
+        order, replacement_order = getattr(self, self.order_field_name), getattr(replacement, self.order_field_name)
+        setattr(self, self.order_field_name, replacement_order)
+        setattr(replacement, self.order_field_name, order)
+        self.save()
+        replacement.save()
+
+    def up(self):
+        """
+        Move this object up one position.
+        """
+        self.swap(self.get_ordering_queryset()
+                      .filter(**{self.order_field_name + '__lt': getattr(self, self.order_field_name)})
+                      .order_by('-' + self.order_field_name))
+
+    def down(self):
+        """
+        Move this object down one position.
+        """
+        self.swap(self.get_ordering_queryset().filter(**{self.order_field_name + '__gt': getattr(self, self.order_field_name)}))
+
+    def to(self, order):
+        """
+        Move object to a certain position, updating all affected objects to move accordingly up or down.
+        """
+        if order is None or getattr(self, self.order_field_name) == order:
+            # object is already at desired position
+            return
+        qs = self.get_ordering_queryset()
+        if getattr(self, self.order_field_name) > order:
+            qs.filter(**{self.order_field_name + '__lt': getattr(self, self.order_field_name),
+                         self.order_field_name + '__gte': order})\
+              .update(**{self.order_field_name: F(self.order_field_name) + 1})
+        else:
+            qs.filter(**{self.order_field_name + '__gt': getattr(self, self.order_field_name),
+                         self.order_field_name + '__lte': order})\
+              .update(**{self.order_field_name: F(self.order_field_name) - 1})
+        setattr(self, self.order_field_name, order)
+        self.save()
+
+    def above(self, ref):
+        """
+        Move this object above the referenced object.
+        """
+        if not self._valid_ordering_reference(ref):
+            raise ValueError(
+                "%r can only be swapped with instances of %r with equal %s fields." % (
+                    self, self.__class__, ' and '.join(["'{}'".format(o[0]) for o in self._get_order_with_respect_to()])
+                )
+            )
+        if getattr(self, self.order_field_name) == getattr(ref, self.order_field_name):
+            return
+        if getattr(self, self.order_field_name) > getattr(ref, self.order_field_name):
+            o = getattr(ref, self.order_field_name)
+        else:
+            o = self.get_ordering_queryset()\
+                    .filter(**{self.order_field_name + '__lt': getattr(ref, self.order_field_name)})\
+                    .aggregate(Max(self.order_field_name))\
+                    .get(self.order_field_name + '__max') or 0
+        self.to(o)
+
+    def below(self, ref):
+        """
+        Move this object below the referenced object.
+        """
+        if not self._valid_ordering_reference(ref):
+            raise ValueError(
+                "%r can only be swapped with instances of %r with equal %s fields." % (
+                    self, self.__class__, ' and '.join(["'{}'".format(o[0]) for o in self._get_order_with_respect_to()])
+                )
+            )
+        if getattr(self, self.order_field_name) == getattr(ref, self.order_field_name):
+            return
+        if getattr(self, self.order_field_name) > getattr(ref, self.order_field_name):
+            o = self.get_ordering_queryset()\
+                    .filter(**{self.order_field_name + '__gt': getattr(ref, self.order_field_name)})\
+                    .aggregate(Min(self.order_field_name))\
+                    .get(self.order_field_name + '__min') or 0
+        else:
+            o = getattr(ref, self.order_field_name)
+        self.to(o)
+
+    def top(self):
+        """
+        Move this object to the top of the ordered stack.
+        """
+        o = self.get_ordering_queryset().aggregate(Min(self.order_field_name)).get(self.order_field_name + '__min')
+        self.to(o)
+
+    def bottom(self):
+        """
+        Move this object to the bottom of the ordered stack.
+        """
+        o = self.get_ordering_queryset().aggregate(Max(self.order_field_name)).get(self.order_field_name + '__max')
+        self.to(o)
+
+
+class OrderedModel(OrderedModelBase):
+    """
+    An abstract model that allows objects to be ordered relative to each other.
+    Provides an ``order`` field.
+    """
+
+    order = models.PositiveIntegerField(editable=False, db_index=True)
+    order_field_name = 'order'
+
+    class Meta:
+        abstract = True
+        ordering = ('order',)
diff --git a/ordered_model/static/ordered_model/arrow-down.gif b/ordered_model/static/ordered_model/arrow-down.gif
new file mode 100644
index 0000000..a967b9f
Binary files /dev/null and b/ordered_model/static/ordered_model/arrow-down.gif differ
diff --git a/ordered_model/static/ordered_model/arrow-up.gif b/ordered_model/static/ordered_model/arrow-up.gif
new file mode 100644
index 0000000..3fe4851
Binary files /dev/null and b/ordered_model/static/ordered_model/arrow-up.gif differ
diff --git a/ordered_model/templates/ordered_model/admin/order_controls.html b/ordered_model/templates/ordered_model/admin/order_controls.html
new file mode 100644
index 0000000..dd106a2
--- /dev/null
+++ b/ordered_model/templates/ordered_model/admin/order_controls.html
@@ -0,0 +1,5 @@
+{% load static %}
+<a href="{{ urls.up }}{{query_string}}">
+    <img src="{% static 'ordered_model/arrow-up.gif' %}"></a>
+<a href="{{ urls.down }}{{query_string}}">
+    <img src="{% static 'ordered_model/arrow-down.gif' %}"></a>
diff --git a/ordered_model/tests/__init__.py b/ordered_model/tests/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/ordered_model/tests/fixtures/test_items.json b/ordered_model/tests/fixtures/test_items.json
new file mode 100644
index 0000000..e185e3d
--- /dev/null
+++ b/ordered_model/tests/fixtures/test_items.json
@@ -0,0 +1,66 @@
+[
+  {
+    "pk": 1, 
+    "model": "tests.item", 
+    "fields": {
+      "name": "1", 
+      "order": 0
+    }
+  }, 
+  {
+    "pk": 2, 
+    "model": "tests.item", 
+    "fields": {
+      "name": "2", 
+      "order": 1
+    }
+  },
+  {
+    "pk": 3, 
+    "model": "tests.item", 
+    "fields": {
+      "name": "3", 
+      "order": 2
+    }
+  },
+  {
+    "pk": 4, 
+    "model": "tests.item", 
+    "fields": {
+      "name": "4", 
+      "order": 3
+    }
+  },
+  {
+    "pk": 1,
+    "model": "tests.customorderfieldmodel",
+    "fields": {
+      "name": "1",
+      "sort_order": 0
+    }
+  },
+  {
+    "pk": 2,
+    "model": "tests.customorderfieldmodel",
+    "fields": {
+      "name": "2",
+      "sort_order": 1
+    }
+  },
+  {
+    "pk": 3,
+    "model": "tests.customorderfieldmodel",
+    "fields": {
+      "name": "3",
+      "sort_order": 2
... 581 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-django-ordered-model.git



More information about the Python-modules-commits mailing list