[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