[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