[Python-modules-commits] [python-django-mptt] 01/03: Import python-django-mptt_0.8.0.orig.tar.gz

Brian May bam at moszumanska.debian.org
Sun Jan 3 04:41:38 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 a68ac09fedef4e685cd7c568ea6ff167873deb41
Author: Brian May <bam at debian.org>
Date:   Sun Jan 3 15:22:34 2016 +1100

    Import python-django-mptt_0.8.0.orig.tar.gz
---
 PKG-INFO                                           |   3 +-
 README.rst                                         |   4 +-
 django_mptt.egg-info/PKG-INFO                      |   3 +-
 django_mptt.egg-info/SOURCES.txt                   |  13 +
 django_mptt.egg-info/requires.txt                  |   2 +-
 docs/admin.rst                                     |   6 +-
 docs/autogenerated.rst                             |   2 +-
 docs/index.rst                                     |   4 +-
 docs/models.rst                                    |   3 +-
 docs/mptt.querysets.rst                            |   7 +
 docs/mptt.rst                                      |   3 +-
 docs/upgrade.rst                                   |  12 +
 docs/utilities.rst                                 |  26 ++
 mptt/__init__.py                                   |   2 +-
 mptt/admin.py                                      |  19 +-
 mptt/fields.py                                     |   5 +-
 mptt/forms.py                                      |   5 +-
 mptt/locale/es_AR/LC_MESSAGES/django.mo            | Bin 0 -> 3001 bytes
 mptt/locale/es_AR/LC_MESSAGES/django.po            | 126 +++++++++
 mptt/managers.py                                   |  59 ++--
 mptt/models.py                                     | 101 ++++---
 mptt/querysets.py                                  |  19 +-
 mptt/signals.py                                    |  11 +
 mptt/templates/admin/mptt_change_list_results.html |   9 +-
 mptt/templatetags/mptt_admin.py                    |  34 ++-
 mptt/templatetags/mptt_tags.py                     |  96 +------
 mptt/utils.py                                      |  81 +++++-
 setup.py                                           |   4 +-
 tests/__pycache__/__init__.cpython-34.pyc          | Bin 0 -> 194 bytes
 tests/__pycache__/settings.cpython-34.pyc          | Bin 0 -> 1500 bytes
 tests/myapp/__pycache__/__init__.cpython-34.pyc    | Bin 0 -> 200 bytes
 tests/myapp/__pycache__/admin.cpython-34.pyc       | Bin 0 -> 497 bytes
 tests/myapp/__pycache__/models.cpython-34.pyc      | Bin 0 -> 12127 bytes
 tests/myapp/__pycache__/tests.cpython-34.pyc       | Bin 0 -> 63200 bytes
 tests/myapp/__pycache__/urls.cpython-34.pyc        | Bin 0 -> 349 bytes
 tests/myapp/doctests.txt                           |  53 ++--
 tests/myapp/models.py                              | 112 +++++---
 tests/myapp/models.pyc                             | Bin 17761 -> 18839 bytes
 tests/myapp/tests.py                               | 302 ++++++++++++++++-----
 tests/myapp/tests.pyc                              | Bin 72578 -> 79289 bytes
 tests/myapp/urls.py                                |   7 +-
 tests/myapp/urls.pyc                               | Bin 454 -> 407 bytes
 tests/mydatabase                                   | Bin 0 -> 610304 bytes
 tests/requirements.txt                             |   3 +
 tests/settings.py                                  |  33 ++-
 tests/settings.pyc                                 | Bin 1423 -> 1597 bytes
 46 files changed, 797 insertions(+), 372 deletions(-)

diff --git a/PKG-INFO b/PKG-INFO
index bed1bcd..77d7b08 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: django-mptt
-Version: 0.7.4
+Version: 0.8.0
 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
@@ -17,7 +17,6 @@ Classifier: License :: OSI Approved :: MIT License
 Classifier: Operating System :: OS Independent
 Classifier: Programming Language :: Python
 Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.6
 Classifier: Programming Language :: Python :: 2.7
 Classifier: Programming Language :: Python :: 3
 Classifier: Programming Language :: Python :: 3.2
diff --git a/README.rst b/README.rst
index f69e8b3..021800e 100644
--- a/README.rst
+++ b/README.rst
@@ -48,8 +48,8 @@ structure and provides tools for working with trees of model instances.
 Requirements
 ------------
 
-* Python 2.6+ (with experimental support for python 3.2+ and PyPy)
-* Django 1.4.2+
+* Python 2.7 or 3.2+
+* A supported version of django (currently 1.8+)
 
 Feature overview
 ----------------
diff --git a/django_mptt.egg-info/PKG-INFO b/django_mptt.egg-info/PKG-INFO
index bed1bcd..77d7b08 100644
--- a/django_mptt.egg-info/PKG-INFO
+++ b/django_mptt.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: django-mptt
-Version: 0.7.4
+Version: 0.8.0
 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
@@ -17,7 +17,6 @@ Classifier: License :: OSI Approved :: MIT License
 Classifier: Operating System :: OS Independent
 Classifier: Programming Language :: Python
 Classifier: Programming Language :: Python :: 2
-Classifier: Programming Language :: Python :: 2.6
 Classifier: Programming Language :: Python :: 2.7
 Classifier: Programming Language :: Python :: 3
 Classifier: Programming Language :: Python :: 3.2
diff --git a/django_mptt.egg-info/SOURCES.txt b/django_mptt.egg-info/SOURCES.txt
index 01c356d..d366748 100644
--- a/django_mptt.egg-info/SOURCES.txt
+++ b/django_mptt.egg-info/SOURCES.txt
@@ -24,6 +24,7 @@ docs/mptt.fields.rst
 docs/mptt.forms.rst
 docs/mptt.managers.rst
 docs/mptt.models.rst
+docs/mptt.querysets.rst
 docs/mptt.rst
 docs/mptt.utils.rst
 docs/overview.rst
@@ -41,6 +42,7 @@ mptt/managers.py
 mptt/models.py
 mptt/querysets.py
 mptt/settings.py
+mptt/signals.py
 mptt/utils.py
 mptt/locale/de/LC_MESSAGES/django.mo
 mptt/locale/de/LC_MESSAGES/django.po
@@ -48,6 +50,8 @@ mptt/locale/dk/LC_MESSAGES/django.mo
 mptt/locale/dk/LC_MESSAGES/django.po
 mptt/locale/es/LC_MESSAGES/django.mo
 mptt/locale/es/LC_MESSAGES/django.po
+mptt/locale/es_AR/LC_MESSAGES/django.mo
+mptt/locale/es_AR/LC_MESSAGES/django.po
 mptt/locale/fr/LC_MESSAGES/django.mo
 mptt/locale/fr/LC_MESSAGES/django.po
 mptt/locale/mn/LC_MESSAGES/django.mo
@@ -71,13 +75,17 @@ tests/.coveragerc
 tests/.gitignore
 tests/__init__.py
 tests/__init__.pyc
+tests/mydatabase
+tests/requirements.txt
 tests/runtests.sh
 tests/settings.py
 tests/settings.pyc
 tests/__pycache__/__init__.cpython-32.pyc
 tests/__pycache__/__init__.cpython-33.pyc
+tests/__pycache__/__init__.cpython-34.pyc
 tests/__pycache__/settings.cpython-32.pyc
 tests/__pycache__/settings.cpython-33.pyc
+tests/__pycache__/settings.cpython-34.pyc
 tests/myapp/__init__.py
 tests/myapp/__init__.pyc
 tests/myapp/admin.py
@@ -91,10 +99,15 @@ tests/myapp/urls.py
 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__/admin.cpython-34.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__/tests.cpython-32.pyc
 tests/myapp/__pycache__/tests.cpython-33.pyc
+tests/myapp/__pycache__/tests.cpython-34.pyc
+tests/myapp/__pycache__/urls.cpython-34.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 a8bacab..363a653 100644
--- a/django_mptt.egg-info/requires.txt
+++ b/django_mptt.egg-info/requires.txt
@@ -1 +1 @@
-Django>=1.4.2
\ No newline at end of file
+Django>=1.8
\ No newline at end of file
diff --git a/docs/admin.rst b/docs/admin.rst
index 4d1a1b4..a4b003f 100644
--- a/docs/admin.rst
+++ b/docs/admin.rst
@@ -29,8 +29,8 @@ attribute in your MPTTModelAdmin::
     from mptt.admin import MPTTModelAdmin
     from myproject.myapp.models import Node
 
-    class CustomMPTTModelAdmin(MPTTModelAdmin)
-        # speficfy pixel amount for this ModelAdmin only:
+    class CustomMPTTModelAdmin(MPTTModelAdmin):
+        # specify pixel amount for this ModelAdmin only:
         mptt_level_indent = 20
 
     admin.site.register(Node, CustomMPTTModelAdmin)
@@ -39,6 +39,6 @@ If you'd like to specify which field should be indented, add an ``mptt_indent_fi
 to your MPTTModelAdmin::
 
     # …
-    class CustomMPTTModelAdmin(MPTTModelAdmin)
+    class CustomMPTTModelAdmin(MPTTModelAdmin):
         mptt_indent_field = "some_node_field"
     # …
diff --git a/docs/autogenerated.rst b/docs/autogenerated.rst
index a8b742c..adfaa55 100644
--- a/docs/autogenerated.rst
+++ b/docs/autogenerated.rst
@@ -8,7 +8,7 @@ They're not necessarily very helpful. You might be just as well off reading the
 
 .. toctree::
    :maxdepth: 3
-   
+
    mptt
 
 .. _`source code`: http://github.com/django-mptt/django-mptt/
diff --git a/docs/index.rst b/docs/index.rst
index 8362837..079bec0 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -8,7 +8,7 @@ you up and running:
 
 .. toctree::
    :maxdepth: 2
-   
+
    overview
    install
    tutorial
@@ -28,5 +28,5 @@ Topic docs:
 
 .. toctree::
    :maxdepth: 3
-   
+
    autogenerated
diff --git a/docs/models.rst b/docs/models.rst
index f490a75..358edbb 100644
--- a/docs/models.rst
+++ b/docs/models.rst
@@ -255,7 +255,8 @@ otherwise.
 -------------------------------------------
 
 Moves the model instance elsewhere in the tree based on ``target`` and
-``position`` (when appropriate).
+``position`` (when appropriate). If moved without any exceptions
+raised then the signal ``node_moved`` will be sent.
 
 .. note::
    It is assumed that when you call this method, the tree fields in the
diff --git a/docs/mptt.querysets.rst b/docs/mptt.querysets.rst
new file mode 100644
index 0000000..bb67d6d
--- /dev/null
+++ b/docs/mptt.querysets.rst
@@ -0,0 +1,7 @@
+==================
+``mptt.querysets``
+==================
+
+.. automodule:: mptt.querysets
+    :members:
+    :undoc-members:
diff --git a/docs/mptt.rst b/docs/mptt.rst
index aec3088..0768f24 100644
--- a/docs/mptt.rst
+++ b/docs/mptt.rst
@@ -4,13 +4,14 @@
 
 ..  toctree::
     :maxdepth: 1
-    
+
     mptt.admin
     mptt.exceptions
     mptt.fields
     mptt.forms
     mptt.managers
     mptt.models
+    mptt.querysets
     mptt.utils
 
 .. automodule:: mptt
diff --git a/docs/upgrade.rst b/docs/upgrade.rst
index 3c94758..bbaf69f 100644
--- a/docs/upgrade.rst
+++ b/docs/upgrade.rst
@@ -2,6 +2,18 @@
 Upgrade notes
 =============
 
+0.8.0
+===================
+
+Dropped support for old Django versions and Python 2.6
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Unsupported versions of django (1.4, 1.5, 1.6, 1.7) are no longer supported, and Python 2.6 is no longer supported.
+
+These versions of python/django no longer receive security patches. You should upgrade to Python 2.7 and Django 1.8+.
+
+Django 1.9 support has been added.
+
 0.7.0
 =====
 
diff --git a/docs/utilities.rst b/docs/utilities.rst
index e9c3c59..734d432 100644
--- a/docs/utilities.rst
+++ b/docs/utilities.rst
@@ -92,3 +92,29 @@ Optional arguments
 ``cumulative``
    If ``True``, the count will be for items related to the child
    node *and* all of its descendants. Defaults to ``False``.
+
+``get_cached_trees()``
+-----------------------------
+
+Takes a list/queryset of model objects in MPTT left (depth-first) order and
+caches the children and parent on each node. This allows up and down traversal
+through the tree without the need for further queries. Use cases include using
+a recursively included template or arbitrarily traversing trees.
+
+Returns a list of top-level nodes. If a single tree was provided in its
+entirety, the list will of course consist of just the tree's root node.
+
+Aliases to this function are also available:
+
+``mptt.templatetags.mptt_tag.cache_tree_children``
+   Use for recursive rendering in templates.
+
+``mptt.querysets.TreeQuerySet.get_cached_trees``
+   Useful for chaining with queries; e.g.,
+   `Node.objects.filter(**kwargs).get_cached_trees()`
+
+Required arguments
+~~~~~~~~~~~~~~~~~~
+
+``queryset``
+   An iterable that consists of all nodes which are to be cached.
diff --git a/mptt/__init__.py b/mptt/__init__.py
index c7d5b76..87ec9ae 100644
--- a/mptt/__init__.py
+++ b/mptt/__init__.py
@@ -1,6 +1,6 @@
 from __future__ import unicode_literals
 
-VERSION = (0, 7, 4)
+VERSION = (0, 8, 0)
 
 
 def register(*args, **kwargs):
diff --git a/mptt/admin.py b/mptt/admin.py
index 88bb8b3..f90413a 100644
--- a/mptt/admin.py
+++ b/mptt/admin.py
@@ -1,13 +1,9 @@
 from __future__ import unicode_literals
 
-import django
 from django.conf import settings
 from django.contrib.admin.actions import delete_selected
 from django.contrib.admin.options import ModelAdmin
-try:
-    from django.utils.encoding import force_text
-except ImportError:  # pragma: no cover (Django 1.4 compatibility)
-    from django.utils.encoding import force_unicode as force_text
+from django.utils.encoding import force_text
 from django.utils.translation import ugettext as _
 
 from mptt.forms import MPTTAdminForm, TreeNodeChoiceField
@@ -37,16 +33,11 @@ class MPTTModelAdmin(ModelAdmin):
                 and db_field.name not in self.raw_id_fields:
             db = kwargs.get('using')
 
-            if django.VERSION >= (1, 7):
-                # This resolves callable values for db_field.rel.limit_choices_to,
-                # but isn't available/required on django < 1.7
-                limit_choices_to = db_field.get_limit_choices_to()
-            else:
-                limit_choices_to = db_field.rel.limit_choices_to
-
+            limit_choices_to = db_field.get_limit_choices_to()
             defaults = dict(
                 form_class=TreeNodeChoiceField,
-                queryset=db_field.rel.to._default_manager.using(db).complex_filter(limit_choices_to),
+                queryset=db_field.rel.to._default_manager.using(
+                    db).complex_filter(limit_choices_to),
                 required=False)
             defaults.update(kwargs)
             kwargs = defaults
@@ -69,7 +60,7 @@ class MPTTModelAdmin(ModelAdmin):
         # If this is True, the confirmation page has been displayed
         if request.POST.get('post'):
             n = 0
-            with queryset.model.objects.delay_mptt_updates():
+            with queryset.model._tree_manager.delay_mptt_updates():
                 for obj in queryset:
                     if self.has_delete_permission(request, obj):
                         obj.delete()
diff --git a/mptt/fields.py b/mptt/fields.py
index be13371..69b809a 100644
--- a/mptt/fields.py
+++ b/mptt/fields.py
@@ -6,6 +6,7 @@ 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
 
 
@@ -38,10 +39,8 @@ class TreeManyToManyField(models.ManyToManyField):
         return super(TreeManyToManyField, self).formfield(**kwargs)
 
 # South integration
-try:  # pragma: no cover
+if 'south' in settings.INSTALLED_APPS:  # pragma: no cover
     from south.modelsinspector import add_introspection_rules
     add_introspection_rules([], ["^mptt\.fields\.TreeForeignKey"])
     add_introspection_rules([], ["^mptt\.fields\.TreeOneToOneField"])
     add_introspection_rules([], ["^mptt\.fields\.TreeManyToManyField"])
-except ImportError:
-    pass
diff --git a/mptt/forms.py b/mptt/forms.py
index 4af2607..ecbf421 100644
--- a/mptt/forms.py
+++ b/mptt/forms.py
@@ -4,10 +4,7 @@ Form components for working with trees.
 from __future__ import unicode_literals
 from django import forms
 from django.forms.forms import NON_FIELD_ERRORS
-try:
-    from django.utils.encoding import smart_text
-except ImportError:  # pragma: no cover (Django 1.4 compatibility)
-    from django.utils.encoding import smart_unicode as smart_text
+from django.utils.encoding import smart_text
 from django.utils.html import conditional_escape, mark_safe
 from django.utils.translation import ugettext_lazy as _
 
diff --git a/mptt/locale/es_AR/LC_MESSAGES/django.mo b/mptt/locale/es_AR/LC_MESSAGES/django.mo
new file mode 100644
index 0000000..7ea77f2
Binary files /dev/null and b/mptt/locale/es_AR/LC_MESSAGES/django.mo differ
diff --git a/mptt/locale/es_AR/LC_MESSAGES/django.po b/mptt/locale/es_AR/LC_MESSAGES/django.po
new file mode 100644
index 0000000..b1352fa
--- /dev/null
+++ b/mptt/locale/es_AR/LC_MESSAGES/django.po
@@ -0,0 +1,126 @@
+# SPANISH (ARGENTINA) TRANSLATION
+# This file is distributed under the same license as the PACKAGE django-mptt.
+# Gonzalo Bustos, 2015.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: \n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2014-08-29 12:31+0200\n"
+"PO-Revision-Date: 2015-10-11 21:39-0300\n"
+"Last-Translator: Gonzalo Bustos\n"
+"Language-Team: Spanish (Argentina)\n"
+"Language: es_AR\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"
+"X-Generator: Poedit 1.6.10\n"
+
+#: __init__.py:34
+#, python-format
+msgid "The model %s has already been registered."
+msgstr "El modelo %s ya ha sido registrado."
+
+#: forms.py:41
+msgid "First child"
+msgstr "Primer hijo"
+
+#: forms.py:42
+msgid "Last child"
+msgstr "Último hijo"
+
+#: forms.py:43
+msgid "Left sibling"
+msgstr "Hermano izquierdo"
+
+#: forms.py:44
+msgid "Right sibling"
+msgstr "Hermano derecho"
+
+#: managers.py:121
+msgid "Cannot insert a node which has already been saved."
+msgstr "No se puede insertar un nodo que ya ha sido guardado."
+
+#: managers.py:306 managers.py:480 managers.py:516 managers.py:673
+#, python-format
+msgid "An invalid position was given: %s."
+msgstr "Posición inválida: %s."
+
+#: managers.py:466 managers.py:653
+msgid "A node may not be made a sibling of itself."
+msgstr "Un nodo no puede ser hermano de sí mismo."
+
+#: managers.py:632 managers.py:753
+msgid "A node may not be made a child of itself."
+msgstr "Un nodo no puede ser hijo de sí mismo."
+
+#: managers.py:634 managers.py:755
+msgid "A node may not be made a child of any of its descendants."
+msgstr "Un nodo no puede ser hijo de alguno de sus descendientes."
+
+#: managers.py:655
+msgid "A node may not be made a sibling of any of its descendants."
+msgstr "Un nodo no puede ser hermano de alguno de sus descendientes."
+
+#: templatetags/mptt_tags.py:23
+#, python-format
+msgid "full_tree_for_model tag was given an invalid model: %s"
+msgstr "Modelo inválido para tag full_tree_for_model: %s"
+
+#: templatetags/mptt_tags.py:44
+#, python-format
+msgid "drilldown_tree_for_node tag was given an invalid model: %s"
+msgstr "Modelo inválido para tag drilldown_tree_for_node: %s"
+
+#: templatetags/mptt_tags.py:48
+#, python-format
+msgid "drilldown_tree_for_node tag was given an invalid model field: %s"
+msgstr "Campo de modelo inválido para tag drilldown_tree_for_node: %s"
+
+#: templatetags/mptt_tags.py:72
+#, python-format
+msgid "%s tag requires three arguments"
+msgstr "El tag %s require tres argumentos"
+
+#: templatetags/mptt_tags.py:74 templatetags/mptt_tags.py:125
+#, python-format
+msgid "second argument to %s tag must be 'as'"
+msgstr "El segundo argumento para el tag %s debe ser 'as'"
+
+#: templatetags/mptt_tags.py:123
+#, python-format
+msgid "%s tag requires either three, seven or eight arguments"
+msgstr "El tag %s requiere tres, siete u ocho argumentos"
+
+#: templatetags/mptt_tags.py:128
+#, python-format
+msgid "if seven arguments are given, fourth argument to %s tag must be 'with'"
+msgstr ""
+"Si se proporcionan siete argumentos, el cuarto para el tag %s debe ser 'with'"
+
+#: templatetags/mptt_tags.py:130
+#, python-format
+msgid "if seven arguments are given, sixth argument to %s tag must be 'in'"
+msgstr ""
+"Si se proporcionan siete argumentos, el sexto para el tag %s debe ser 'in'"
+
+#: templatetags/mptt_tags.py:134
+#, python-format
+msgid ""
+"if eight arguments are given, fourth argument to %s tag must be 'cumulative'"
+msgstr ""
+"Si se proporcionan ocho argumentos, el cuarto para el tag %s debe ser "
+"'cumulative'"
+
+#: templatetags/mptt_tags.py:136
+#, python-format
+msgid "if eight arguments are given, fifth argument to %s tag must be 'count'"
+msgstr ""
+"Si se proporcionan ocho argumentos, el quinto para el tag %s debe ser 'count'"
+
+#: templatetags/mptt_tags.py:138
+#, python-format
+msgid "if eight arguments are given, seventh argument to %s tag must be 'in'"
+msgstr ""
+"Si se proporcionan ocho argumentos, el séptimo para el tag %s debe ser 'in'"
diff --git a/mptt/managers.py b/mptt/managers.py
index 4841ead..bac96ad 100644
--- a/mptt/managers.py
+++ b/mptt/managers.py
@@ -13,12 +13,8 @@ from django.utils.translation import ugettext as _
 from mptt.exceptions import CantDisableUpdates, InvalidMove
 from mptt.querysets import TreeQuerySet
 from mptt.utils import _get_tree_model
+from mptt.signals import node_moved
 
-if django.VERSION < (1, 7):
-    _tree_manager_superclass = models.Manager
-else:
-    # Django 1.7+ added this crazy new pattern for manager inheritance.
-    _tree_manager_superclass = models.Manager.from_queryset(TreeQuerySet)
 
 __all__ = ('TreeManager',)
 
@@ -64,7 +60,7 @@ CUMULATIVE_COUNT_SUBQUERY_M2M = """(
 )"""
 
 
-class TreeManager(_tree_manager_superclass):
+class TreeManager(models.Manager.from_queryset(TreeQuerySet)):
     """
     A manager for working with trees of objects.
     """
@@ -80,14 +76,6 @@ class TreeManager(_tree_manager_superclass):
                 # _base_manager is the treemanager on tree_model
                 self._base_manager = self.tree_model._tree_manager
 
-    def get_query_set(self, *args, **kwargs):
-        """
-        Ensures that this manager always returns nodes in tree order.
-
-        This method can be removed when support for Django < 1.6 is dropped.
-        """
-        return TreeQuerySet(self.model, using=self._db).order_by(self.tree_id_attr, self.left_attr)
-
     def get_queryset(self, *args, **kwargs):
         """
         Ensures that this manager always returns nodes in tree order.
@@ -139,9 +127,6 @@ class TreeManager(_tree_manager_superclass):
 
         opts = queryset.model._mptt_meta
 
-        if not queryset:
-            return self.none()
-
         filters = Q()
 
         e = 'e' if include_self else ''
@@ -158,9 +143,26 @@ class TreeManager(_tree_manager_superclass):
         min_key = '%s__%s' % (min_attr, min_op)
         max_key = '%s__%s' % (max_attr, max_op)
 
-        q = queryset.order_by(opts.tree_id_attr, opts.parent_attr, opts.left_attr)
+        q = queryset.order_by(opts.tree_id_attr, opts.parent_attr, opts.left_attr).only(
+            opts.tree_id_attr,
+            opts.left_attr,
+            opts.right_attr,
+            min_attr,
+            max_attr,
+            opts.parent_attr,
+            # These fields are used by MPTTModel.update_mptt_cached_fields()
+            *opts.order_insertion_by
+        )
 
-        for group in groupby(q, key = lambda n: (getattr(n, opts.tree_id_attr), getattr(n, opts.parent_attr))):
+        if not q:
+            return self.none()
+
+        for group in groupby(
+                q,
+                key=lambda n: (
+                    getattr(n, opts.tree_id_attr),
+                    getattr(n, opts.parent_attr + '_id'),
+                )):
             next_lft = None
             for node in list(group[1]):
                 tree, lft, rght, min_val, max_val = (getattr(node, opts.tree_id_attr),
@@ -170,7 +172,7 @@ class TreeManager(_tree_manager_superclass):
                                                      getattr(node, max_attr))
                 if next_lft is None:
                     next_lft = rght + 1
-                    min_max = {'min': min_val, 'max': max_val} 
+                    min_max = {'min': min_val, 'max': max_val}
                 elif lft == next_lft:
                     if min_val < min_max['min']:
                         min_max['min'] = min_val
@@ -178,10 +180,18 @@ class TreeManager(_tree_manager_superclass):
                         min_max['max'] = max_val
                     next_lft = rght + 1
                 elif lft != next_lft:
-                    filters |= Q(**{tree_key: tree, min_key: min_max['min'], max_key: min_max['max']})
+                    filters |= Q(**{
+                        tree_key: tree,
+                        min_key: min_max['min'],
+                        max_key: min_max['max'],
+                    })
                     min_max = {'min': min_val, 'max': max_val}
                     next_lft = rght + 1
-            filters |= Q(**{tree_key: tree, min_key: min_max['min'], max_key: min_max['max']})
+            filters |= Q(**{
+                tree_key: tree,
+                min_key: min_max['min'],
+                max_key: min_max['max'],
+            })
 
         return self.filter(filters)
 
@@ -559,7 +569,8 @@ class TreeManager(_tree_manager_superclass):
 
     def _move_node(self, node, target, position='last-child', save=True, refresh_target=True):
         if self._base_manager:
-            return self._base_manager.move_node(node, target, position=position)
+            return self._base_manager._move_node(node, target, position=position,
+                                                 save=save, refresh_target=refresh_target)
 
         if self.tree_model._mptt_is_tracking:
             # delegate to insert_node and clean up the gaps later.
@@ -601,6 +612,8 @@ class TreeManager(_tree_manager_superclass):
         move the node yourself by setting node.parent.
         """
         self._move_node(node, target, position=position)
+        node_moved.send(sender=node.__class__, instance=node,
+                        target=target, position=position)
 
     def root_node(self, tree_id):
         """
diff --git a/mptt/models.py b/mptt/models.py
index 6705771..0d9b8b0 100644
--- a/mptt/models.py
+++ b/mptt/models.py
@@ -2,17 +2,18 @@ from __future__ import unicode_literals
 from functools import reduce, wraps
 import operator
 import threading
-import django
 
 from django.db import models
 from django.db.models.base import ModelBase
 from django.db.models.query import Q
+from django.db.models.query_utils import DeferredAttribute
 
 from django.utils import six
 from django.utils.translation import ugettext as _
 
 from mptt.fields import TreeForeignKey, TreeOneToOneField, TreeManyToManyField
 from mptt.managers import TreeManager
+from mptt.signals import node_moved
 from mptt.utils import _get_tree_model
 
 
@@ -120,18 +121,28 @@ class MPTTOptions(object):
          - parent pk
          - fields specified in order_insertion_by
 
-        These are used in pre_save to determine if the relevant fields have changed,
+        These are used in save() to determine if the relevant fields have changed,
         so that the MPTT fields need to be updated.
         """
         instance._mptt_cached_fields = {}
         field_names = set((self.parent_attr,))
-        field_names__add = field_names.add
         if self.order_insertion_by:
             for f in self.order_insertion_by:
                 if f[0] == '-':
                     f = f[1:]
-                field_names__add(f)
+                field_names.add(f)
         for field_name in field_names:
+            if instance._deferred:
+                field = instance._meta.get_field(field_name)
+                if field.attname in instance.get_deferred_fields() \
+                        and field.attname not in instance.__dict__:
+                    # deferred attribute (i.e. via .only() or .defer())
+                    # It'd be silly to cache this (that'd do a database query)
+                    # Instead, we mark it as a deferred attribute here, then
+                    # assume it hasn't changed during save(), unless it's no
+                    # longer deferred.
+                    instance._mptt_cached_fields[field_name] = DeferredAttribute
+                    continue
             instance._mptt_cached_fields[field_name] = self.get_raw_field_value(
                 instance, field_name)
 
@@ -223,9 +234,7 @@ class MPTTModelBase(ModelBase):
          - adds a TreeManager to the model
         """
         if class_name == 'NewBase' and class_dict == {}:
-            # skip ModelBase, on django < 1.5 it doesn't handle NewBase.
-            super_new = super(ModelBase, meta).__new__
-            return super_new(meta, class_name, bases, class_dict)
+            return super(MPTTModelBase, meta).__new__(meta, class_name, bases, class_dict)
         is_MPTTModel = False
         try:
             MPTTModel
@@ -336,27 +345,14 @@ class MPTTModelBase(ModelBase):
 
                 # make sure we have a tree manager somewhere
                 tree_manager = None
-                attrs = dir(cls)
-                if "objects" in attrs and isinstance(cls.objects, TreeManager):
-                    tree_manager = cls.objects
-                
-                # Go look for it somewhere else
-                else:
-                    for attr in sorted(attrs):
-                        try:
-                            # HACK: avoid using getattr(cls, attr)
-                            # because it calls __get__ on descriptors, which can cause nasty side effects
-                            # with more inconsiderate apps.
-                            # (e.g. django-tagging's TagField is a descriptor which can do db queries on getattr)
-                            # ( ref: http://stackoverflow.com/questions/27790344 )
-                            obj = cls.__dict__[attr]
-                        except KeyError:
-                            continue
-                        if isinstance(obj, TreeManager):
-                            tree_manager = obj
-                            # prefer any locally defined manager (i.e. keep going if not local)
-                            if obj.model is cls:
-                                break
+                cls_managers = cls._meta.concrete_managers + cls._meta.abstract_managers
+                for __, __, cls_manager in cls_managers:
+                    if isinstance(cls_manager, TreeManager):
+                        # prefer any locally defined manager (i.e. keep going if not local)
+                        if cls_manager.model is cls:
+                            tree_manager = cls_manager
+                            break
+
                 if tree_manager and tree_manager.model is not cls:
                     tree_manager = tree_manager._copy_to_model(cls)
                 elif tree_manager is None:
@@ -708,13 +704,15 @@ class MPTTModel(six.with_metaclass(MPTTModelBase, models.Model)):
         """
         return getattr(self, self._mptt_meta.level_attr)
 
-    def insert_at(self, target, position='first-child', save=False, allow_existing_pk=False, refresh_target=True):
+    def insert_at(self, target, position='first-child', save=False,
+                  allow_existing_pk=False, refresh_target=True):
         """
         Convenience method for calling ``TreeManager.insert_node`` with this
         model instance.
         """
         self._tree_manager.insert_node(
-            self, target, position, save, allow_existing_pk=allow_existing_pk, refresh_target=refresh_target)
+            self, target, position, save, allow_existing_pk=allow_existing_pk,
+            refresh_target=refresh_target)
 
     def is_child_node(self):
         """
@@ -798,10 +796,11 @@ class MPTTModel(six.with_metaclass(MPTTModelBase, models.Model)):
         from django.db.models.fields import AutoField
 
         field_names = []
-        internal_fields = (self._mptt_meta.left_attr, self._mptt_meta.right_attr, self._mptt_meta.tree_id_attr,
-                           self._mptt_meta.level_attr, self._mptt_meta.parent_attr)
+        internal_fields = (
+            self._mptt_meta.left_attr, self._mptt_meta.right_attr, self._mptt_meta.tree_id_attr,
+            self._mptt_meta.level_attr, self._mptt_meta.parent_attr)
         for field in self._meta.fields:
-            if (field.name not in internal_fields) and (not isinstance(field, AutoField)) and (not field.primary_key):
+            if (field.name not in internal_fields) and (not isinstance(field, AutoField)) and (not field.primary_key):  # noqa
                 field_names.append(field.name)
         return field_names
 
@@ -856,14 +855,21 @@ class MPTTModel(six.with_metaclass(MPTTModelBase, models.Model)):
         force_update = kwargs.get('force_update', False)
         force_insert = kwargs.get('force_insert', False)
         collapse_old_tree = None
+        deferred_fields = self.get_deferred_fields()
         if force_update or (not force_insert and self._is_saved(using=kwargs.get('using'))):
             # it already exists, so do a move
             old_parent_id = self._mptt_cached_fields[opts.parent_attr]
-            same_order = old_parent_id == parent_id
+            if old_parent_id is DeferredAttribute:
+                same_order = True
+            else:
+                same_order = old_parent_id == parent_id
+
             if same_order and len(self._mptt_cached_fields) > 1:
-                get_raw_field_value = opts.get_raw_field_value
                 for field_name, old_value in self._mptt_cached_fields.items():
-                    if old_value != get_raw_field_value(self, field_name):
+                    if old_value is DeferredAttribute and field_name not in deferred_fields:
+                        same_order = False
+                        break
+                    if old_value != opts.get_raw_field_value(self, field_name):
                         same_order = False
                         break
                 if not do_updates and not same_order:
@@ -901,7 +907,9 @@ class MPTTModel(six.with_metaclass(MPTTModelBase, models.Model)):
                             or getattr(self, opts.right_attr) > getattr(parent, opts.right_attr))
 
                     if right_sibling:
-                        self._tree_manager._move_node(self, right_sibling, 'left', save=False, refresh_target=False)
+                        self._tree_manager._move_node(
+                            self, right_sibling, 'left', save=False,
+                            refresh_target=False)
                     else:
                         # Default movement
                         if parent_id is None:
@@ -910,7 +918,8 @@ class MPTTModel(six.with_metaclass(MPTTModelBase, models.Model)):
                                 rightmost_sibling = root_nodes.exclude(
                                     pk=self.pk).order_by('-' + opts.tree_id_attr)[0]
                                 self._tree_manager._move_node(
-                                    self, rightmost_sibling, 'right', save=False, refresh_target=False)
+                                    self, rightmost_sibling, 'right', save=False,
+                                    refresh_target=False)
                             except IndexError:
                                 pass
                         else:
@@ -926,15 +935,22 @@ class MPTTModel(six.with_metaclass(MPTTModelBase, models.Model)):
                     # Make sure the new parent is always
                     # restored on the way out in case of errors.
                     opts.set_raw_field_value(self, opts.parent_attr, parent_id)
+
+                # If there were no exceptions raised then send a moved signal
+                node_moved.send(sender=self.__class__, instance=self,
+                                target=getattr(self, opts.parent_attr))
             else:
                 opts.set_raw_field_value(self, opts.parent_attr, parent_id)
-                if (not track_updates) and (django.get_version() >= '1.5'):
+                if not track_updates:
                     # When not using delayed/disabled updates,
-                    # populate update_fields (Django 1.5 and later) with user defined model fields.
-                    # This helps preserve tree integrity when saving model on top of a modified tree.
+                    # populate update_fields with user defined model fields.
+                    # This helps preserve tree integrity when saving model on top
+                    # of a modified tree.
                     if len(args) > 3:
                         if not args[3]:
+                            args = list(args)
                             args[3] = self._get_user_field_names()
+                            args = tuple(args)
                     else:
                         if not kwargs.get("update_fields", None):
                             kwargs["update_fields"] = self._get_user_field_names()
@@ -958,7 +974,8 @@ class MPTTModel(six.with_metaclass(MPTTModelBase, models.Model)):
                         right_sibling = opts.get_ordered_insertion_target(self, parent)
 
                 if right_sibling:
-                    self.insert_at(right_sibling, 'left', allow_existing_pk=True, refresh_target=False)
+                    self.insert_at(right_sibling, 'left', allow_existing_pk=True,
+                                   refresh_target=False)
 
                     if parent:
                         # since we didn't insert into parent, we have to update parent.rght
diff --git a/mptt/querysets.py b/mptt/querysets.py
index cbebb70..dcad16a 100644
--- a/mptt/querysets.py
+++ b/mptt/querysets.py
@@ -1,10 +1,25 @@
 from django.db import models
 
+from mptt import utils
+
+
 class TreeQuerySet(models.query.QuerySet):
     def get_descendants(self, *args, **kwargs):
-        return self.model.objects.get_queryset_descendants(self, *args, **kwargs)
+        """
+        Alias to `mptt.managers.TreeManager.get_queryset_descendants`.
+        """
+        return self.model._tree_manager.get_queryset_descendants(self, *args, **kwargs)
     get_descendants.queryset_only = True
 
     def get_ancestors(self, *args, **kwargs):
-        return self.model.objects.get_queryset_ancestors(self, *args, **kwargs)
+        """
+        Alias to `mptt.managers.TreeManager.get_queryset_ancestors`.
+        """
+        return self.model._tree_manager.get_queryset_ancestors(self, *args, **kwargs)
     get_ancestors.queryset_only = True
+
+    def get_cached_trees(self):
+        """
+        Alias to `mptt.utils.get_cached_trees`.
+        """
+        return utils.get_cached_trees(self)
diff --git a/mptt/signals.py b/mptt/signals.py
new file mode 100644
index 0000000..e24e460
--- /dev/null
+++ b/mptt/signals.py
@@ -0,0 +1,11 @@
+import django.dispatch
+
+# Behaves like Djangos normal pre-/post_save signals signals with the
+# added arguments ``target`` and ``position`` that matches those of
+# ``move_to``.
+# If the signal is called from ``save`` it'll not be pass position.
+node_moved = django.dispatch.Signal(providing_args=[
+    'instance',
+    'target',
+    'position'
+])
diff --git a/mptt/templates/admin/mptt_change_list_results.html b/mptt/templates/admin/mptt_change_list_results.html
index d7336e8..ebf8d2b 100644
--- a/mptt/templates/admin/mptt_change_list_results.html
+++ b/mptt/templates/admin/mptt_change_list_results.html
@@ -8,9 +8,12 @@
 <table cellspacing="0" id="result_list">
 <thead>
 <tr>
-{% for header in result_headers %}<th scope="col"{{ header.class_attrib }}>
-{{ header.text|capfirst }}
-</th>{% endfor %}
+    {% for header in result_headers %}
+        <th scope="col"{{ header.class_attrib }}>
+            <div class="text"><span>{{ header.text|capfirst }}</span></div>
+            <div class="clear"></div>
+        </th>
+    {% endfor %}
 </tr>
 </thead>
 <tbody>
diff --git a/mptt/templatetags/mptt_admin.py b/mptt/templatetags/mptt_admin.py
index a771806..e5805c1 100644
--- a/mptt/templatetags/mptt_admin.py
+++ b/mptt/templatetags/mptt_admin.py
@@ -3,21 +3,14 @@ from __future__ import unicode_literals
 from django.conf import settings
... 1494 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