[Python-modules-commits] [python-django-mptt] 01/04: Import python-django-mptt_0.8.3.orig.tar.gz
Brian May
bam at moszumanska.debian.org
Wed Apr 6 03:50:05 UTC 2016
This is an automated email from the git hooks/post-receive script.
bam pushed a commit to branch master
in repository python-django-mptt.
commit 20a2efe130927acb6e52aa406666d3c76d36e836
Author: Brian May <bam at debian.org>
Date: Wed Apr 6 13:35:22 2016 +1000
Import python-django-mptt_0.8.3.orig.tar.gz
---
INSTALL | 8 +-
MANIFEST.in | 1 +
PKG-INFO | 5 +-
README.rst | 9 +-
django_mptt.egg-info/PKG-INFO | 5 +-
django_mptt.egg-info/SOURCES.txt | 12 +
django_mptt.egg-info/requires.txt | 2 +-
django_mptt.egg-info/top_level.txt | 1 +
docs/Makefile | 6 -
docs/admin.rst | 111 ++++++-
mptt/__init__.py | 3 +-
mptt/admin.py | 193 ++++++++++-
mptt/fields.py | 5 +-
mptt/managers.py | 11 +-
mptt/models.py | 14 +-
mptt/static/mptt/arrow-move.png | Bin 0 -> 457 bytes
mptt/static/mptt/disclosure-down.png | Bin 0 -> 496 bytes
mptt/static/mptt/disclosure-right.png | Bin 0 -> 464 bytes
mptt/static/mptt/draggable-admin.css | 52 +++
mptt/static/mptt/draggable-admin.js | 422 ++++++++++++++++++++++++
mptt/templatetags/mptt_admin.py | 8 +-
setup.cfg | 2 +-
setup.py | 77 ++---
tests/__init__.pyc | Bin 226 -> 210 bytes
tests/__pycache__/__init__.cpython-35.pyc | Bin 0 -> 194 bytes
tests/__pycache__/settings.cpython-35.pyc | Bin 0 -> 1525 bytes
tests/myapp/__pycache__/__init__.cpython-35.pyc | Bin 0 -> 200 bytes
tests/myapp/__pycache__/admin.cpython-35.pyc | Bin 0 -> 568 bytes
tests/myapp/__pycache__/models.cpython-35.pyc | Bin 0 -> 12127 bytes
tests/myapp/__pycache__/tests.cpython-35.pyc | Bin 0 -> 65077 bytes
tests/myapp/__pycache__/urls.cpython-35.pyc | Bin 0 -> 349 bytes
tests/myapp/admin.py | 5 +-
tests/myapp/admin.pyc | Bin 632 -> 733 bytes
tests/myapp/tests.py | 108 +++++-
tests/myapp/tests.pyc | Bin 79289 -> 81653 bytes
tests/settings.py | 1 +
tests/settings.pyc | Bin 1597 -> 2156 bytes
37 files changed, 973 insertions(+), 88 deletions(-)
diff --git a/INSTALL b/INSTALL
index db7ab38..40a3d92 100644
--- a/INSTALL
+++ b/INSTALL
@@ -9,8 +9,8 @@ somewhere on your PYTHONPATH, or symlink to it from somewhere on your
PYTHONPATH; this is useful if you're working from a git checkout.
Requires:
- - Python 2.6 or newer
- - Django 1.4.2 or newer
+ - Python 2.7 or newer
+ - Django 1.8 or newer
-You can obtain Python from http://www.python.org/ and Django from
-http://www.djangoproject.com/
+You can obtain Python from https://www.python.org/ and Django from
+https://www.djangoproject.com/
diff --git a/MANIFEST.in b/MANIFEST.in
index 8fcf272..0cc5997 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -8,3 +8,4 @@ recursive-include mptt *.json
recursive-include tests *
recursive-include mptt/templates *
recursive-include mptt/locale *
+recursive-include mptt/static *
diff --git a/PKG-INFO b/PKG-INFO
index 77d7b08..a24fcc9 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,12 +1,12 @@
Metadata-Version: 1.1
Name: django-mptt
-Version: 0.8.0
+Version: 0.8.3
Summary: Utilities for implementing Modified Preorder Tree Traversal
with your Django Models and working with trees of Model instances.
Home-page: http://github.com/django-mptt/django-mptt
Author: Craig de Stigter
Author-email: craig.ds at gmail.com
-License: UNKNOWN
+License: MIT License
Description: UNKNOWN
Platform: UNKNOWN
Classifier: Development Status :: 4 - Beta
@@ -22,6 +22,7 @@ Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.2
Classifier: Programming Language :: Python :: 3.3
Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Utilities
diff --git a/README.rst b/README.rst
index 021800e..737a0ee 100644
--- a/README.rst
+++ b/README.rst
@@ -39,7 +39,7 @@ details about how the technique itself works:
What is ``django-mptt``?
========================
-``django-mptt`` is a reusable Django app which aims to make it easy for you
+``django-mptt`` is a reusable Django app which aims to make it easy for you
to use MPTT with your own Django models.
It takes care of the details of managing a database table as a tree
@@ -49,7 +49,7 @@ Requirements
------------
* Python 2.7 or 3.2+
-* A supported version of django (currently 1.8+)
+* A supported version of Django (currently 1.8+)
Feature overview
----------------
@@ -72,7 +72,7 @@ Feature overview
* A ``TreeManager`` manager is added to all registered models. This provides
methods to:
-
+
* move nodes around a tree, or into a different tree
* insert a node anywhere in a tree
* rebuild the MPTT fields for the tree (useful when you do bulk updates
@@ -83,3 +83,6 @@ Feature overview
* Utility functions for tree models.
* Template tags and filters for rendering trees.
+
+* Admin classes for visualizing and modifying trees in Django's administration
+ interface.
diff --git a/django_mptt.egg-info/PKG-INFO b/django_mptt.egg-info/PKG-INFO
index 77d7b08..a24fcc9 100644
--- a/django_mptt.egg-info/PKG-INFO
+++ b/django_mptt.egg-info/PKG-INFO
@@ -1,12 +1,12 @@
Metadata-Version: 1.1
Name: django-mptt
-Version: 0.8.0
+Version: 0.8.3
Summary: Utilities for implementing Modified Preorder Tree Traversal
with your Django Models and working with trees of Model instances.
Home-page: http://github.com/django-mptt/django-mptt
Author: Craig de Stigter
Author-email: craig.ds at gmail.com
-License: UNKNOWN
+License: MIT License
Description: UNKNOWN
Platform: UNKNOWN
Classifier: Development Status :: 4 - Beta
@@ -22,6 +22,7 @@ Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.2
Classifier: Programming Language :: Python :: 3.3
Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Utilities
diff --git a/django_mptt.egg-info/SOURCES.txt b/django_mptt.egg-info/SOURCES.txt
index d366748..675959f 100644
--- a/django_mptt.egg-info/SOURCES.txt
+++ b/django_mptt.egg-info/SOURCES.txt
@@ -64,6 +64,11 @@ mptt/locale/pt_BR/LC_MESSAGES/django.mo
mptt/locale/pt_BR/LC_MESSAGES/django.po
mptt/locale/ru/LC_MESSAGES/django.mo
mptt/locale/ru/LC_MESSAGES/django.po
+mptt/static/mptt/arrow-move.png
+mptt/static/mptt/disclosure-down.png
+mptt/static/mptt/disclosure-right.png
+mptt/static/mptt/draggable-admin.css
+mptt/static/mptt/draggable-admin.js
mptt/templates/admin/grappelli_mptt_change_list.html
mptt/templates/admin/grappelli_mptt_change_list_results.html
mptt/templates/admin/mptt_change_list.html
@@ -83,9 +88,11 @@ tests/settings.pyc
tests/__pycache__/__init__.cpython-32.pyc
tests/__pycache__/__init__.cpython-33.pyc
tests/__pycache__/__init__.cpython-34.pyc
+tests/__pycache__/__init__.cpython-35.pyc
tests/__pycache__/settings.cpython-32.pyc
tests/__pycache__/settings.cpython-33.pyc
tests/__pycache__/settings.cpython-34.pyc
+tests/__pycache__/settings.cpython-35.pyc
tests/myapp/__init__.py
tests/myapp/__init__.pyc
tests/myapp/admin.py
@@ -100,14 +107,19 @@ tests/myapp/urls.pyc
tests/myapp/__pycache__/__init__.cpython-32.pyc
tests/myapp/__pycache__/__init__.cpython-33.pyc
tests/myapp/__pycache__/__init__.cpython-34.pyc
+tests/myapp/__pycache__/__init__.cpython-35.pyc
tests/myapp/__pycache__/admin.cpython-34.pyc
+tests/myapp/__pycache__/admin.cpython-35.pyc
tests/myapp/__pycache__/models.cpython-32.pyc
tests/myapp/__pycache__/models.cpython-33.pyc
tests/myapp/__pycache__/models.cpython-34.pyc
+tests/myapp/__pycache__/models.cpython-35.pyc
tests/myapp/__pycache__/tests.cpython-32.pyc
tests/myapp/__pycache__/tests.cpython-33.pyc
tests/myapp/__pycache__/tests.cpython-34.pyc
+tests/myapp/__pycache__/tests.cpython-35.pyc
tests/myapp/__pycache__/urls.cpython-34.pyc
+tests/myapp/__pycache__/urls.cpython-35.pyc
tests/myapp/fixtures/categories.json
tests/myapp/fixtures/genres.json
tests/myapp/fixtures/items.json
diff --git a/django_mptt.egg-info/requires.txt b/django_mptt.egg-info/requires.txt
index 363a653..531dd9b 100644
--- a/django_mptt.egg-info/requires.txt
+++ b/django_mptt.egg-info/requires.txt
@@ -1 +1 @@
-Django>=1.8
\ No newline at end of file
+Django>=1.8
diff --git a/django_mptt.egg-info/top_level.txt b/django_mptt.egg-info/top_level.txt
index 7c545e2..5f89f8d 100644
--- a/django_mptt.egg-info/top_level.txt
+++ b/django_mptt.egg-info/top_level.txt
@@ -1 +1,2 @@
mptt
+tests
diff --git a/docs/Makefile b/docs/Makefile
index 1a7e91c..876c7a0 100644
--- a/docs/Makefile
+++ b/docs/Makefile
@@ -39,12 +39,6 @@ clean:
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
- @echo "renaming sphinx dirs so they're okay for gh-pages"
- sed -i -s -e 's/"_static\//"static\//g' $(BUILDDIR)/html/*.html
- mv $(BUILDDIR)/html/_static/ $(BUILDDIR)/html/static
- sed -i -s -e 's/"_sources\//"sources\//g' $(BUILDDIR)/html/*.html
- mv $(BUILDDIR)/html/_sources/ $(BUILDDIR)/html/sources
- @echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
diff --git a/docs/admin.rst b/docs/admin.rst
index a4b003f..8f5027b 100644
--- a/docs/admin.rst
+++ b/docs/admin.rst
@@ -3,11 +3,16 @@ Admin classes
=============
``mptt.admin.MPTTModelAdmin``
------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This is a bare-bones tree admin. All it does is enforce ordering, and indent the nodes
in the tree to make a pretty tree list view.
+.. image:: mpttmodeladmin-genres.png
+ :align: center
+ :width: 26.21cm
+ :alt: MPTTModelAdmin screenshot
+
Usage::
from django.contrib import admin
@@ -42,3 +47,107 @@ to your MPTTModelAdmin::
class CustomMPTTModelAdmin(MPTTModelAdmin):
mptt_indent_field = "some_node_field"
# …
+
+
+``mptt.admin.DraggableMPTTAdmin``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. versionadded:: 0.8.1
+
+.. image:: draggablempttadmin-genres.png
+ :align: center
+ :width: 26.39cm
+ :alt: DraggableMPTTAdmin screenshot
+
+This is a tree admin based on FeinCMS_ offering drag-drop functionality for
+moving nodes::
+
+ from django.contrib import admin
+ from mptt.admin import DraggableMPTTAdmin
+ from myproject.myapp.models import Node
+
+ admin.site.register(
+ Node,
+ DraggableMPTTAdmin,
+ list_display=(
+ 'tree_actions',
+ 'indented_title',
+ # ...more fields if you feel like it...
+ ),
+ list_display_links=(
+ 'indented_title',
+ ),
+ )
+
+
+.. note::
+
+ Supported browsers include all recent versions of Firefox, Chrome,
+ Safari and Internet Explorer (9 or better).
+
+.. warning::
+
+ Does not work well with big trees (more than a few hundred nodes, or trees
+ deeper than 10 levels). Patches implementing lazy-loading of deep trees
+ are very much appreciated.
+
+
+It is recommended that ``tree_actions`` is the first value passed to
+``list_display``; this also requires you to specify ``list_display_links``
+because ``tree_actions`` cannot be used as the object link field.
+
+``indented_title`` does nothing but return the indented self-description
+of nodes, ``20px`` per level (or the value of ``mptt_level_indent``,
+see below.)
+
+``list_per_page`` is set to 2000 by default (which effectively disables
+pagination for most trees).
+
+
+Replacing ``indented_title``
+----------------------------
+
+If you want to replace the ``indented_title`` method with your own, we
+recommend using the following code::
+
+ from django.utils.html import format_html
+
+ class MyDraggableMPTTAdmin(DraggableMPTTAdmin):
+ list_display = ('tree_actions', 'something')
+ list_display_links = ('something',)
+
+ def something(self, instance):
+ return format_html(
+ '<div style="text-indent:{}px">{}</div>',
+ instance._mpttfield('level') * self.mptt_level_indent,
+ item.name, # Or whatever you want to put here
+ )
+ something.short_description = _('something nice')
+
+For changing the indentation per node, look below. Simply replacing
+``indented_title`` is insufficient because the indentation also needs
+to be communicated to the JavaScript code.
+
+
+Overriding admin templates per app or model
+-------------------------------------------
+
+``DraggableMPTTAdmin`` uses the stock admin changelist template with some CSS
+and JavaScript on top, so simply follow the official guide for
+`overriding admin templates`_.
+
+
+Changing the indentation of nodes
+---------------------------------
+
+Simply set ``mptt_level_indent`` to a different pixel value (defaults
+to ``20``)::
+
+ # ...
+ class MyDraggableMPTTAdmin(DraggableMPTTAdmin):
+ mptt_level_indent = 50
+ # ...
+
+
+.. _overriding admin templates: https://docs.djangoproject.com/en/1.9/ref/contrib/admin/#overriding-admin-templates
+.. _FeinCMS: https://github.com/feincms/feincms/
diff --git a/mptt/__init__.py b/mptt/__init__.py
index 87ec9ae..f3a8209 100644
--- a/mptt/__init__.py
+++ b/mptt/__init__.py
@@ -1,6 +1,7 @@
from __future__ import unicode_literals
-VERSION = (0, 8, 0)
+VERSION = (0, 8, 3)
+__version__ = '.'.join(str(v) for v in VERSION)
def register(*args, **kwargs):
diff --git a/mptt/admin.py b/mptt/admin.py
index f90413a..730a146 100644
--- a/mptt/admin.py
+++ b/mptt/admin.py
@@ -1,15 +1,23 @@
from __future__ import unicode_literals
+import json
+
+from django import http
from django.conf import settings
from django.contrib.admin.actions import delete_selected
from django.contrib.admin.options import ModelAdmin
+from django.db import IntegrityError, transaction
+from django.forms.utils import flatatt
+from django.templatetags.static import static
from django.utils.encoding import force_text
-from django.utils.translation import ugettext as _
+from django.utils.html import format_html, mark_safe
+from django.utils.translation import ugettext as _, ugettext_lazy
+from mptt.exceptions import InvalidMove
from mptt.forms import MPTTAdminForm, TreeNodeChoiceField
from mptt.models import MPTTModel, TreeForeignKey
-__all__ = ('MPTTModelAdmin', 'MPTTAdminForm')
+__all__ = ('MPTTModelAdmin', 'MPTTAdminForm', 'DraggableMPTTAdmin')
IS_GRAPPELLI_INSTALLED = 'grappelli' in settings.INSTALLED_APPS
@@ -84,3 +92,184 @@ class MPTTModelAdmin(ModelAdmin):
'delete_selected',
_('Delete selected %(verbose_name_plural)s'))
return actions
+
+
+class JS(object):
+ """
+ Use this to insert a script tag via ``forms.Media`` containing additional
+ attributes (such as ``id`` and ``data-*`` for CSP-compatible data
+ injection.)::
+
+ media.add_js([
+ JS('asset.js', {
+ 'id': 'asset-script',
+ 'data-the-answer': '"42"',
+ }),
+ ])
+
+ The rendered media tag (via ``{{ media.js }}`` or ``{{ media }}`` will
+ now contain a script tag as follows, without line breaks::
+
+ <script type="text/javascript" src="/static/asset.js"
+ data-answer=""42"" id="asset-script"></script>
+
+ The attributes are automatically escaped. The data attributes may now be
+ accessed inside ``asset.js``::
+
+ var answer = document.querySelector('#asset-script').dataset.answer;
+ """
+ def __init__(self, js, attrs):
+ self.js = js
+ self.attrs = attrs
+
+ def startswith(self, _):
+ # Masquerade as absolute path so that we are returned as-is.
+ return True
+
+ def __html__(self):
+ return format_html(
+ '{}"{}',
+ static(self.js),
+ mark_safe(flatatt(self.attrs)),
+ ).rstrip('"')
+
+
+class DraggableMPTTAdmin(MPTTModelAdmin):
+ """
+ The ``DraggableMPTTAdmin`` modifies the standard Django administration
+ change list to a drag-drop enabled interface.
+ """
+
+ change_list_template = None # Back to default
+ list_per_page = 2000 # This will take a really long time to load.
+ list_display = ('tree_actions', 'indented_title') # Sane defaults.
+ list_display_links = ('indented_title',) # Sane defaults.
+ mptt_level_indent = 20
+
+ def tree_actions(self, item):
+ try:
+ url = item.get_absolute_url()
+ except Exception: # Nevermind.
+ url = ''
+
+ return format_html(
+ '<div class="drag-handle"></div>'
+ '<div class="tree-node" data-pk="{}" data-level="{}"'
+ ' data-url="{}"></div>',
+ item.pk,
+ item._mpttfield('level'),
+ url,
+ )
+ tree_actions.short_description = ''
+
+ def indented_title(self, item):
+ """
+ Generate a short title for an object, indent it depending on
+ the object's depth in the hierarchy.
+ """
+ return format_html(
+ '<div style="text-indent:{}px">{}</div>',
+ item._mpttfield('level') * self.mptt_level_indent,
+ item,
+ )
+ indented_title.short_description = ugettext_lazy('title')
+
+ def changelist_view(self, request, *args, **kwargs):
+ if request.is_ajax() and request.POST.get('cmd') == 'move_node':
+ return self._move_node(request)
+
+ response = super(DraggableMPTTAdmin, self).changelist_view(
+ request, *args, **kwargs)
+
+ try:
+ response.context_data['media'].add_css({'all': (
+ 'mptt/draggable-admin.css',
+ )})
+ response.context_data['media'].add_js((
+ JS('mptt/draggable-admin.js', {
+ 'id': 'draggable-admin-context',
+ 'data-context': json.dumps(self._tree_context(request)),
+ }),
+ ),)
+ except (AttributeError, KeyError):
+ # Not meant for us if there is no context_data attribute (no
+ # TemplateResponse) or no media in the context.
+ pass
+
+ return response
+
+ @transaction.atomic
+ def _move_node(self, request):
+ position = request.POST.get('position')
+ if position not in ('last-child', 'left', 'right'):
+ self.message_user(request, _('Did not understand moving instruction.'))
+ return http.HttpResponse('FAIL, unknown instruction.')
+
+ queryset = self.get_queryset(request)
+ try:
+ cut_item = queryset.get(pk=request.POST.get('cut_item'))
+ pasted_on = queryset.get(pk=request.POST.get('pasted_on'))
+ except (self.model.DoesNotExist, TypeError, ValueError):
+ self.message_user(request, _('Objects have disappeared, try again.'))
+ return http.HttpResponse('FAIL, invalid objects.')
+
+ if not self.has_change_permission(request, cut_item):
+ self.message_user(request, _('No permission'))
+ return http.HttpResponse('FAIL, no permission.')
+
+ try:
+ self.model._tree_manager.move_node(cut_item, pasted_on, position)
+ except InvalidMove as e:
+ self.message_user(request, '%s' % e)
+ return http.HttpResponse('FAIL, invalid move.')
+ except IntegrityError as e:
+ self.message_user(request, _('Database error: %s') % e)
+ raise
+
+ self.message_user(
+ request,
+ _('%s has been successfully moved.') % cut_item)
+ return http.HttpResponse('OK, moved.')
+
+ def _tree_context(self, request):
+ opts = self.model._meta
+
+ return {
+ 'storageName': 'tree_%s_%s_collapsed' % (opts.app_label, opts.model_name),
+ 'treeStructure': self._build_tree_structure(self.get_queryset(request)),
+ 'levelIndent': self.mptt_level_indent,
+ 'messages': {
+ 'before': _('move node before node'),
+ 'child': _('move node to child position'),
+ 'after': _('move node after node'),
+ 'collapseTree': _('Collapse tree'),
+ 'expandTree': _('Expand tree'),
+ },
+ }
+
+ def _build_tree_structure(self, queryset):
+ """
+ Build an in-memory representation of the item tree, trying to keep
+ database accesses down to a minimum. The returned dictionary looks like
+ this (as json dump):
+
+ {"6": [7, 8, 10]
+ "7": [12],
+ ...
+ }
+
+ Leaves are not included in the dictionary.
+ """
+ all_nodes = {}
+
+ mptt_opts = self.model._mptt_meta
+ items = queryset.values_list(
+ 'pk',
+ '%s_id' % mptt_opts.parent_attr,
+ )
+ for p_id, parent_id in items:
+ all_nodes.setdefault(
+ str(parent_id) if parent_id else 0,
+ [],
+ ).append(p_id)
+ return all_nodes
diff --git a/mptt/fields.py b/mptt/fields.py
index 69b809a..f7a4ec3 100644
--- a/mptt/fields.py
+++ b/mptt/fields.py
@@ -3,13 +3,14 @@ Model fields for working with trees.
"""
from __future__ import unicode_literals
-__all__ = ('TreeForeignKey', 'TreeOneToOneField', 'TreeManyToManyField')
-
from django.db import models
from django.conf import settings
from mptt.forms import TreeNodeChoiceField, TreeNodeMultipleChoiceField
+__all__ = ('TreeForeignKey', 'TreeOneToOneField', 'TreeManyToManyField')
+
+
class TreeForeignKey(models.ForeignKey):
"""
Extends the foreign key, but uses mptt's ``TreeNodeChoiceField`` as
diff --git a/mptt/managers.py b/mptt/managers.py
index bac96ad..657624e 100644
--- a/mptt/managers.py
+++ b/mptt/managers.py
@@ -5,7 +5,6 @@ from __future__ import unicode_literals
import contextlib
from itertools import groupby
-import django
from django.db import models, connections, router
from django.db.models import F, ManyToManyField, Max, Q
from django.utils.translation import ugettext as _
@@ -80,11 +79,11 @@ class TreeManager(models.Manager.from_queryset(TreeQuerySet)):
"""
Ensures that this manager always returns nodes in tree order.
"""
- if django.VERSION < (1, 7):
- qs = TreeQuerySet(self.model, using=self._db)
- else:
- qs = super(TreeManager, self).get_queryset(*args, **kwargs)
- return qs.order_by(self.tree_id_attr, self.left_attr)
+ return super(TreeManager, self).get_queryset(
+ *args, **kwargs
+ ).order_by(
+ self.tree_id_attr, self.left_attr
+ )
def _get_queryset_relatives(self, queryset, direction, include_self):
"""
diff --git a/mptt/models.py b/mptt/models.py
index 0d9b8b0..0f84009 100644
--- a/mptt/models.py
+++ b/mptt/models.py
@@ -269,8 +269,8 @@ class MPTTModelBase(ModelBase):
else:
bases = [base for base in cls.mro() if issubclass(base, MPTTModel)]
for base in bases:
- if (not (base._meta.abstract or base._meta.proxy)
- and base._tree_manager.tree_model is base):
+ if (not (base._meta.abstract or base._meta.proxy) and
+ base._tree_manager.tree_model is base):
cls._mptt_tracking_base = base
break
if cls is cls._mptt_tracking_base:
@@ -754,8 +754,8 @@ class MPTTModel(six.with_metaclass(MPTTModelBase, models.Model)):
right = getattr(self, opts.right_attr)
return (
- left > getattr(other, opts.left_attr)
- and right < getattr(other, opts.right_attr))
+ left > getattr(other, opts.left_attr) and
+ right < getattr(other, opts.right_attr))
@raise_if_unsaved
def is_ancestor_of(self, other, include_self=False):
@@ -902,9 +902,9 @@ class MPTTModel(six.with_metaclass(MPTTModelBase, models.Model)):
# we need to update the parent.rght so things like
# get_children and get_descendant_count work correctly.
update_cached_parent = (
- getattr(self, opts.tree_id_attr) != getattr(parent, opts.tree_id_attr)
- or getattr(self, opts.left_attr) < getattr(parent, opts.left_attr)
- or getattr(self, opts.right_attr) > getattr(parent, opts.right_attr))
+ getattr(self, opts.tree_id_attr) != getattr(parent, opts.tree_id_attr) or # noqa
+ getattr(self, opts.left_attr) < getattr(parent, opts.left_attr) or
+ getattr(self, opts.right_attr) > getattr(parent, opts.right_attr))
if right_sibling:
self._tree_manager._move_node(
diff --git a/mptt/static/mptt/arrow-move.png b/mptt/static/mptt/arrow-move.png
new file mode 100644
index 0000000..d528679
Binary files /dev/null and b/mptt/static/mptt/arrow-move.png differ
diff --git a/mptt/static/mptt/disclosure-down.png b/mptt/static/mptt/disclosure-down.png
new file mode 100644
index 0000000..5f21757
Binary files /dev/null and b/mptt/static/mptt/disclosure-down.png differ
diff --git a/mptt/static/mptt/disclosure-right.png b/mptt/static/mptt/disclosure-right.png
new file mode 100644
index 0000000..50741ef
Binary files /dev/null and b/mptt/static/mptt/disclosure-right.png differ
diff --git a/mptt/static/mptt/draggable-admin.css b/mptt/static/mptt/draggable-admin.css
new file mode 100644
index 0000000..936daad
--- /dev/null
+++ b/mptt/static/mptt/draggable-admin.css
@@ -0,0 +1,52 @@
+.field-tree_actions {
+ width: 50px;
+ padding: 2px;
+}
+
+.field-tree_actions > div {
+ display: inline-block;
+ vertical-align: middle;
+ background-repeat: no-repeat;
+ width: 18px;
+ height: 18px;
+ margin: 7px 2px 0 0;
+}
+
+.tree-node { cursor: pointer; }
+.tree-node.children { background-image: url(disclosure-down.png); }
+.tree-node.closed { background-image: url(disclosure-right.png); }
+
+.drag-handle { background-image: url(arrow-move.png); cursor: move; }
+
+/* focus */
+#result_list tbody tr:focus {
+ background-color: #ffffcc !important; outline: 0px; }
+
+#drag-line {
+ position: absolute;
+ height: 3px;
+ font-size: 0px;
+ background-color: #417690;
+}
+
+#drag-line:before {
+ content: ' ';
+ display: block;
+ position: absolute;
+ height: 10px;
+ width: 10px;
+ background: #417690;
+ margin: -3px 0 0 0;
+ border-radius: 9px;
+}
+
+#drag-line span {
+ display: block;
+ position: absolute;
+ left: 15px;
+ bottom: 0;
+ width: 300px;
+ height: 18px;
+ color: #417690;
+ font-size: 12px;
+}
diff --git a/mptt/static/mptt/draggable-admin.js b/mptt/static/mptt/draggable-admin.js
new file mode 100644
index 0000000..ec141ad
--- /dev/null
+++ b/mptt/static/mptt/draggable-admin.js
@@ -0,0 +1,422 @@
+/* global django */
+
+// IE<9 lacks Array.prototype.indexOf
+if (!Array.prototype.indexOf) {
+ Array.prototype.indexOf = function(needle) {
+ for (var i=0, l=this.length; i<l; ++i) {
+ if (this[i] === needle) return i;
+ }
+ return -1;
+ };
+}
+
+// https://github.com/jquery/jquery-ui/blob/master/ui/disable-selection.js
+django.jQuery.fn.extend({
+ disableSelection: (function() {
+ var eventType = 'onselectstart' in document.createElement('div') ? 'selectstart' : 'mousedown';
+
+ return function() {
+ return this.on(eventType + '.ui-disableSelection', function(event) {
+ event.preventDefault();
+ });
+ };
+ })(),
+
+ enableSelection: function() {
+ return this.off('.ui-disableSelection');
+ }
+});
+
+
+django.jQuery(function($){
+ // We are not on a changelist it seems.
+ if (!document.getElementById('result_list')) return;
+
+ var DraggableMPTTAdmin = null;
+
+ function isExpandedNode(id) {
+ return DraggableMPTTAdmin.collapsedNodes.indexOf(id) == -1;
+ }
+
+ function markNodeAsExpanded(id) {
+ // remove itemId from array of collapsed nodes
+ var idx = DraggableMPTTAdmin.collapsedNodes.indexOf(id);
+ if(idx >= 0)
+ DraggableMPTTAdmin.collapsedNodes.splice(idx, 1);
+ }
+
+ function markNodeAsCollapsed(id) {
+ if(isExpandedNode(id))
+ DraggableMPTTAdmin.collapsedNodes.push(id);
+ }
+
+ function treeNode(pk) {
+ return $('.tree-node[data-pk="' + pk + '"]');
+ }
+
+ // toggle children
+ function doToggle(id, show) {
+ var children = DraggableMPTTAdmin.treeStructure[id] || [];
+ for (var i=0; i<children.length; ++i) {
+ var childId = children[i];
+ if(show) {
+ treeNode(childId).closest('tr').show();
+ // only reveal children if current node is not collapsed
+ if(isExpandedNode(childId)) {
+ doToggle(childId, show);
+ }
+ } else {
+ treeNode(childId).closest('tr').hide();
+ // always recursively hide children
+ doToggle(childId, show);
+ }
+ }
+ }
+
+ function rowLevel($row) {
+ try {
+ return $row.find('.tree-node').data('level') || 0;
+ } catch (e) {
+ return 0;
+ }
+ }
+
+ /* Thanks, Django */
+ function getCookie(name) {
+ var cookieValue = null;
+ if (document.cookie && document.cookie != '') {
+ var cookies = document.cookie.split(';');
+ for (var i = 0; i < cookies.length; i++) {
+ var cookie = $.trim(cookies[i]);
+ // Does this cookie string begin with the name we want?
+ if (cookie.substring(0, name.length + 1) == (name + '=')) {
+ cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
+ break;
+ }
+ }
+ }
+ return cookieValue;
+ }
+
+ /*
+ * FeinCMS Drag-n-drop tree reordering.
+ * Based upon code by bright4 for Radiant CMS, rewritten for
+ * FeinCMS by Bjorn Post.
+ *
+ * September 2010
+ */
+ $.extend($.fn.feinTree = function() {
+ $.each(DraggableMPTTAdmin.treeStructure, function(key, value) {
+ treeNode(key).addClass('children');
+ });
+
+ $('div.drag-handle').bind('mousedown', function(event) {
+ var BEFORE = 'before';
+ var AFTER = 'after';
+ var CHILD = 'child';
+ var CHILD_PAD = DraggableMPTTAdmin.levelIndent;
+ var originalRow = $(event.target).closest('tr');
+ var rowHeight = originalRow.height();
+ var moveTo = new Object();
+ var resultListWidth = $('#result_list').width();
+
+ $('body').addClass('dragging').disableSelection().bind('mousemove', function(event) {
+ // Remove focus
+ originalRow.blur();
+
+ // attach dragged item to mouse
+ var cloned = originalRow.html();
+ if($('#ghost').length == 0) {
+ $('<div id="ghost"></div>').appendTo('body');
+ }
+ $('#ghost').html(cloned).css({
+ 'opacity': .8,
+ 'position': 'absolute',
+ 'top': event.pageY,
+ 'left': event.pageX - 30,
+ 'width': 600
+ });
+
+ // check on edge of screen
+ if(event.pageY+100 > $(window).height()+$(window).scrollTop()) {
+ $('html,body').stop().animate({scrollTop: $(window).scrollTop()+250 }, 500);
+ } else if(event.pageY-50 < $(window).scrollTop()) {
+ $('html,body').stop().animate({scrollTop: $(window).scrollTop()-250 }, 500);
+ }
+
+ // check if drag-line element already exists, else append
+ if($('#drag-line').length < 1) {
+ $('body').append('<div id="drag-line"><span></span></div>');
+ }
+
+ // loop trough all rows
+ $('tr', originalRow.parent()).each(function(index, el) {
+ var element = $(el),
+ top = element.offset().top,
+ next;
+
+ // check if mouse is over a row
+ if (event.pageY >= top && event.pageY < top + rowHeight) {
+ var targetRow = null,
+ targetLoc = null,
+ elementLevel = rowLevel(element);
+
+ if (event.pageY >= top && event.pageY < top + rowHeight / 3) {
+ targetRow = element;
+ targetLoc = BEFORE;
+ } else if (event.pageY >= top + rowHeight / 3 && event.pageY < top + rowHeight * 2 / 3) {
+ next = element.next();
+ // there's no point in allowing adding children when there are some already
+ // better move the items to the correct place right away
+ if (!next.length || rowLevel(next) <= elementLevel) {
+ targetRow = element;
+ targetLoc = CHILD;
+ }
+ } else if (event.pageY >= top + rowHeight * 2 / 3 && event.pageY < top + rowHeight) {
+ next = element.next();
+ if (!next.length || rowLevel(next) <= elementLevel) {
+ targetRow = element;
+ targetLoc = AFTER;
+ }
+ }
+
+ if(targetRow) {
+
+ // Positioning relative to cell containing the link
+ var offset = targetRow.find('th').offset();
+ var left = offset.left
+ + rowLevel(targetRow) * CHILD_PAD
+ + (targetLoc == CHILD ? CHILD_PAD : 0)
+ + 5; // Center of the circle aligns with start of link text (cell padding!)
+
+ $('#drag-line').css({
+ 'width': resultListWidth - left,
+ 'left': left,
+ 'top': offset.top + (targetLoc == BEFORE ? 0 : rowHeight)
+ }).find('span').text(DraggableMPTTAdmin.messages[targetLoc] || '');
+
+ // Store the found row and options
+ moveTo.hovering = element;
+ moveTo.relativeTo = targetRow;
+ moveTo.side = targetLoc;
+
+ return true;
+ }
+ }
+ });
+ });
+
+ $('body').keydown(function(event) {
+ if (event.which == '27') {
+ $('#drag-line').remove();
+ $('#ghost').remove();
+ $('body').removeClass('dragging').enableSelection().unbind('mousemove').unbind('mouseup');
+ event.preventDefault();
+ }
+ });
+
+ $('body').bind('mouseup', function() {
+ if(moveTo.relativeTo) {
+ var cutItem = originalRow.find('.tree-node').data('pk');
+ var pastedOn = moveTo.relativeTo.find('.tree-node').data('pk');
+
+ // get out early if items are the same
+ if(cutItem != pastedOn) {
+ var isParent = (
+ rowLevel(moveTo.relativeTo.next()) >
+ rowLevel(moveTo.relativeTo));
+
+ var position = '';
+
+ // determine position
+ if(moveTo.side == CHILD && !isParent) {
+ position = 'last-child';
+ } else if (moveTo.side == BEFORE) {
... 530 lines suppressed ...
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-django-mptt.git
More information about the Python-modules-commits
mailing list