[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