[Python-modules-commits] [django-sitetree] 01/07: Import django-sitetree_1.6.0.orig.tar.gz

Michael Fladischer fladi at moszumanska.debian.org
Mon Aug 29 20:52:36 UTC 2016


This is an automated email from the git hooks/post-receive script.

fladi pushed a commit to branch master
in repository django-sitetree.

commit cd8603063525b894a17ba00296bd2e743f028905
Author: Michael Fladischer <FladischerMichael at fladi.at>
Date:   Mon Aug 29 22:18:59 2016 +0200

    Import django-sitetree_1.6.0.orig.tar.gz
---
 .travis.yml                                        |  18 ++--
 AUTHORS                                            |   2 +
 CHANGELOG                                          |  11 +++
 LICENSE                                            |   4 +-
 docs/source/conf.py                                |   2 +-
 setup.cfg                                          |   5 ++
 sitetree/__init__.py                               |   2 +-
 sitetree/admin.py                                  |  12 +--
 sitetree/compat.py                                 |  37 ++++++++
 sitetree/exceptions.py                             |   4 +
 sitetree/fields.py                                 |  16 +++-
 .../management/commands/sitetree_resync_apps.py    |  22 +++--
 sitetree/management/commands/sitetreedump.py       |  31 ++++---
 sitetree/management/commands/sitetreeload.py       |  52 ++++++-----
 sitetree/models.py                                 |  15 +++-
 sitetree/runtests.py                               |  23 ++++-
 sitetree/sitetreeapp.py                            |  41 ++++++---
 sitetree/tests.py                                  |  68 +++++++++-----
 sitetree/toolbox.py                                |   7 ++
 tox.ini                                            | 100 ++++-----------------
 20 files changed, 287 insertions(+), 185 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 9cb83f6..4f1bbed 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,10 +5,10 @@ python:
   - 3.4
   - 3.3
   - 2.7
-  - 2.6
 
 env:
-  - DJANGO="Django>=1.9.0,<1.10"
+  - DJANGO="Django>=1.10,<1.11"
+  - DJANGO="Django>=1.9,<1.10"
   - DJANGO="Django>=1.8.6,<1.9"
   - DJANGO="Django>=1.7,<1.8"
   - DJANGO="Django>=1.6,<1.7"
@@ -33,19 +33,11 @@ matrix:
      env: DJANGO="Django>=1.4,<1.5"
 
    - python: 3.3
-     env: DJANGO="Django>=1.9.0,<1.10"
+     env: DJANGO="Django>=1.10,<1.11"
+   - python: 3.3
+     env: DJANGO="Django>=1.9,<1.10"
    - python: 3.3
      env: DJANGO="Django>=1.4,<1.5"
 
-   - python: 2.6
-     env: DJANGO="Django>=1.9.0,<1.10"
-   - python: 2.6
-     env: DJANGO="Django>=1.8.6,<1.9"
-   - python: 2.6
-     env: DJANGO="Django>=1.7,<1.8"
-   - python: 2.6
-     env: DJANGO="Django>=1.6,<1.7"
-     
-
 after_success:
   coveralls
diff --git a/AUTHORS b/AUTHORS
index 2f02b1c..e0f0120 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -34,6 +34,8 @@ ibooj <https://github.com/ibooj>
 Patrick Altman <https://github.com/paltman>
 Ben Cole <https://github.com/wengole>
 Vitaliy Ivanov <https://github.com/vit-ivanov>
+Sergey Maranchuk <https://github.com/slav0nic>
+Martey Dodoo <https://github.com/martey>
 
 
 Translators
diff --git a/CHANGELOG b/CHANGELOG
index bab0799..cee1011 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -2,6 +2,17 @@ django-sitetree changelog
 =========================
 
 
+v1.6.0
+------
++ Prevent TreeItems from being their own parents (see #200).
++ Added `toolbox` module as API single entry point.
++ Added `exceptions` module.
+* Django 1.10 compatibility improvements.
+* Cache.reset() misbehavior fixed (closes #191).
+* Fixed broken delete operation in admin for custom TreeItem (see #190).
+* Reduced number of cache calls (see #194).
+
+
 v1.5.1
 ------
 * Django 1.9 compatibility improvements.
diff --git a/LICENSE b/LICENSE
index b80d42c..70e78b9 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2010-2015, django-sitetree project
+Copyright (c) 2010-2016, django-sitetree project
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without modification,
@@ -11,7 +11,7 @@ are permitted provided that the following conditions are met:
        notice, this list of conditions and the following disclaimer in the
        documentation and/or other materials provided with the distribution.
 
-    3. Neither the name of the django-sitetree project nor the names of its 
+    3. Neither the name of the django-sitetree nor the names of its
        contributors may be used to endorse or promote products derived from 
        this software without specific prior written permission.
 
diff --git a/docs/source/conf.py b/docs/source/conf.py
index dc83b91..aca3488 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -42,7 +42,7 @@ master_doc = 'index'
 
 # General information about the project.
 project = u'django-sitetree'
-copyright = u'2011-2015, Igor \'idle sign\' Starikov'
+copyright = u'2011-2016, Igor \'idle sign\' Starikov'
 
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..8b8c1dc
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,5 @@
+[aliases]
+release = clean --all sdist bdist_wheel upload
+
+[wheel]
+universal = 1
diff --git a/sitetree/__init__.py b/sitetree/__init__.py
index 7f8aecb..2d28686 100644
--- a/sitetree/__init__.py
+++ b/sitetree/__init__.py
@@ -1,4 +1,4 @@
-VERSION = (1, 5, 1)
+VERSION = (1, 6, 0)
 
 
 default_app_config = 'sitetree.config.SitetreeConfig'
diff --git a/sitetree/admin.py b/sitetree/admin.py
index 13970d2..22f4505 100644
--- a/sitetree/admin.py
+++ b/sitetree/admin.py
@@ -107,6 +107,7 @@ class TreeItemAdmin(admin.ModelAdmin):
     )
     filter_horizontal = ('access_permissions',)
     change_form_template = 'admin/sitetree/treeitem/change_form.html'
+    delete_confirmation_template = 'admin/sitetree/treeitem/delete_confirmation.html'
 
     def response_add(self, request, obj, post_url_continue=None, **kwargs):
         """Redirects to the appropriate items' 'continue' page on item add.
@@ -257,18 +258,18 @@ def redirects_handler(*args, **kwargs):
     introduced in Django 1.4 by url handling changes.
 
     """
-    referer = args[0].META['HTTP_REFERER']
+    path = args[0].path
     shift = '../'
 
-    if 'delete' in referer:
+    if 'delete' in path:
         # Weird enough 'delete' is not handled by TreeItemAdmin::response_change().
         shift += '../'
-    elif 'history' in referer:
+    elif 'history' in path:
         if 'item_id' not in kwargs:
             # Encountered request from history page to return to tree layout page.
             shift += '../'
 
-    return HttpResponseRedirect(referer + shift)
+    return HttpResponseRedirect(path + shift)
 
 
 class TreeAdmin(admin.ModelAdmin):
@@ -300,8 +301,7 @@ class TreeAdmin(admin.ModelAdmin):
         prefix_change = 'change/' if DJANGO_POST_19 else ''
 
         sitetree_urls = [
-            # Ignore urls.W002. Leading slash is in the right place.
-            url(r'^/$', redirects_handler, name=get_tree_item_url_name('changelist')),
+            url(r'^change/$', redirects_handler, name=get_tree_item_url_name('changelist')),
 
             url(r'^((?P<tree_id>\d+)/)?%sitem_add/$' % prefix_change,
                 self.admin_site.admin_view(self.tree_admin.item_add), name=get_tree_item_url_name('add')),
diff --git a/sitetree/compat.py b/sitetree/compat.py
new file mode 100644
index 0000000..d494b4e
--- /dev/null
+++ b/sitetree/compat.py
@@ -0,0 +1,37 @@
+from django import VERSION
+
+
+class CommandOption(object):
+    """Command line option wrapper."""
+
+    def __init__(self, *args, **kwargs):
+        self.args = args
+        self.kwargs = kwargs
+
+
+def options_getter(command_options):
+    """Compatibility function to get rid of optparse in management commands after Django 1.10.
+
+    :param tuple command_options: tuple with `CommandOption` objects.
+
+    """
+    def get_options(option_func=None):
+        from optparse import make_option
+        from django.core.management.base import BaseCommand
+
+        func = option_func or make_option
+
+        options = tuple([func(*option.args, **option.kwargs) for option in command_options])
+
+        if option_func is None:
+            if VERSION < (1, 8):
+                result = BaseCommand.option_list + options
+            else:
+                result = []
+
+        else:
+            result = options
+
+        return result
+
+    return get_options
diff --git a/sitetree/exceptions.py b/sitetree/exceptions.py
new file mode 100644
index 0000000..fbfedfc
--- /dev/null
+++ b/sitetree/exceptions.py
@@ -0,0 +1,4 @@
+
+
+class SiteTreeError(Exception):
+    """Exception class for sitetree application."""
diff --git a/sitetree/fields.py b/sitetree/fields.py
index 0dd9c15..05df777 100644
--- a/sitetree/fields.py
+++ b/sitetree/fields.py
@@ -1,3 +1,4 @@
+from django import VERSION
 from django import template
 from django.template.base import Parser, Token, TOKEN_BLOCK
 from django.forms import ChoiceField
@@ -24,10 +25,13 @@ class TreeItemChoiceField(ChoiceField):
     root_title = '---------'
 
     def __init__(self, tree, required=True, widget=None, label=None, initial=None, help_text=None, *args, **kwargs):
-        super(TreeItemChoiceField, self).__init__(required=required, widget=widget, label=label, initial=initial,
-                                                  help_text=help_text, *args, **kwargs)
+        super(TreeItemChoiceField, self).__init__(
+            required=required, widget=widget, label=label, initial=initial,
+            help_text=help_text, *args, **kwargs)
+
         if isinstance(tree, MODEL_TREE_CLASS):
             tree = tree.alias
+
         self.tree = tree
         self.choices = self._build_choices()
 
@@ -35,9 +39,15 @@ class TreeItemChoiceField(ChoiceField):
         """Build choices list runtime using 'sitetree_tree' tag"""
         tree_token = u'sitetree_tree from "%s" template "%s"' % (self.tree, self.template)
 
+        context_kwargs = {'current_app': 'admin'}
+        if VERSION >= (1, 8):
+            context = template.Context(context_kwargs)
+        else:
+            context = template.Context(**context_kwargs)
+
         choices_str = sitetree_tree(
             Parser(None), Token(token_type=TOKEN_BLOCK, contents=tree_token)
-        ).render(template.Context(current_app='admin'))
+        ).render(context)
 
         tree_choices = [('', self.root_title)]
         for line in choices_str.splitlines():
diff --git a/sitetree/management/commands/sitetree_resync_apps.py b/sitetree/management/commands/sitetree_resync_apps.py
index b389b32..0a06135 100644
--- a/sitetree/management/commands/sitetree_resync_apps.py
+++ b/sitetree/management/commands/sitetree_resync_apps.py
@@ -1,16 +1,23 @@
-from optparse import make_option
-
 from django.core.management.base import BaseCommand
 from django.db import DEFAULT_DB_ALIAS
 
 from sitetree.utils import get_tree_model, import_project_sitetree_modules
 from sitetree.settings import APP_MODULE_NAME
 from sitetree.sitetreeapp import Cache
+from sitetree.compat import CommandOption, options_getter
 
 
 MODEL_TREE_CLASS = get_tree_model()
 
 
+get_options = options_getter((
+    CommandOption(
+        '--database', action='store', dest='database', default=DEFAULT_DB_ALIAS,
+        help='Nominates a specific database to place trees and items into. Defaults to the "default" database.'
+    ),
+))
+
+
 class Command(BaseCommand):
 
     help = 'Places sitetrees of the project applications (defined in `app_name.sitetree.py`) into DB, ' \
@@ -18,12 +25,11 @@ class Command(BaseCommand):
 
     args = '[app_name app_name ...]'
 
-    option_list = BaseCommand.option_list + (
-        make_option(
-            '--database', action='store', dest='database', default=DEFAULT_DB_ALIAS,
-            help='Nominates a specific database to place trees and items into. Defaults to the "default" database.'
-        ),
-    )
+    option_list = get_options()
+
+    def add_arguments(self, parser):
+        parser.add_argument('args', metavar='app', nargs='+', help='Application names.')
+        get_options(parser.add_argument)
 
     def handle(self, *apps, **options):
         using = options.get('database', DEFAULT_DB_ALIAS)
diff --git a/sitetree/management/commands/sitetreedump.py b/sitetree/management/commands/sitetreedump.py
index 321be5e..f077c11 100644
--- a/sitetree/management/commands/sitetreedump.py
+++ b/sitetree/management/commands/sitetreedump.py
@@ -1,30 +1,38 @@
-from optparse import make_option
-
 from django.core import serializers
 from django.core.management.base import BaseCommand, CommandError
 from django.db import DEFAULT_DB_ALIAS
 
 from sitetree.utils import get_tree_model, get_tree_item_model
+from sitetree.compat import CommandOption, options_getter
 
 
 MODEL_TREE_CLASS = get_tree_model()
 MODEL_TREE_ITEM_CLASS = get_tree_item_model()
 
 
+get_options = options_getter((
+    CommandOption(
+        '--indent', default=None, dest='indent', type=int,
+        help='Specifies the indent level to use when pretty-printing output.'),
+
+    CommandOption('--items_only', action='store_true', dest='items_only', default=False,
+         help='Export tree items only.'),
+
+    CommandOption('--database', action='store', dest='database', default=DEFAULT_DB_ALIAS,
+         help='Nominates a specific database to export fixtures from. Defaults to the "default" database.'),
+))
+
+
 class Command(BaseCommand):
 
-    option_list = BaseCommand.option_list + (
-        make_option('--indent', default=None, dest='indent', type='int',
-            help='Specifies the indent level to use when pretty-printing output.'),
-        make_option('--items_only', action='store_true', dest='items_only', default=False,
-            help='Export tree items only.'),
-        make_option('--database', action='store', dest='database',
-            default=DEFAULT_DB_ALIAS, help='Nominates a specific database to export fixtures from. '
-                    'Defaults to the "default" database.'),
-        )
+    option_list = get_options()
     help = 'Output sitetrees from database as a fixture in JSON format.'
     args = '[tree_alias tree_alias ...]'
 
+    def add_arguments(self, parser):
+        parser.add_argument('args', metavar='tree', nargs='?', help='Tree aliases.', default=[])
+        get_options(parser.add_argument)
+
     def handle(self, *aliases, **options):
 
         indent = options.get('indent', None)
@@ -46,5 +54,6 @@ class Command(BaseCommand):
 
         try:
             return serializers.serialize('json', objects, indent=indent)
+
         except Exception as e:
             raise CommandError('Unable to serialize sitetree(s): %s' % e)
diff --git a/sitetree/management/commands/sitetreeload.py b/sitetree/management/commands/sitetreeload.py
index 81a90a4..a90c089 100644
--- a/sitetree/management/commands/sitetreeload.py
+++ b/sitetree/management/commands/sitetreeload.py
@@ -1,8 +1,7 @@
 import sys
-from optparse import make_option
 from collections import defaultdict
 
-import django
+from django import VERSION
 from django.core import serializers
 from django.core.management.base import BaseCommand, CommandError
 from django.core.management.color import no_style
@@ -10,26 +9,43 @@ from django.db import connections, router, transaction, DEFAULT_DB_ALIAS
 from django.core.exceptions import ObjectDoesNotExist
 
 from sitetree.utils import get_tree_model, get_tree_item_model
+from sitetree.compat import CommandOption, options_getter
 
 
 MODEL_TREE_CLASS = get_tree_model()
 MODEL_TREE_ITEM_CLASS = get_tree_item_model()
 
+VER_LESS_17 = VERSION < (1, 7)
+VER_LESS_18 = VERSION < (1, 8)
+
+
+get_options = options_getter((
+    CommandOption(
+        '--database', action='store', dest='database',
+        default=DEFAULT_DB_ALIAS, help='Nominates a specific database to load fixtures into. '
+                                       'Defaults to the "default" database.'),
+
+    CommandOption(
+        '--mode', action='store', dest='mode', default='append',
+        help='Mode to put data into DB. Variants: `replace`, `append`.'),
+
+    CommandOption(
+        '--items_into_tree', action='store', dest='into_tree', default=None,
+        help='Import only tree items data into tree with given alias.'),
+))
+
 
 class Command(BaseCommand):
 
-    option_list = BaseCommand.option_list + (
-        make_option('--database', action='store', dest='database',
-            default=DEFAULT_DB_ALIAS, help='Nominates a specific database to load fixtures into. '
-                    'Defaults to the "default" database.'),
-        make_option('--mode', action='store', dest='mode',
-            default='append', help='Mode to put data into DB. Variants: `replace`, `append`.'),
-        make_option('--items_into_tree', action='store', dest='into_tree',
-            default=None, help='Import only tree items data into tree with given alias.'),
-        )
+    option_list = get_options()
+
     help = 'Loads sitetrees from fixture in JSON format into database.'
     args = '[fixture_file fixture_file ...]'
 
+    def add_arguments(self, parser):
+        parser.add_argument('args', metavar='fixture', nargs='+', help='Fixture files.')
+        get_options(parser.add_argument)
+
     def handle(self, *fixture_files, **options):
 
         using = options.get('database', DEFAULT_DB_ALIAS)
@@ -50,17 +66,13 @@ class Command(BaseCommand):
 
         self.style = no_style()
 
-        django_version = django.get_version()
-        django_version_less_17 = django_version < '1.7'
-        django_version_less_18 = django_version < '1.8'
-
-        if django_version_less_17:
+        if VER_LESS_17:
             transaction.commit_unless_managed(using=using)
 
-        if django_version_less_18:
+        if VER_LESS_18:
             transaction.enter_transaction_management(using=using)
 
-        if django_version_less_17:
+        if VER_LESS_17:
             transaction.managed(True, using=using)
 
         loaded_object_count = 0
@@ -164,7 +176,7 @@ class Command(BaseCommand):
                 import traceback
                 fixture.close()
 
-                if django_version_less_18:
+                if VER_LESS_18:
                     transaction.rollback(using=using)
                     transaction.leave_transaction_management(using=using)
 
@@ -186,7 +198,7 @@ class Command(BaseCommand):
                 for line in sequence_sql:
                     cursor.execute(line)
 
-        if django_version_less_18:
+        if VER_LESS_18:
             transaction.commit(using=using)
             transaction.leave_transaction_management(using=using)
 
diff --git a/sitetree/models.py b/sitetree/models.py
index 45bc47c..7304b92 100644
--- a/sitetree/models.py
+++ b/sitetree/models.py
@@ -125,11 +125,22 @@ class TreeItemBase(models.Model):
         help_text=_('Item position among other site tree items under the same parent.'), db_index=True, default=0)
 
     def save(self, force_insert=False, force_update=False, **kwargs):
-        """We override parent save method to set item's sort order to its' primary
-        key value.
+        """Ensure that item is not its own parent and set proper sort order.
 
         """
+        # Ensure that item is not its own parent, since this breaks
+        # the sitetree (and possibly the entire site).
+        if self.parent == self:
+            self.parent = None
+        
+        # Set item's sort order to its primary key.
+        id_ = self.id
+        if id_ and self.sort_order == 0:
+            self.sort_order = id_
+        
         super(TreeItemBase, self).save(force_insert, force_update, **kwargs)
+
+        # Set item's sort order to its primary key if not already set.
         if self.sort_order == 0:
             self.sort_order = self.id
             self.save()
diff --git a/sitetree/runtests.py b/sitetree/runtests.py
index 90d67bf..27970d5 100755
--- a/sitetree/runtests.py
+++ b/sitetree/runtests.py
@@ -12,15 +12,30 @@ def main():
     sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
 
     if not settings.configured:
-        settings.configure(
+
+        configure_kwargs = dict(
             INSTALLED_APPS=('django.contrib.auth', 'django.contrib.contenttypes', APP_NAME),
             DATABASES={'default': {'ENGINE': 'django.db.backends.sqlite3'}},
-            ROOT_URLCONF = 'sitetree.tests',
+            ROOT_URLCONF='sitetree.tests',
             MIDDLEWARE_CLASSES=global_settings.MIDDLEWARE_CLASSES,  # Prevents Django 1.7 warning.
-            TEMPLATE_CONTEXT_PROCESSORS=tuple(global_settings.TEMPLATE_CONTEXT_PROCESSORS) + (
+        )
+
+        try:
+            configure_kwargs['TEMPLATE_CONTEXT_PROCESSORS'] = tuple(global_settings.TEMPLATE_CONTEXT_PROCESSORS) + (
                 'django.core.context_processors.request',
             )
-        )
+
+        except AttributeError:
+
+            # Django 1.8+
+            configure_kwargs['TEMPLATES'] = [
+                {
+                    'BACKEND': 'django.template.backends.django.DjangoTemplates',
+                    'APP_DIRS': True,
+                },
+            ]
+
+        settings.configure(**configure_kwargs)
 
     try:  # Django 1.7 +
         from django import setup
diff --git a/sitetree/sitetreeapp.py b/sitetree/sitetreeapp.py
index 2f79c64..e387106 100644
--- a/sitetree/sitetreeapp.py
+++ b/sitetree/sitetreeapp.py
@@ -26,6 +26,7 @@ from .utils import get_tree_model, get_tree_item_model, import_app_sitetree_modu
 from .settings import (
     ALIAS_TRUNK, ALIAS_THIS_CHILDREN, ALIAS_THIS_SIBLINGS, ALIAS_THIS_PARENT_SIBLINGS, ALIAS_THIS_ANCESTOR_CHILDREN,
     UNRESOLVED_ITEM_MARKER, RAISE_ITEMS_ERRORS_ON_DEBUG, CACHE_TIMEOUT)
+from .exceptions import SiteTreeError
 
 
 if VERSION >= (1, 9, 0):
@@ -53,6 +54,7 @@ _SITETREE = None
 
 _THREAD_LOCAL = local()
 _THREAD_LANG = 'sitetree_lang'
+_THREAD_CACHE = 'sitetree_cache'
 
 
 def get_sitetree():
@@ -284,7 +286,7 @@ class Cache(object):
         Could be used to show up tree changes made in a different process.
 
         """
-        cache.get('sitetrees_reset', True)
+        cache.set('sitetrees_reset', True)
 
     def init(self):
         """Initializes local cache from Django cache."""
@@ -292,10 +294,15 @@ class Cache(object):
         # Drop cache flag set by .reset() method.
         cache.get('sitetrees_reset') and self.empty()
 
-        cache_ = cache.get('sitetrees')
+        cache_ = getattr(_THREAD_LOCAL, _THREAD_CACHE, None)
         if cache_ is None:
-            # Init cache dictionary with predefined entries.
-            cache_ = {'sitetrees': {}, 'urls': {}, 'parents': {}, 'items_by_ids': {}, 'tree_aliases': {}}
+
+            cache_ = cache.get(
+                # Init cache dictionary with predefined entries.
+                'sitetrees', {'sitetrees': {}, 'urls': {}, 'parents': {}, 'items_by_ids': {}, 'tree_aliases': {}})
+
+            setattr(_THREAD_LOCAL, _THREAD_CACHE, cache_)
+
         self.cache = cache_
 
     def save(self):
@@ -305,6 +312,7 @@ class Cache(object):
     def empty(self, **kwargs):
         """Empties cached sitetree data."""
         self.cache = None
+        setattr(_THREAD_LOCAL, _THREAD_CACHE, None)
         cache.delete('sitetrees')
         cache.delete('sitetrees_reset')
 
@@ -410,9 +418,16 @@ class SiteTree(object):
 
     def current_app_is_admin(self):
         """Returns boolean whether current application is Admin contrib."""
-        current_app = (
-            getattr(self._global_context.get('request', None), 'current_app',
-                    self._global_context.current_app))
+        global_context = self._global_context
+
+        current_app = getattr(
+            # Try from request.resolver_match.app_name
+            getattr(global_context.get('request', None), 'resolver_match', None), 'app_name',
+            # Try from global context obj.
+            getattr(global_context, 'current_app', None))
+
+        if current_app is None:  # Try from global context dict.
+            current_app = global_context.get('current_app', '')
 
         return current_app == 'admin'
 
@@ -427,8 +442,6 @@ class SiteTree(object):
         get_cache_entry = cache.get_entry
         set_cache_entry = cache.set_entry
 
-        cache.init()
-
         sitetree_needs_caching = False
         if not self.current_app_is_admin():
             # We do not need i18n for a tree rendered in Admin dropdown.
@@ -638,11 +651,16 @@ class SiteTree(object):
         self.lang_init()
         # Resolve tree_alias from the context.
         tree_alias = self.resolve_var(tree_alias)
+
+        self.cache.init()  # Warm up cache.
+
         # Get tree.
         tree_alias, sitetree_items = self.get_sitetree(tree_alias)
+
         # No items in tree, fail silently.
         if not sitetree_items:
             return False, False
+
         return tree_alias, sitetree_items
 
     def get_current_page_title(self, tree_alias, context):
@@ -856,6 +874,7 @@ class SiteTree(object):
         if start_from.inbreadcrumbs and start_from.hidden == False and self.check_access(start_from,
                                                                                          self._global_context):
             self.cache_breadcrumbs.append(start_from)
+
         if hasattr(start_from, 'parent') and start_from.parent is not None:
             self.breadcrumbs_climber(tree_alias, self.get_item_by_id(tree_alias, start_from.parent.id))
 
@@ -879,7 +898,3 @@ class SiteTree(object):
                 varname = varname
 
         return varname
-
-
-class SiteTreeError(Exception):
-    """Exception class for sitetree application."""
diff --git a/sitetree/tests.py b/sitetree/tests.py
index a38ac17..a857a0c 100644
--- a/sitetree/tests.py
+++ b/sitetree/tests.py
@@ -5,12 +5,13 @@ try:
     from StringIO import StringIO
 except ImportError:
     from io import StringIO
-    
+
 try:
     from unittest import mock
 except ImportError:
     import mock
 
+from django import VERSION
 from django.conf import settings
 from django.utils.translation import activate
 from django.template.base import Template, TemplateSyntaxError
@@ -21,26 +22,26 @@ from django.contrib.auth.models import Permission
 from django.contrib.admin.sites import site
 from django.core.management import call_command
 from django.core.exceptions import ImproperlyConfigured
-from django.conf.urls import patterns, url
+from django.conf.urls import url
 
 from sitetree.models import Tree, TreeItem
-from sitetree.forms import TreeItemForm
 from sitetree.admin import TreeAdmin, TreeItemAdmin, redirects_handler
-from sitetree.utils import (
-    tree, item, get_app_n_model, import_app_sitetree_module, import_project_sitetree_modules, get_model_class
-)
-from sitetree.sitetreeapp import (
-    SiteTree, SiteTreeError, register_items_hook, register_i18n_trees, register_dynamic_trees, compose_dynamic_tree,
-    get_dynamic_trees
-)
-
+from sitetree.utils import get_app_n_model, get_model_class
+from sitetree.sitetreeapp import SiteTree
+from sitetree.toolbox import TreeItemForm, tree, item, import_app_sitetree_module, import_project_sitetree_modules, \
+    register_items_hook, register_i18n_trees, register_dynamic_trees, compose_dynamic_tree, get_dynamic_trees
+from sitetree.exceptions import SiteTreeError
 
-urlpatterns = patterns(
-    '',
+urlpatterns = [
     url(r'articles/', lambda r: None, name='articles_list'),
     url(r'articles/(\d+)/', lambda r: None, name='articles_detailed'),
     url(r'articles/(?P<id>\d+)_(?P<slug>[\w-]+)/', lambda r: None, name='url'),
-)
+]
+
+if VERSION < (1, 10):
+    from django.conf.urls import patterns
+    urlpatterns.insert(0, '')
+    urlpatterns = patterns(*urlpatterns)
 
 
 class MockRequest(object):
@@ -70,17 +71,26 @@ class MockUser(object):
         return self.permissions
 
 
-def get_mock_context(app=None, path=None, user_authorized=False, tree_item=None, put_var=None):
+def get_mock_context(app='', path=None, user_authorized=False, tree_item=None, put_var=None):
+
     ctx = Context(
         {
             'request': MockRequest(path, user_authorized),
             't2_root2_title': 'my_real_title', 'art_id': 10, 'tree_item': tree_item,
             'somevar_str': 'articles_list', 'somevar_list': ['a', 'b'], 'put_var': put_var
-        },
-        current_app=app
+        }
     )
     ctx.template = mock.MagicMock()
     ctx.template.engine.string_if_invalid = ''
+
+    if VERSION >= (1, 10):
+        match = mock.MagicMock()
+        match.app_name = app
+        ctx.resolver_match = match
+
+    else:
+        ctx._current_app = app
+
     return ctx
 
 
@@ -288,6 +298,19 @@ class TreeItemModelTest(SitetreeTest):
         ti1.delete()
         self.assertIsNone(ti1.id)
 
+    def test_no_recursive_parents(self):
+        """Verify that treeitems cannot be their own parent."""
+        tree = Tree(alias="mytree")
+        tree.save()
+        tree_item = TreeItem(title="i'm my own grandpa", tree=tree)
+        # This item needs to be saved, otherwise it cannot be set
+        # as a parent.
+        tree_item.save()
+
+        tree_item.parent = tree_item
+        tree_item.save()
+        self.assertNotEqual(tree_item, tree_item.parent)
+
     def test_context_proc_required(self):
         context = Context()
         old_debug = settings.DEBUG
@@ -590,7 +613,9 @@ class TreeTest(SitetreeTest):
         self.assertEqual(self.t3_en.get_title(), 'tree3en_title')
 
     def test_children_filtering(self):
-        self.sitetree._global_context = get_mock_context(path='/')
+        self.sitetree.cache.init()
+
+        self.sitetree.set_global_context(get_mock_context(path='/'))
         self.sitetree.get_sitetree('tree3')
         children = self.sitetree.get_children('tree3', self.t3_root)
         filtered = self.sitetree.filter_items(children, 'menu')
@@ -602,7 +627,7 @@ class TreeTest(SitetreeTest):
 
     def test_register_i18n_trees(self):
         register_i18n_trees(['tree3'])
-        self.sitetree._global_context = get_mock_context(path='/the_same_url/')
+        self.sitetree.set_global_context(get_mock_context(path='/the_same_url/'))
 
         activate('en')
         self.sitetree.get_sitetree('tree3')
@@ -625,6 +650,8 @@ class DynamicTreeTest(SitetreeTest):
 
     def test_basic_old_and_new(self):
 
+        self.sitetree.cache.init()
+
         # Assert no dynamic attached.
         tree_alias, sitetree_items = self.sitetree.get_sitetree('main')
         self.assertEqual(len(sitetree_items), 1)
@@ -683,7 +710,8 @@ class DynamicTreeTest(SitetreeTest):
             register_dynamic_trees(trees, **kwargs)
 
         mock_context = get_mock_context(path='/the_same_url/')
-        self.sitetree._global_context = mock_context
+        self.sitetree.cache.init()
+        self.sitetree.set_global_context(mock_context)
         tree_alias, sitetree_items = self.sitetree.get_sitetree('main')
 
         if reset_cache:
diff --git a/sitetree/toolbox.py b/sitetree/toolbox.py
new file mode 100644
index 0000000..d46082f
--- /dev/null
+++ b/sitetree/toolbox.py
@@ -0,0 +1,7 @@
+# Unused imports below are exposed as API.
+from .utils import get_tree_item_model, get_tree_model, tree, item, import_app_sitetree_module, \
+    import_project_sitetree_modules
+from .sitetreeapp import register_i18n_trees, register_items_hook, compose_dynamic_tree, register_dynamic_trees, \
+    get_dynamic_trees
+from .fields import TreeItemChoiceField
+from .forms import TreeItemForm
diff --git a/tox.ini b/tox.ini
index ac713cb..81ce724 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,87 +1,25 @@
 [tox]
-envlist =
-    py27-django14,
-    py27-django15,
-    py27-django16,
-    py27-django17,
-    py27-django18,
-    py27-django19,
+envlist = py{27,32,33,34}-django{15,16,17,18,19,110}
+          py{35}-django{18,19,110}
 
-    py3-django15,
-    py3-django16,
-    py3-django17,
-    py3-django18,
-    py3-django19
-
-
-[django14]
-deps = Django>=1.4,<1.5
-
-[django15]
-deps = Django>=1.5,<1.6
-
-[django16]
-deps = Django>=1.6,<1.7
-
-[django17]
-deps = Django>=1.7,<1.8
-
-[django18]
-deps = Django>=1.8,<1.9
-
-[django19]
-deps = Django==1.9.0
+install_command = pip install {opts} {packages}
+skip_missing_interpreters = True
 
 [testenv]
-commands = python sitetree/runtests.py
-
-
-[testenv:py27-django14]
-basepython = python2.7
-deps = {[django14]deps}
-       mock
+basepython =
+    py27: python2.7
+    py32: python3.2
+    py33: python3.3
+    py34: python3.4
+    py35: python3.5
 
-[testenv:py27-django15]
-basepython = python2.7
-deps = {[django15]deps}
-       mock
-
-[testenv:py27-django16]
-basepython = python2.7
-deps = {[django16]deps}
-       mock
-
-[testenv:py27-django17]
-basepython = python2.7
-deps = {[django17]deps}
-       mock
-
-[testenv:py27-django18]
-basepython = python2.7
-deps = {[django18]deps}
-       mock
-
-[testenv:py27-django19]
-basepython = python2.7
-deps = {[django19]deps}
-       mock
-
-[testenv:py3-django15]
-basepython = python3
-deps = {[django15]deps}
-
-[testenv:py3-django16]
-basepython = python3
-deps = {[django16]deps}
-
-[testenv:py3-django17]
-basepython = python3
-deps = {[django17]deps}
-
-[testenv:py3-django18]
-basepython = python3
-deps = {[django18]deps}
+commands = python sitetree/runtests.py
 
-[testenv:py3-django19]
-basepython = python3
-deps = {[django19]deps}
+deps =
+    mock
+    django15: Django>=1.5,<1.6
+    django16: Django>=1.6,<1.7
+    django17: Django>=1.7,<1.8
+    django18: Django>=1.8,<1.9
+    django19: Django>=1.9,<1.10
+    django110: Django>=1.10,<1.11

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/django-sitetree.git



More information about the Python-modules-commits mailing list