[Python-modules-commits] [python-django-mptt] 02/06: Import python-django-mptt_0.7.4.orig.tar.gz

Brian May bam at moszumanska.debian.org
Mon Nov 30 08:56:12 UTC 2015


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 bf303811ce7a4d87ff6b296ac33e0679673ad72a
Author: Brian May <bam at debian.org>
Date:   Mon Nov 30 19:30:28 2015 +1100

    Import python-django-mptt_0.7.4.orig.tar.gz
---
 PKG-INFO                      |   2 +-
 django_mptt.egg-info/PKG-INFO |   2 +-
 mptt/__init__.py              |   2 +-
 mptt/managers.py              |  83 ++++++++++++++++++++++++++++++------------
 mptt/querysets.py             |   2 +
 tests/myapp/models.py         |   2 +-
 tests/myapp/models.pyc        | Bin 16585 -> 17761 bytes
 tests/myapp/tests.py          |  40 +++++++++++++-------
 tests/myapp/tests.pyc         | Bin 71165 -> 72578 bytes
 9 files changed, 91 insertions(+), 42 deletions(-)

diff --git a/PKG-INFO b/PKG-INFO
index 7316baa..bed1bcd 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: django-mptt
-Version: 0.7.3
+Version: 0.7.4
 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
diff --git a/django_mptt.egg-info/PKG-INFO b/django_mptt.egg-info/PKG-INFO
index 7316baa..bed1bcd 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.3
+Version: 0.7.4
 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
diff --git a/mptt/__init__.py b/mptt/__init__.py
index 7327dbc..c7d5b76 100644
--- a/mptt/__init__.py
+++ b/mptt/__init__.py
@@ -1,6 +1,6 @@
 from __future__ import unicode_literals
 
-VERSION = (0, 7, 3)
+VERSION = (0, 7, 4)
 
 
 def register(*args, **kwargs):
diff --git a/mptt/managers.py b/mptt/managers.py
index 263b068..4841ead 100644
--- a/mptt/managers.py
+++ b/mptt/managers.py
@@ -5,6 +5,7 @@ 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 _
@@ -13,6 +14,12 @@ from mptt.exceptions import CantDisableUpdates, InvalidMove
 from mptt.querysets import TreeQuerySet
 from mptt.utils import _get_tree_model
 
+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',)
 
 
@@ -57,7 +64,7 @@ CUMULATIVE_COUNT_SUBQUERY_M2M = """(
 )"""
 
 
-class TreeManager(models.Manager):
+class TreeManager(_tree_manager_superclass):
     """
     A manager for working with trees of objects.
     """
@@ -85,7 +92,11 @@ class TreeManager(models.Manager):
         """
         Ensures that this manager always returns nodes in tree order.
         """
-        return TreeQuerySet(self.model, using=self._db).order_by(self.tree_id_attr, self.left_attr)
+        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)
 
     def _get_queryset_relatives(self, queryset, direction, include_self):
         """
@@ -99,10 +110,30 @@ class TreeManager(models.Manager):
         Instead, it should be used via ``get_queryset_descendants()`` and/or
         ``get_queryset_ancestors()``.
 
-        This function works by grouping contiguous siblings, finding the smallest
-        left and greatest right attributes in the group, and using those to create
-        a query that requests ranges of models rather than querying for each
-        individual model. The net result is a simpler query and fewer SQL variables.
+        This function works by grouping contiguous siblings and using them to create
+        a range that selects all nodes between the range, instead of querying for each
+        node individually. Three variables are required when querying for ancestors or
+        descendants: tree_id_attr, left_attr, right_attr. If we weren't using ranges
+        and our queryset contained 100 results, the resulting SQL query would contain
+        300 variables. However, when using ranges, if the same queryset contained 10
+        sets of contiguous siblings, then the resulting SQL query should only contain
+        30 variables.
+
+        The attributes used to create the range are completely
+        dependent upon whether you are ascending or descending the tree.
+
+        * Ascending (ancestor nodes): select all nodes whose right_attr is greater
+          than (or equal to, if include_self = True) the smallest right_attr within
+          the set of contiguous siblings, and whose left_attr is less than (or equal
+          to) the largest left_attr within the set of contiguous siblings.
+
+        * Descending (descendant nodes): select all nodes whose left_attr is greater
+          than (or equal to, if include_self = True) the smallest left_attr within
+          the set of contiguous siblings, and whose right_attr is less than (or equal
+          to) the largest right_attr within the set of contiguous siblings.
+
+        The result is the more contiguous siblings in the original queryset, the fewer
+        SQL variables will be required to execute the query.
         """
         assert self.model is queryset.model
 
@@ -114,39 +145,43 @@ class TreeManager(models.Manager):
         filters = Q()
 
         e = 'e' if include_self else ''
+        max_op = 'lt' + e
+        min_op = 'gt' + e
         if direction == 'asc':
-            lft_op = 'lt' + e
-            rght_op = 'gt' + e
+            max_attr = opts.left_attr
+            min_attr = opts.right_attr
         elif direction == 'desc':
-            lft_op = 'gt' + e
-            rght_op = 'lt' + e
+            max_attr = opts.right_attr
+            min_attr = opts.left_attr
 
-        l_key = '%s__%s' % (opts.left_attr, lft_op)
-        r_key = '%s__%s' % (opts.right_attr, rght_op)
-        t_key = opts.tree_id_attr
+        tree_key = opts.tree_id_attr
+        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)
 
         for group in groupby(q, key = lambda n: (getattr(n, opts.tree_id_attr), getattr(n, opts.parent_attr))):
             next_lft = None
             for node in list(group[1]):
-                tree, lft, rght = (getattr(node, opts.tree_id_attr),
-                                   getattr(node, opts.left_attr),
-                                   getattr(node, opts.right_attr))
+                tree, lft, rght, min_val, max_val = (getattr(node, opts.tree_id_attr),
+                                                     getattr(node, opts.left_attr),
+                                                     getattr(node, opts.right_attr),
+                                                     getattr(node, min_attr),
+                                                     getattr(node, max_attr))
                 if next_lft is None:
                     next_lft = rght + 1
-                    minl_maxr = {'lft': lft, 'rght': rght}
+                    min_max = {'min': min_val, 'max': max_val} 
                 elif lft == next_lft:
-                    if lft < minl_maxr['lft']:
-                        minl_maxr['lft'] = lft
-                    if rght > minl_maxr['rght']:
-                        minl_maxr['rght'] = rght
+                    if min_val < min_max['min']:
+                        min_max['min'] = min_val
+                    if max_val > min_max['max']:
+                        min_max['max'] = max_val
                     next_lft = rght + 1
                 elif lft != next_lft:
-                    filters |= Q(**{t_key: tree, l_key: minl_maxr['lft'], r_key: minl_maxr['rght']})
-                    minl_maxr = {'lft': lft, 'rght': rght}
+                    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(**{t_key: tree, l_key: minl_maxr['lft'], r_key: minl_maxr['rght']})
+            filters |= Q(**{tree_key: tree, min_key: min_max['min'], max_key: min_max['max']})
 
         return self.filter(filters)
 
diff --git a/mptt/querysets.py b/mptt/querysets.py
index 110ce4c..cbebb70 100644
--- a/mptt/querysets.py
+++ b/mptt/querysets.py
@@ -3,6 +3,8 @@ from django.db import models
 class TreeQuerySet(models.query.QuerySet):
     def get_descendants(self, *args, **kwargs):
         return self.model.objects.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)
+    get_ancestors.queryset_only = True
diff --git a/tests/myapp/models.py b/tests/myapp/models.py
index f84084a..2f0f6ed 100644
--- a/tests/myapp/models.py
+++ b/tests/myapp/models.py
@@ -148,7 +148,7 @@ class Person(MPTTModel):
 
     # just testing it's actually possible to override the tree manager
     objects = CustomTreeManager()
-    
+
     # This line is set because of https://github.com/django-mptt/django-mptt/issues/369
     _default_manager = objects
 
diff --git a/tests/myapp/models.pyc b/tests/myapp/models.pyc
index 86927e8..925ca54 100644
Binary files a/tests/myapp/models.pyc and b/tests/myapp/models.pyc differ
diff --git a/tests/myapp/tests.py b/tests/myapp/tests.py
index d09cf62..d9c8a5b 100644
--- a/tests/myapp/tests.py
+++ b/tests/myapp/tests.py
@@ -15,7 +15,7 @@ else:
 
 import django
 from django.contrib.auth.models import Group, User
-from django.db.models import Q
+from django.db.models import Q, Manager
 try:
     from django.apps import apps
     get_models = apps.get_models
@@ -1206,50 +1206,62 @@ class ManagerTests(TreeTestCase):
              'Vertical Scrolling Shootemup']
         )
 
+    def _get_anc_names(self, qs, include_self=False):
+        anc = qs.model.objects.get_queryset_ancestors(
+            qs, include_self=include_self)
+        return list(anc.values_list('name', flat=True).order_by('name'))
 
     def test_get_queryset_ancestors(self):
-        def get_anc_names(qs, include_self=False):
-            anc = qs.model.objects.get_queryset_ancestors(
-                qs, include_self=include_self)
-            return list(anc.values_list('name', flat=True).order_by('name'))
-
         qs = Category.objects.filter(Q(name='Nintendo Wii')|Q(name='PlayStation 3'))
 
         self.assertEqual(
-            get_anc_names(qs),
+            self._get_anc_names(qs),
              ['PC & Video Games']
         )
 
         self.assertEqual(
-            get_anc_names(qs, include_self=True),
+            self._get_anc_names(qs, include_self=True),
             ['Nintendo Wii', 'PC & Video Games', 'PlayStation 3']
         )
 
         qs = Genre.objects.filter(parent=None)
+        self.assertEqual(self._get_anc_names(qs),[])
+        self.assertEqual(self._get_anc_names(qs, include_self=True), ['Action', 'Role-playing Game'])
 
-        self.assertEqual(get_anc_names(qs),[])
-
-        self.assertEqual(get_anc_names(qs, include_self=True), ['Action', 'Role-playing Game'])
+    def test_get_queryset_ancestors_regression_379(self):
+        # https://github.com/django-mptt/django-mptt/issues/379
+        qs = Genre.objects.all()
+        self.assertEqual(self._get_anc_names(qs, include_self=True), list(Genre.objects.values_list('name', flat=True).order_by('name')))
 
     def test_custom_querysets(self):
         """
         Test that a custom manager also provides custom querysets.
         """
-        
+
         self.assertTrue(isinstance(Person.objects.all(), CustomTreeQueryset))
         self.assertTrue(isinstance(Person.objects.all()[0].get_children(), CustomTreeQueryset))
         self.assertTrue(hasattr(Person.objects.none(), 'custom_method'))
-        
+
         # In Django 1.4, we would have had a custom type CustomEmptyTreeQueryset
         # but this was abandoned in later versions. However, the best method is
         # to just test if the custom method is available.
         # self.assertTrue(hasattr(Person.objects.all()[0].get_children().none(), 'custom_method'))
-        
+
         self.assertEqual(
             type(Person.objects.all()),
             type(Person.objects.root_nodes())
         )
 
+    @unittest.skipIf(django.VERSION < (1, 7), 'Django 1.6 and earlier does not provide Manager.from_queryset')
+    def test_manager_from_custom_queryset(self):
+        """
+        Test that a manager created from a custom queryset works.
+        Regression test for #378.
+        """
+        TreeManager.from_queryset(CustomTreeQueryset)().contribute_to_class(Genre, 'my_manager')
+
+        self.assertIsInstance(Genre.my_manager.get_queryset(), CustomTreeQueryset)
+
 
 class CacheTreeChildrenTestCase(TreeTestCase):
     """
diff --git a/tests/myapp/tests.pyc b/tests/myapp/tests.pyc
index 389625e..fe36ada 100644
Binary files a/tests/myapp/tests.pyc and b/tests/myapp/tests.pyc differ

-- 
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