django-mptt-0.8.0/0000755000076500000240000000000012635633365015237 5ustar cdestigterstaff00000000000000django-mptt-0.8.0/django_mptt.egg-info/0000755000076500000240000000000012635633364021236 5ustar cdestigterstaff00000000000000django-mptt-0.8.0/django_mptt.egg-info/dependency_links.txt0000644000076500000240000000000112635633355025304 0ustar cdestigterstaff00000000000000 django-mptt-0.8.0/django_mptt.egg-info/PKG-INFO0000644000076500000240000000216112635633355022333 0ustar cdestigterstaff00000000000000Metadata-Version: 1.1 Name: django-mptt 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 Author: Craig de Stigter Author-email: craig.ds@gmail.com License: UNKNOWN Description: UNKNOWN Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Environment :: Web Environment Classifier: Framework :: Django Classifier: Intended Audience :: Developers 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.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.2 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Utilities django-mptt-0.8.0/django_mptt.egg-info/requires.txt0000644000076500000240000000001312635633355023630 0ustar cdestigterstaff00000000000000Django>=1.8django-mptt-0.8.0/django_mptt.egg-info/SOURCES.txt0000644000076500000240000000620712635633364023127 0ustar cdestigterstaff00000000000000INSTALL LICENSE MANIFEST.in NOTES README.rst setup.cfg setup.py django_mptt.egg-info/PKG-INFO django_mptt.egg-info/SOURCES.txt django_mptt.egg-info/dependency_links.txt django_mptt.egg-info/requires.txt django_mptt.egg-info/top_level.txt docs/Makefile docs/admin.rst docs/autogenerated.rst docs/conf.py docs/forms.rst docs/index.rst docs/install.rst docs/models.rst docs/mptt.admin.rst docs/mptt.exceptions.rst 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 docs/technical_details.rst docs/templates.rst docs/tutorial.rst docs/upgrade.rst docs/utilities.rst mptt/__init__.py mptt/admin.py mptt/exceptions.py mptt/fields.py mptt/forms.py 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 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 mptt/locale/mn/LC_MESSAGES/django.po mptt/locale/nb/LC_MESSAGES/django.mo mptt/locale/nb/LC_MESSAGES/django.po mptt/locale/pl/LC_MESSAGES/django.mo mptt/locale/pl/LC_MESSAGES/django.po mptt/locale/pt_BR/LC_MESSAGES/django.mo mptt/locale/pt_BR/LC_MESSAGES/django.po mptt/locale/ru/LC_MESSAGES/django.mo mptt/locale/ru/LC_MESSAGES/django.po mptt/templates/admin/grappelli_mptt_change_list.html mptt/templates/admin/grappelli_mptt_change_list_results.html mptt/templates/admin/mptt_change_list.html mptt/templates/admin/mptt_change_list_results.html mptt/templatetags/__init__.py mptt/templatetags/mptt_admin.py mptt/templatetags/mptt_tags.py 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 tests/myapp/admin.pyc tests/myapp/doctests.txt tests/myapp/models.py tests/myapp/models.pyc tests/myapp/tests.py tests/myapp/tests.pyc 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 tests/myapp/fixtures/persons.jsondjango-mptt-0.8.0/django_mptt.egg-info/top_level.txt0000644000076500000240000000000512635633355023763 0ustar cdestigterstaff00000000000000mptt django-mptt-0.8.0/docs/0000755000076500000240000000000012635633364016166 5ustar cdestigterstaff00000000000000django-mptt-0.8.0/docs/admin.rst0000644000076500000240000000231612627654434020014 0ustar cdestigterstaff00000000000000============= Admin classes ============= ``mptt.admin.MPTTModelAdmin`` ----------------------------- This is a bare-bones tree admin. All it does is enforce ordering, and indent the nodes in the tree to make a pretty tree list view. Usage:: from django.contrib import admin from mptt.admin import MPTTModelAdmin from myproject.myapp.models import Node admin.site.register(Node, MPTTModelAdmin) You can change the indent pixels per level globally by putting this in your settings.py:: # default is 10 pixels MPTT_ADMIN_LEVEL_INDENT = 20 If you'd like to specify the pixel amount per Model, define an ``mptt_level_indent`` attribute in your MPTTModelAdmin:: from django.contrib import admin from mptt.admin import MPTTModelAdmin from myproject.myapp.models import Node class CustomMPTTModelAdmin(MPTTModelAdmin): # specify pixel amount for this ModelAdmin only: mptt_level_indent = 20 admin.site.register(Node, CustomMPTTModelAdmin) If you'd like to specify which field should be indented, add an ``mptt_indent_field`` to your MPTTModelAdmin:: # … class CustomMPTTModelAdmin(MPTTModelAdmin): mptt_indent_field = "some_node_field" # … django-mptt-0.8.0/docs/autogenerated.rst0000644000076500000240000000051412632165737021550 0ustar cdestigterstaff00000000000000 Autogenerated documentation =========================== These docs are generated by Sphinx from the docstrings in ``django-mptt``. They're not necessarily very helpful. You might be just as well off reading the `source code`_. .. toctree:: :maxdepth: 3 mptt .. _`source code`: http://github.com/django-mptt/django-mptt/ django-mptt-0.8.0/docs/conf.py0000644000076500000240000001610512515002575017457 0ustar cdestigterstaff00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals # # django-mptt documentation build configuration file, created by # sphinx-quickstart on Wed Sep 8 20:11:06 2010. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath('..')) os.environ['DJANGO_SETTINGS_MODULE'] = 'tests.settings' # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx'] # Add any paths that contain templates here, relative to this directory. templates_path = [] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = 'django-mptt' copyright = '2007 - 2013, Craig de Stigter, Jonathan Buchanan and others' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version_tuple = __import__('mptt').VERSION version = ".".join(str(v) for v in version_tuple) # The full version, including alpha/beta/rc tags. release = version # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'django-mpttdoc' # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'django-mptt.tex', 'django-mptt Documentation', 'Craig de Stigter', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'django-mptt', 'django-mptt Documentation', ['Craig de Stigter'], 1) ] django-mptt-0.8.0/docs/forms.rst0000644000076500000240000001247612462512456020054 0ustar cdestigterstaff00000000000000================================== Working with trees in Django forms ================================== .. contents:: :depth: 3 Fields ====== The following custom form fields are provided in the ``mptt.forms`` package. ``TreeNodeChoiceField`` ----------------------- This is the default formfield used by ``TreeForeignKey`` A subclass of `ModelChoiceField`_ which represents the tree level of each node when generating option labels. For example, where a form which used a ``ModelChoiceField``:: category = ModelChoiceField(queryset=Category.objects.all()) ...would result in a select with the following options:: --------- Root 1 Child 1.1 Child 1.1.1 Root 2 Child 2.1 Child 2.1.1 Using a ``TreeNodeChoiceField`` instead:: category = TreeNodeChoiceField(queryset=Category.objects.all()) ...would result in a select with the following options:: Root 1 --- Child 1.1 ------ Child 1.1.1 Root 2 --- Child 2.1 ------ Child 2.1.1 The text used to indicate a tree level can by customised by providing a ``level_indicator`` argument:: category = TreeNodeChoiceField(queryset=Category.objects.all(), level_indicator=u'+--') ...which for this example would result in a select with the following options:: Root 1 +-- Child 1.1 +--+-- Child 1.1.1 Root 2 +-- Child 2.1 +--+-- Child 2.1.1 .. _`ModelChoiceField`: http://docs.djangoproject.com/en/dev/ref/forms/fields/#django.forms.ModelChoiceField ``TreeNodeMultipleChoiceField`` ------------------------------- Just like ``TreeNodeChoiceField``, but accepts more than one value. ``TreeNodePositionField`` ------------------------- A subclass of `ChoiceField`_ whose choices default to the valid arguments for the `move_to method`_. .. _`ChoiceField`: http://docs.djangoproject.com/en/dev/ref/forms/fields/#choicefield Forms ===== The following custom form is provided in the ``mptt.forms`` package. ``MoveNodeForm`` ---------------- A form which allows the user to move a given node from one location in its tree to another, with optional restriction of the nodes which are valid target nodes for the `move_to method` Fields ~~~~~~ The form contains the following fields: * ``target`` -- a ``TreeNodeChoiceField`` for selecting the target node for the node movement. Target nodes will be displayed as a single `` {% for node,structure in classifiers|tree_info:"ancestors" %} {% if node.is_child_node %} {% endif %} {% endfor %} django-mptt-0.8.0/docs/tutorial.rst0000644000076500000240000001120112515002575020545 0ustar cdestigterstaff00000000000000 ======== Tutorial ======== The Problem =========== You've created a Django project, and you need to manage some hierarchical data. For instance you've got a bunch of hierarchical pages in a CMS, and sometimes pages are *children* of other pages Now suppose you want to show a breadcrumb on your site, like this:: Home > Products > Food > Meat > Spam > Spammy McDelicious To get all those page titles you might do something like this:: titles = [] while page: titles.append(page.title) page = page.parent That's one database query for each page in the breadcrumb, and database queries are slow. Let's do this a better way. The Solution ============ Modified Preorder Tree Traversal can be a bit daunting at first, but it's one of the best ways to solve this problem. If you want to go into the details, there's a good explanation here: `Storing Hierarchical Data in a Database`_ or `Managing Hierarchical Data in Mysql`_ tl;dr: MPTT makes most tree operations much cheaper in terms of queries. In fact all these operations take at most one query, and sometimes zero: * get descendants of a node * get ancestors of a node * get all nodes at a given level * get leaf nodes And this one takes zero queries: * count the descendants of a given node .. _`Storing Hierarchical Data in a Database`: http://www.sitepoint.com/hierarchical-data-database/ .. _`Managing Hierarchical Data in Mysql`: http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/ Enough intro. Let's get started. Getting started =============== Add ``mptt`` To ``INSTALLED_APPS`` ---------------------------------- As with most Django applications, you should add ``mptt`` to the ``INSTALLED_APPS`` in your ``settings.py`` file:: INSTALLED_APPS = ( 'django.contrib.auth', # ... 'mptt', ) Set up your model ----------------- Start with a basic subclass of MPTTModel, something like this:: from django.db import models from mptt.models import MPTTModel, TreeForeignKey class Genre(MPTTModel): name = models.CharField(max_length=50, unique=True) parent = TreeForeignKey('self', null=True, blank=True, related_name='children', db_index=True) class MPTTMeta: order_insertion_by = ['name'] You must define a parent field which is a ``TreeForeignKey`` to ``'self'``. A ``TreeForeignKey`` is just a regular ``ForeignKey`` that renders form fields differently in the admin and a few other places. Because you're inheriting from MPTTModel, your model will also have a number of other fields: ``level``, ``lft``, ``rght``, and ``tree_id``. These fields are managed by the MPTT algorithm. Most of the time you won't need to use these fields directly. That ``MPTTMeta`` class adds some tweaks to ``django-mptt`` - in this case, just ``order_insertion_by``. This indicates the natural ordering of the data in the tree. Now create your table in the database:: python manage.py syncdb Create some data ---------------- Fire up a django shell:: python manage.py shell Now create some data to test:: from myapp.models import Genre rock = Genre.objects.create(name="Rock") blues = Genre.objects.create(name="Blues") Genre.objects.create(name="Hard Rock", parent=rock) Genre.objects.create(name="Pop Rock", parent=rock) Make a view ----------- This one's pretty simple for now. Add this lightweight view to your ``views.py``:: def show_genres(request): return render_to_response("genres.html", {'nodes':Genre.objects.all()}, context_instance=RequestContext(request)) And add a URL for it in ``urls.py``:: (r'^genres/$', 'myapp.views.show_genres'), Template -------- .. highlightlang:: html+django ``django-mptt`` includes some template tags for making this bit easy too. Create a template called ``genres.html`` in your template directory and put this in it:: {% load mptt_tags %} That recursetree tag will recursively render that template fragment for all the nodes. Try it out by going to ``/genres/``. There's more; `check out the docs`_ for custom admin-site stuff, more template tags, tree rebuild functions etc. Now you can stop thinking about how to do trees, and start making a great django app! .. _`check out the docs`: http://django-mptt.github.com/django-mptt/ django-mptt-0.8.0/docs/upgrade.rst0000644000076500000240000002010212633173225020333 0ustar cdestigterstaff00000000000000============= 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 ===== Dropped support for Django 1.5, Added support for 1.8 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Django 1.5 support has been removed since django 1.5 is not supported upstream any longer. Django 1.8 support has been added. Deprecated: Calling ``recursetree``/``cache_tree_children`` with incorrectly-ordered querysets ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Previously, when given a queryset argument, ``cache_tree_children`` called ``.order_by`` to ensure that the queryset was in the correct order. In 0.7, calling ``cache_tree_children`` with an incorrectly-ordered queryset will cause a deprecation warning. In 0.8, it will raise an error. This also applies to ``recursetree``, since it calls ``cache_tree_children``. This probably doesn't affect many usages, since the default ordering for mptt models will work fine. Minor: ``TreeManager.get_queryset`` no longer provided on Django < 1.6 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Django renamed ``get_query_set`` to ``get_queryset`` in Django 1.6. For backward compatibility django-mptt had both methods available for 1.4-1.5 users. This has been removed. You should use ``get_query_set`` on Django 1.4-1.5, and ``get_queryset`` if you're on 1.6+. Removed FeinCMSModelAdmin ~~~~~~~~~~~~~~~~~~~~~~~~~ Deprecated in 0.6.0, this has now been removed. 0.6.0 ===== mptt now requires Python 2.6+, and supports Python 3.2+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ mptt 0.6 drops support for both Python 2.4 and 2.5. This was done to make it easier to support Python 3, as well as support the new context managers (delay_mptt_updates and disable_mptt_updates). If you absolutely can't upgrade your Python version, you'll need to stick to mptt 0.5.5 until you can. No more implicit ``empty_label=True`` on form fields ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Until 0.5, ``TreeNodeChoiceField`` and ``TreeNodeMultipleChoiceField`` implicitly set ``empty_label=True``. This was around since a long time ago, for unknown reasons. It has been removed in 0.6.0 as it caused occasional headaches for users. If you were relying on this behavior, you'll need to explicitly pass ``empty_label=True`` to any of those fields you use, otherwise you will start seeing new '--------' choices appearing in them. Deprecated FeinCMSModelAdmin ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you were using ``mptt.admin.FeinCMSModelAdmin``, you should switch to using ``feincms.admin.tree_editor.TreeEditor`` instead, or you'll get a loud deprecation warning. 0.4.2 to 0.5.5 ============== ``TreeManager`` is now the default manager, ``YourModel.tree`` removed ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In 0.5, ``TreeManager`` now behaves just like a normal django manager. If you don't override anything, you'll now get a ``TreeManager`` by default (``.objects``.) Before 0.5, ``.tree`` was the default name for the ``TreeManager``. That's been removed, so we recommend updating your code to use ``.objects``. If you don't want to update ``.tree`` to ``.objects`` everywhere just yet, you should add an explicit ``TreeManager`` to your models:: objects = tree = TreeManager() ``save(raw=True)`` keyword argument removed ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In earlier versions, MPTTModel.save() had a ``raw`` keyword argument. If True, the MPTT fields would not be updated during the save. This (undocumented) argument has now been removed. ``_meta`` attributes moved to ``_mptt_meta`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In 0.4, we deprecated all these attributes on model._meta. These have now been removed:: MyModel._meta.left_attr MyModel._meta.right_attr MyModel._meta.tree_id_attr MyModel._meta.level_attr MyModel._meta.tree_manager_attr MyModel._meta.parent_attr MyModel._meta.order_insertion_by If you're still using any of these, you'll need to update by simply renaming ``_meta`` to ``_mptt_meta``. Running the tests ~~~~~~~~~~~~~~~~~ Tests are now run with:: cd tests/ ./runtests.sh The previous method (``python setup.py test``) no longer works since we switched to plain distutils. 0.3 to 0.4.2 ============ Model changes ~~~~~~~~~~~~~ MPTT attributes on ``MyModel._meta`` deprecated, moved to ``MyModel._mptt_meta`` ---------------------------------------------------------------------------------- Most people won't need to worry about this, but if you're using any of the following, note that these are deprecated and will be removed in 0.5:: MyModel._meta.left_attr MyModel._meta.right_attr MyModel._meta.tree_id_attr MyModel._meta.level_attr MyModel._meta.tree_manager_attr MyModel._meta.parent_attr MyModel._meta.order_insertion_by They'll continue to work as previously for now, but you should upgrade your code if you can. Simply replace ``_meta`` with ``_mptt_meta``. Use model inheritance where possible ------------------------------------ The preferred way to do model registration in ``django-mptt`` 0.4 is via model inheritance. Suppose you start with this:: class Node(models.Model): ... mptt.register(Node, order_insertion_by=['name'], parent_attr='padre') First, Make your model a subclass of ``MPTTModel``, instead of ``models.Model``:: from mptt.models import MPTTModel class Node(MPTTModel): ... Then remove your call to ``mptt.register()``. If you were passing it keyword arguments, you should add them to an ``MPTTMeta`` inner class on the model:: class Node(MPTTModel): ... class MPTTMeta: order_insertion_by = ['name'] parent_attr = 'padre' If necessary you can still use ``mptt.register``. It was removed in 0.4.0 but restored in 0.4.2, since people reported use cases that didn't work without it.) For instance, if you need to register models where the code isn't under your control, you'll need to use ``mptt.register()``. Behind the scenes, ``mptt.register()`` in 0.4 will actually add MPTTModel to ``Node.__bases__``, thus achieving the same result as subclassing ``MPTTModel``. If you're already inheriting from something other than ``Model``, that means multiple inheritance. You're probably all upgraded at this point :) A couple more notes for more complex scenarios: More complicated scenarios ~~~~~~~~~~~~~~~~~~~~~~~~~~ What if I'm already inheriting from something? ---------------------------------------------- If your model is already a subclass of an abstract model, you should use multiple inheritance:: class Node(MPTTModel, ParentModel): ... You should always put MPTTModel as the first model base. This is because there's some complicated metaclass stuff going on behind the scenes, and if Django's model metaclass gets called before the MPTT one, strange things can happen. Isn't multiple inheritance evil? Well, maybe. However, the `Django model docs`_ don't forbid this, and as long as your other model doesn't have conflicting methods, it should be fine. .. note:: As always when dealing with multiple inheritance, approach with a bit of caution. Our brief testing says it works, but if you find that the Django internals are somehow breaking this approach for you, please `create an issue`_ with specifics. .. _`create an issue`: http://github.com/django-mptt/django-mptt/issues .. _`Django model docs`: http://docs.djangoproject.com/en/dev/topics/db/models/#multiple-inheritance Compatibility with 0.3 ---------------------- ``MPTTModel`` was added in 0.4. If you're writing a library or reusable app that needs to work with 0.3, you should use the ``mptt.register()`` function instead, as above. django-mptt-0.8.0/docs/utilities.rst0000644000076500000240000000717012632165737020741 0ustar cdestigterstaff00000000000000================================ Utilities for working with trees ================================ .. contents:: :depth: 3 List/tree utilities =================== The ``mptt.utils`` module contains the following functions for working with and creating lists of model instances which represent trees. ``previous_current_next()`` --------------------------- From http://www.wordaligned.org/articles/zippy-triples-served-with-python Creates an iterator which returns (previous, current, next) triples, with ``None`` filling in when there is no previous or next available. This function is useful if you want to step through a tree one item at a time and you need to refer to the previous or next item in the tree. It is used in the implementation of `tree_item_iterator()`_. Required arguments ~~~~~~~~~~~~~~~~~~ ``items`` A list or other iterable item. ``tree_item_iterator()`` ------------------------ This function is used to implement the ``tree_info`` template filter, yielding two-tuples of (tree item, tree structure information ``dict``). See the ``tree_info`` documentation for more information. Required arguments ~~~~~~~~~~~~~~~~~~ ``items`` A list or iterable of model instances which represent a tree. Optional arguments ~~~~~~~~~~~~~~~~~~ ``ancestors`` Boolean. If ``True``, a list of unicode representations of the ancestors of the current node, in descending order (root node first, immediate parent last), will be added to the tree structure information ``dict` under the key ``'ancestors'``. ``drilldown_tree_for_node()`` ----------------------------- This function is used in the implementation of the ``drilldown_tree_for_node`` template tag. It creates an iterable which yields model instances representing a drilldown tree for a given node. A drilldown tree consists of a node's ancestors, itself and its immediate children, all in tree order. Optional arguments may be given to specify details of a relationship between the given node's class and another model class, for the purpose of adding related item counts to the node's children. Required arguments ~~~~~~~~~~~~~~~~~~ ``node`` A model instance which represents a node in a tree. Optional arguments ~~~~~~~~~~~~~~~~~~ ``rel_cls`` A model class which has a relationship to the node's class. ``rel_field`` The name of the field in ``rel_cls`` which holds the relationship to the node's class. ``count_attr`` The name of an attribute which should be added to each child of the node in the drilldown tree (if any), containing a count of how many instances of ``rel_cls`` are related to it through ``rel_field``. ``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. django-mptt-0.8.0/INSTALL0000644000076500000240000000075412462512456016271 0ustar cdestigterstaff00000000000000Thanks for downloading Django MPTT To install, run the following command inside this directory: python setup.py install Or if you'd prefer, you can simply place the included ``mptt`` directory somewhere on your PYTHONPATH, or symlink to it from somewhere on your PYTHONPATH; this is useful if you're working from a git checkout. Requires: - Python 2.6 or newer - Django 1.4.2 or newer You can obtain Python from http://www.python.org/ and Django from http://www.djangoproject.com/ django-mptt-0.8.0/LICENSE0000644000076500000240000000207712455555636016256 0ustar cdestigterstaff00000000000000Django MPTT ----------- Copyright (c) 2007, Jonathan Buchanan Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. django-mptt-0.8.0/MANIFEST.in0000644000076500000240000000037312455555636017004 0ustar cdestigterstaff00000000000000include INSTALL include LICENSE include MANIFEST.in include NOTES include README.rst recursive-include docs *.rst *.py Makefile recursive-include mptt *.json recursive-include tests * recursive-include mptt/templates * recursive-include mptt/locale * django-mptt-0.8.0/mptt/0000755000076500000240000000000012635633364016222 5ustar cdestigterstaff00000000000000django-mptt-0.8.0/mptt/__init__.py0000644000076500000240000000101312635631451020321 0ustar cdestigterstaff00000000000000from __future__ import unicode_literals VERSION = (0, 8, 0) def register(*args, **kwargs): """ Registers a model class as an MPTTModel, adding MPTT fields and adding MPTTModel to __bases__. This is equivalent to just subclassing MPTTModel, but works for an already-created model. """ from mptt.models import MPTTModelBase return MPTTModelBase.register(*args, **kwargs) class AlreadyRegistered(Exception): "Deprecated - don't use this anymore. It's never thrown, you don't need to catch it" django-mptt-0.8.0/mptt/admin.py0000644000076500000240000000652212635612714017665 0ustar cdestigterstaff00000000000000from __future__ import unicode_literals from django.conf import settings from django.contrib.admin.actions import delete_selected from django.contrib.admin.options import ModelAdmin from django.utils.encoding import force_text from django.utils.translation import ugettext as _ from mptt.forms import MPTTAdminForm, TreeNodeChoiceField from mptt.models import MPTTModel, TreeForeignKey __all__ = ('MPTTModelAdmin', 'MPTTAdminForm') IS_GRAPPELLI_INSTALLED = 'grappelli' in settings.INSTALLED_APPS class MPTTModelAdmin(ModelAdmin): """ A basic admin class that displays tree items according to their position in the tree. No extra editing functionality beyond what Django admin normally offers. """ if IS_GRAPPELLI_INSTALLED: change_list_template = 'admin/grappelli_mptt_change_list.html' else: change_list_template = 'admin/mptt_change_list.html' form = MPTTAdminForm def formfield_for_foreignkey(self, db_field, request, **kwargs): if issubclass(db_field.rel.to, MPTTModel) \ and not isinstance(db_field, TreeForeignKey) \ and db_field.name not in self.raw_id_fields: db = kwargs.get('using') 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), required=False) defaults.update(kwargs) kwargs = defaults return super(MPTTModelAdmin, self).formfield_for_foreignkey( db_field, request, **kwargs) def get_ordering(self, request): """ Changes the default ordering for changelists to tree-order. """ mptt_opts = self.model._mptt_meta return self.ordering or (mptt_opts.tree_id_attr, mptt_opts.left_attr) def delete_selected_tree(self, modeladmin, request, queryset): """ Deletes multiple instances and makes sure the MPTT fields get recalculated properly. (Because merely doing a bulk delete doesn't trigger the post_delete hooks.) """ # If this is True, the confirmation page has been displayed if request.POST.get('post'): n = 0 with queryset.model._tree_manager.delay_mptt_updates(): for obj in queryset: if self.has_delete_permission(request, obj): obj.delete() n += 1 obj_display = force_text(obj) self.log_deletion(request, obj, obj_display) self.message_user( request, _('Successfully deleted %(count)d items.') % {'count': n}) # Return None to display the change list page again return None else: # (ab)using the built-in action to display the confirmation page return delete_selected(self, request, queryset) def get_actions(self, request): actions = super(MPTTModelAdmin, self).get_actions(request) if 'delete_selected' in actions: actions['delete_selected'] = ( self.delete_selected_tree, 'delete_selected', _('Delete selected %(verbose_name_plural)s')) return actions django-mptt-0.8.0/mptt/exceptions.py0000644000076500000240000000065712462512456020761 0ustar cdestigterstaff00000000000000""" MPTT exceptions. """ from __future__ import unicode_literals class InvalidMove(Exception): """ An invalid node move was attempted. For example, attempting to make a node a child of itself. """ pass class CantDisableUpdates(Exception): """ User tried to disable updates on a model that doesn't support it (abstract, proxy or a multiple-inheritance subclass of an MPTTModel) """ pass django-mptt-0.8.0/mptt/fields.py0000644000076500000240000000306712627654434020052 0ustar cdestigterstaff00000000000000""" Model fields for working with trees. """ 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 class TreeForeignKey(models.ForeignKey): """ Extends the foreign key, but uses mptt's ``TreeNodeChoiceField`` as the default form field. This is useful if you are creating models that need automatically generated ModelForms to use the correct widgets. """ def formfield(self, **kwargs): """ Use MPTT's ``TreeNodeChoiceField`` """ kwargs.setdefault('form_class', TreeNodeChoiceField) return super(TreeForeignKey, self).formfield(**kwargs) class TreeOneToOneField(models.OneToOneField): def formfield(self, **kwargs): kwargs.setdefault('form_class', TreeNodeChoiceField) return super(TreeOneToOneField, self).formfield(**kwargs) class TreeManyToManyField(models.ManyToManyField): def formfield(self, **kwargs): kwargs.setdefault('form_class', TreeNodeMultipleChoiceField) return super(TreeManyToManyField, self).formfield(**kwargs) # South integration 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"]) django-mptt-0.8.0/mptt/forms.py0000644000076500000240000001575612634472470017736 0ustar cdestigterstaff00000000000000""" Form components for working with trees. """ from __future__ import unicode_literals from django import forms from django.forms.forms import NON_FIELD_ERRORS from django.utils.encoding import smart_text from django.utils.html import conditional_escape, mark_safe from django.utils.translation import ugettext_lazy as _ from mptt.exceptions import InvalidMove from mptt.settings import DEFAULT_LEVEL_INDICATOR __all__ = ( 'TreeNodeChoiceField', 'TreeNodeMultipleChoiceField', 'TreeNodePositionField', 'MoveNodeForm', ) # Fields ###################################################################### class TreeNodeChoiceFieldMixin(object): def __init__(self, queryset, *args, **kwargs): self.level_indicator = kwargs.pop('level_indicator', DEFAULT_LEVEL_INDICATOR) # if a queryset is supplied, enforce ordering if hasattr(queryset, 'model'): mptt_opts = queryset.model._mptt_meta queryset = queryset.order_by(mptt_opts.tree_id_attr, mptt_opts.left_attr) super(TreeNodeChoiceFieldMixin, self).__init__(queryset, *args, **kwargs) def _get_level_indicator(self, obj): level = getattr(obj, obj._mptt_meta.level_attr) return mark_safe(conditional_escape(self.level_indicator) * level) def label_from_instance(self, obj): """ Creates labels which represent the tree level of each node when generating option labels. """ level_indicator = self._get_level_indicator(obj) return mark_safe(level_indicator + ' ' + conditional_escape(smart_text(obj))) class TreeNodeChoiceField(TreeNodeChoiceFieldMixin, forms.ModelChoiceField): """A ModelChoiceField for tree nodes.""" class TreeNodeMultipleChoiceField(TreeNodeChoiceFieldMixin, forms.ModelMultipleChoiceField): """A ModelMultipleChoiceField for tree nodes.""" class TreeNodePositionField(forms.ChoiceField): """A ChoiceField for specifying position relative to another node.""" FIRST_CHILD = 'first-child' LAST_CHILD = 'last-child' LEFT = 'left' RIGHT = 'right' DEFAULT_CHOICES = ( (FIRST_CHILD, _('First child')), (LAST_CHILD, _('Last child')), (LEFT, _('Left sibling')), (RIGHT, _('Right sibling')), ) def __init__(self, *args, **kwargs): if 'choices' not in kwargs: kwargs['choices'] = self.DEFAULT_CHOICES super(TreeNodePositionField, self).__init__(*args, **kwargs) # Forms ####################################################################### class MoveNodeForm(forms.Form): """ A form which allows the user to move a given node from one location in its tree to another, with optional restriction of the nodes which are valid target nodes for the move. """ target = TreeNodeChoiceField(queryset=None) position = TreeNodePositionField() def __init__(self, node, *args, **kwargs): """ The ``node`` to be moved must be provided. The following keyword arguments are also accepted:: ``valid_targets`` Specifies a ``QuerySet`` of valid targets for the move. If not provided, valid targets will consist of everything other node of the same type, apart from the node itself and any descendants. For example, if you want to restrict the node to moving within its own tree, pass a ``QuerySet`` containing everything in the node's tree except itself and its descendants (to prevent invalid moves) and the root node (as a user could choose to make the node a sibling of the root node). ``target_select_size`` The size of the select element used for the target node. Defaults to ``10``. ``position_choices`` A tuple of allowed position choices and their descriptions. Defaults to ``TreeNodePositionField.DEFAULT_CHOICES``. ``level_indicator`` A string which will be used to represent a single tree level in the target options. """ self.node = node valid_targets = kwargs.pop('valid_targets', None) target_select_size = kwargs.pop('target_select_size', 10) position_choices = kwargs.pop('position_choices', None) level_indicator = kwargs.pop('level_indicator', None) super(MoveNodeForm, self).__init__(*args, **kwargs) opts = node._mptt_meta if valid_targets is None: valid_targets = node._tree_manager.exclude(**{ opts.tree_id_attr: getattr(node, opts.tree_id_attr), opts.left_attr + '__gte': getattr(node, opts.left_attr), opts.right_attr + '__lte': getattr(node, opts.right_attr), }) self.fields['target'].queryset = valid_targets self.fields['target'].widget.attrs['size'] = target_select_size if level_indicator: self.fields['target'].level_indicator = level_indicator if position_choices: self.fields['position'].choices = position_choices def save(self): """ Attempts to move the node using the selected target and position. If an invalid move is attempted, the related error message will be added to the form's non-field errors and the error will be re-raised. Callers should attempt to catch ``InvalidNode`` to redisplay the form with the error, should it occur. """ try: self.node.move_to(self.cleaned_data['target'], self.cleaned_data['position']) return self.node except InvalidMove as e: self.errors[NON_FIELD_ERRORS] = self.error_class(e) raise class MPTTAdminForm(forms.ModelForm): """ A form which validates that the chosen parent for a node isn't one of its descendants. """ def __init__(self, *args, **kwargs): super(MPTTAdminForm, self).__init__(*args, **kwargs) if self.instance and self.instance.pk: instance = self.instance opts = self._meta.model._mptt_meta parent_field = self.fields.get(opts.parent_attr) if parent_field: parent_qs = parent_field.queryset parent_qs = parent_qs.exclude( pk__in=instance.get_descendants( include_self=True ).values_list('pk', flat=True) ) parent_field.queryset = parent_qs def clean(self): cleaned_data = super(MPTTAdminForm, self).clean() opts = self._meta.model._mptt_meta parent = cleaned_data.get(opts.parent_attr) if self.instance and parent: if parent.is_descendant_of(self.instance, include_self=True): if opts.parent_attr not in self._errors: self._errors[opts.parent_attr] = self.error_class() self._errors[opts.parent_attr].append(_('Invalid parent')) del self.cleaned_data[opts.parent_attr] return cleaned_data django-mptt-0.8.0/mptt/locale/0000755000076500000240000000000012635633364017461 5ustar cdestigterstaff00000000000000django-mptt-0.8.0/mptt/locale/de/0000755000076500000240000000000012635633364020051 5ustar cdestigterstaff00000000000000django-mptt-0.8.0/mptt/locale/de/LC_MESSAGES/0000755000076500000240000000000012635633364021636 5ustar cdestigterstaff00000000000000django-mptt-0.8.0/mptt/locale/de/LC_MESSAGES/django.mo0000644000076500000240000000561112455555636023445 0ustar cdestigterstaff00000000000000l698)r;+"2' Z f)q@:6FNLEF(Co&5:KCk9GM1-C  @6 <w 8 I M7 D I H +]      %s tag requires either three, seven or eight arguments%s tag requires three argumentsA node may not be made a child of any of its descendants.A node may not be made a child of itself.A node may not be made a sibling of any of its descendants.A node may not be made a sibling of itself.An invalid position was given: %s.Cannot insert a node which has already been saved.First childLast childThe model %s has already been registered.drilldown_tree_for_node tag was given an invalid model field: %sdrilldown_tree_for_node tag was given an invalid model: %sfull_tree_for_model tag was given an invalid model: %sif eight arguments are given, fifth argument to %s tag must be 'count'if eight arguments are given, fourth argument to %s tag must be 'cumulative'if eight arguments are given, seventh argument to %s tag must be 'in'if seven arguments are given, fourth argument to %s tag must be 'with'if seven arguments are given, sixth argument to %s tag must be 'in'second argument to %s tag must be 'as'Project-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: POT-Creation-Date: 2009-09-23 14:44+0200 PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: FULL NAME Language-Team: LANGUAGE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit %s Tag benötigt entweder drei, sieben oder acht Argumente%s Tag benötigt drei ArgumenteEin Element kann nicht Unterelement eines seiner Unterlemente sein.Ein Element kann nicht Unterelement von sich selbst sein.Ein Element kann nicht ein Geschwister eines seiner Unterelemente sein.Ein Element kann nicht in ein Geschwister von sich selbst umgewandelt werden.Eine ungültige Position wurde angegeben: %s.Kann ein Element, welches schon gespeichert wurde, nicht einfügen.Erstes UnterelementLetztes Unterelement%s wurde schon registriert.drilldown_tree_for_node Tag bekam ein ungültiges Modellfeld: %sdrilldown_tree_for_node tag bekam ein ungültiges Modell: %sfull_tree_for_model Tag bekam ein ungültiges Modell: %swenn '%s' acht Argumente übergeben werden, muss das fünfte 'count' seinwenn '%s' acht Argumente übergeben werden, muss das vierte 'cumulative' seinwenn '%s' acht Argumente übergeben werden, muss das achte 'in' seinwenn '%s' sieben Argumente übergeben werden, muss das vierte 'with' seinwenn '%s' sieben Argumente übergeben werden, muss das sechste 'in' seinZweites Argument für %s Tag muss 'as' seindjango-mptt-0.8.0/mptt/locale/de/LC_MESSAGES/django.po0000644000076500000240000000772712455555636023462 0ustar cdestigterstaff00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2009-09-23 14:44+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: __init__.py:34 #, python-format msgid "The model %s has already been registered." msgstr "%s wurde schon registriert." #: forms.py:41 msgid "First child" msgstr "Erstes Unterelement" #: forms.py:42 msgid "Last child" msgstr "Letztes Unterelement" #: forms.py:43 msgid "Left sibling" msgstr "" #: forms.py:44 msgid "Right sibling" msgstr "" #: managers.py:121 msgid "Cannot insert a node which has already been saved." msgstr "Kann ein Element, welches schon gespeichert wurde, nicht einfügen." #: managers.py:306 managers.py:480 managers.py:516 managers.py:673 #, python-format msgid "An invalid position was given: %s." msgstr "Eine ungültige Position wurde angegeben: %s." #: managers.py:466 managers.py:653 msgid "A node may not be made a sibling of itself." msgstr "Ein Element kann nicht in ein Geschwister von sich selbst umgewandelt werden." #: managers.py:632 managers.py:753 msgid "A node may not be made a child of itself." msgstr "Ein Element kann nicht Unterelement von sich selbst sein." #: managers.py:634 managers.py:755 msgid "A node may not be made a child of any of its descendants." msgstr "Ein Element kann nicht Unterelement eines seiner Unterlemente sein." #: managers.py:655 msgid "A node may not be made a sibling of any of its descendants." msgstr "Ein Element kann nicht ein Geschwister eines seiner Unterelemente sein." #: templatetags/mptt_tags.py:23 #, python-format msgid "full_tree_for_model tag was given an invalid model: %s" msgstr "full_tree_for_model Tag bekam ein ungültiges Modell: %s" #: templatetags/mptt_tags.py:44 #, python-format msgid "drilldown_tree_for_node tag was given an invalid model: %s" msgstr "drilldown_tree_for_node tag bekam ein ungültiges Modell: %s" #: templatetags/mptt_tags.py:48 #, python-format msgid "drilldown_tree_for_node tag was given an invalid model field: %s" msgstr "drilldown_tree_for_node Tag bekam ein ungültiges Modellfeld: %s" #: templatetags/mptt_tags.py:72 #, python-format msgid "%s tag requires three arguments" msgstr "%s Tag benötigt drei Argumente" #: templatetags/mptt_tags.py:74 templatetags/mptt_tags.py:125 #, python-format msgid "second argument to %s tag must be 'as'" msgstr "Zweites Argument für %s Tag muss 'as' sein" #: templatetags/mptt_tags.py:123 #, python-format msgid "%s tag requires either three, seven or eight arguments" msgstr "%s Tag benötigt entweder drei, sieben oder acht Argumente" #: templatetags/mptt_tags.py:128 #, python-format msgid "if seven arguments are given, fourth argument to %s tag must be 'with'" msgstr "wenn '%s' sieben Argumente übergeben werden, muss das vierte 'with' sein" #: templatetags/mptt_tags.py:130 #, python-format msgid "if seven arguments are given, sixth argument to %s tag must be 'in'" msgstr "wenn '%s' sieben Argumente übergeben werden, muss das sechste 'in' sein" #: templatetags/mptt_tags.py:134 #, python-format msgid "" "if eight arguments are given, fourth argument to %s tag must be 'cumulative'" msgstr "" "wenn '%s' acht Argumente übergeben werden, muss das vierte 'cumulative' sein" #: templatetags/mptt_tags.py:136 #, python-format msgid "if eight arguments are given, fifth argument to %s tag must be 'count'" msgstr "wenn '%s' acht Argumente übergeben werden, muss das fünfte 'count' sein" #: templatetags/mptt_tags.py:138 #, python-format msgid "if eight arguments are given, seventh argument to %s tag must be 'in'" msgstr "wenn '%s' acht Argumente übergeben werden, muss das achte 'in' sein" django-mptt-0.8.0/mptt/locale/dk/0000755000076500000240000000000012635633364020057 5ustar cdestigterstaff00000000000000django-mptt-0.8.0/mptt/locale/dk/LC_MESSAGES/0000755000076500000240000000000012635633364021644 5ustar cdestigterstaff00000000000000django-mptt-0.8.0/mptt/locale/dk/LC_MESSAGES/django.mo0000644000076500000240000000570512455555636023457 0ustar cdestigterstaff000000000000006 @9`);+",2O    )@:6ZFLE%FkC&59S#;)@.X#:   % CF > : P UU L N LG 0     %s tag requires either three, seven or eight arguments%s tag requires three argumentsA node may not be made a child of any of its descendants.A node may not be made a child of itself.A node may not be made a sibling of any of its descendants.A node may not be made a sibling of itself.An invalid position was given: %s.Cannot insert a node which has already been saved.First childLast childLeft siblingRight siblingThe model %s has already been registered.drilldown_tree_for_node tag was given an invalid model field: %sdrilldown_tree_for_node tag was given an invalid model: %sfull_tree_for_model tag was given an invalid model: %sif eight arguments are given, fifth argument to %s tag must be 'count'if eight arguments are given, fourth argument to %s tag must be 'cumulative'if eight arguments are given, seventh argument to %s tag must be 'in'if seven arguments are given, fourth argument to %s tag must be 'with'if seven arguments are given, sixth argument to %s tag must be 'in'second argument to %s tag must be 'as'Project-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: POT-Creation-Date: 2009-09-11 10:38+0200 PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: FULL NAME Language-Team: LANGUAGE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit %s tagget påkræver enten tre, syv eller otte argumenter%s tagget påkræver tre argumenterEn knude må ikke være barn af nogle af dets efterkommere.En knude må ikke være barn af sig selv.En knude må ikke være søskend med nogle af dets efterkommere.En knude må ikke være søskend til sig selv.En ugyldig position blev givet: %s.Kan ikke indsættte en knude, der allerede er blevet gemt.Første barnSidste barnVenstre søskendHøjre søskendDin model er allerede registreret %s.drilldown_tree_for_node tagget blev givet et ugyldigt modelfelt: %sdrilldown_tree_for_node tagget blev givet en ugyldig model: %sfull_tree_for_model tagget blev givet en ugyldig model: %shvis otte argumenter gives, skal det fjedre argument til %s tagget være 'count'hvis otte argumenter gives, skal det fjedre argument til %s tagget være 'cumulative'hvis otte argumenter gives, skal det syvne argument til %s tagget være 'in'hvis syv argumenter gives, skal det fjerde argument til %s tagget være 'with'hvis syv argumenter gives, skal det sjette argument til %s tagget være 'in'det andet argument til %s tagget skal være 'as'django-mptt-0.8.0/mptt/locale/dk/LC_MESSAGES/django.po0000644000076500000240000000776112455555636023466 0ustar cdestigterstaff00000000000000# django-mptt in Danish. # django-mptt på Dansk. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # Michael Lind Mortensen , 2009. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2009-09-11 10:38+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: __init__.py:34 #, python-format msgid "The model %s has already been registered." msgstr "Din model er allerede registreret %s." #: forms.py:40 msgid "First child" msgstr "Første barn" #: forms.py:41 msgid "Last child" msgstr "Sidste barn" #: forms.py:42 msgid "Left sibling" msgstr "Venstre søskend" #: forms.py:43 msgid "Right sibling" msgstr "Højre søskend" #: managers.py:121 msgid "Cannot insert a node which has already been saved." msgstr "Kan ikke indsættte en knude, der allerede er blevet gemt." #: managers.py:237 managers.py:411 managers.py:447 managers.py:604 #, python-format msgid "An invalid position was given: %s." msgstr "En ugyldig position blev givet: %s." #: managers.py:397 managers.py:584 msgid "A node may not be made a sibling of itself." msgstr "En knude må ikke være søskend til sig selv." #: managers.py:563 managers.py:684 msgid "A node may not be made a child of itself." msgstr "En knude må ikke være barn af sig selv." #: managers.py:565 managers.py:686 msgid "A node may not be made a child of any of its descendants." msgstr "En knude må ikke være barn af nogle af dets efterkommere." #: managers.py:586 msgid "A node may not be made a sibling of any of its descendants." msgstr "En knude må ikke være søskend med nogle af dets efterkommere." #: templatetags/mptt_tags.py:23 #, python-format msgid "full_tree_for_model tag was given an invalid model: %s" msgstr "full_tree_for_model tagget blev givet en ugyldig model: %s" #: templatetags/mptt_tags.py:44 #, python-format msgid "drilldown_tree_for_node tag was given an invalid model: %s" msgstr "drilldown_tree_for_node tagget blev givet en ugyldig model: %s" #: templatetags/mptt_tags.py:48 #, python-format msgid "drilldown_tree_for_node tag was given an invalid model field: %s" msgstr "drilldown_tree_for_node tagget blev givet et ugyldigt modelfelt: %s" #: templatetags/mptt_tags.py:72 #, python-format msgid "%s tag requires three arguments" msgstr "%s tagget påkræver tre argumenter" #: templatetags/mptt_tags.py:74 templatetags/mptt_tags.py:125 #, python-format msgid "second argument to %s tag must be 'as'" msgstr "det andet argument til %s tagget skal være 'as'" #: templatetags/mptt_tags.py:123 #, python-format msgid "%s tag requires either three, seven or eight arguments" msgstr "%s tagget påkræver enten tre, syv eller otte argumenter" #: templatetags/mptt_tags.py:128 #, python-format msgid "if seven arguments are given, fourth argument to %s tag must be 'with'" msgstr "hvis syv argumenter gives, skal det fjerde argument til %s tagget være 'with'" #: templatetags/mptt_tags.py:130 #, python-format msgid "if seven arguments are given, sixth argument to %s tag must be 'in'" msgstr "hvis syv argumenter gives, skal det sjette argument til %s tagget være 'in'" #: templatetags/mptt_tags.py:134 #, python-format msgid "" "if eight arguments are given, fourth argument to %s tag must be 'cumulative'" msgstr "" "hvis otte argumenter gives, skal det fjedre argument til %s tagget være 'cumulative'" #: templatetags/mptt_tags.py:136 #, python-format msgid "if eight arguments are given, fifth argument to %s tag must be 'count'" msgstr "hvis otte argumenter gives, skal det fjedre argument til %s tagget være 'count'" #: templatetags/mptt_tags.py:138 #, python-format msgid "if eight arguments are given, seventh argument to %s tag must be 'in'" msgstr "hvis otte argumenter gives, skal det syvne argument til %s tagget være 'in'" django-mptt-0.8.0/mptt/locale/es/0000755000076500000240000000000012635633364020070 5ustar cdestigterstaff00000000000000django-mptt-0.8.0/mptt/locale/es/LC_MESSAGES/0000755000076500000240000000000012635633364021655 5ustar cdestigterstaff00000000000000django-mptt-0.8.0/mptt/locale/es/LC_MESSAGES/django.mo0000644000076500000240000000112612515002575023443 0ustar cdestigterstaff00000000000000<\p q{ k % FAdd childSuccessfully deleted %s items.View on siteProject-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: POT-Creation-Date: 2014-08-29 12:31+0200 PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: FULL NAME Language-Team: LANGUAGE Language: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n != 1); Añadir hijo%s registros eliminados correctamenteVer en el sitiodjango-mptt-0.8.0/mptt/locale/es/LC_MESSAGES/django.po0000644000076500000240000000704112515002575023450 0ustar cdestigterstaff00000000000000# SPANISH TRANSLATION # This file is distributed under the same license as the PACKAGE django-mptt. # Borja Fernandez 2014. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2014-08-29 12:31+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \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" #: admin.py:105 admin.py:107 msgid "Add child" msgstr "Añadir hijo" #: admin.py:113 admin.py:115 msgid "View on site" msgstr "Ver en el sitio" #: admin.py:127 #, python-format msgid "Successfully deleted %s items." msgstr "%s registros eliminados correctamente" #: admin.py:132 #, python-format msgid "Elimina seleccionado %(verbose_name_plural)s" msgstr "" #: forms.py:62 msgid "Primer hijo" msgstr "" #: forms.py:63 msgid "Ultimo hijo" msgstr "" #: forms.py:64 msgid "Hermano izquierda" msgstr "" #: forms.py:65 msgid "Hermano derecha" msgstr "" #: forms.py:183 msgid "Padre no válido" msgstr "" #: managers.py:413 msgid "No se puede insertar un nodo que ya ha sido salvado." msgstr "" #: managers.py:625 managers.py:798 managers.py:834 managers.py:998 #, python-format msgid "Se ha proporcionado una posición no válida: %s." msgstr "" #: managers.py:784 managers.py:978 msgid "Un nodo no se puede hacer hermano de si mismo." msgstr "" #: managers.py:957 managers.py:1082 msgid "Un nodo no puede ser hijo de si mismo" msgstr "" #: managers.py:959 managers.py:1084 msgid "Un nodo no puede ser hijo de alguno de sus descendientes." msgstr "" #: managers.py:980 msgid "Un nodo no puede ser hermano de uno de sus descendientes" msgstr "" #: models.py:271 msgid "register() espera un Django model class argument" msgstr "" #: templatetags/mptt_tags.py:32 #, python-format msgid "full_tree_for_model tag was given an invalid model: %s" msgstr "" #: templatetags/mptt_tags.py:56 #, python-format msgid "drilldown_tree_for_node tag modelo inválido: %s" msgstr "" #: templatetags/mptt_tags.py:63 #, python-format msgid "drilldown_tree_for_node tag campo del model inválido: %s" msgstr "" #: templatetags/mptt_tags.py:90 #, python-format msgid "%s tag requiere tres argumentos" msgstr "" #: templatetags/mptt_tags.py:92 templatetags/mptt_tags.py:147 #, python-format msgid "segundo argumento para %s tag debe ser 'as'" msgstr "" #: templatetags/mptt_tags.py:144 #, python-format msgid "%s tag requiere tres, siete o ocho argumentos" msgstr "" #: templatetags/mptt_tags.py:151 #, python-format msgid "Si se proporcionan 7 argumentos, el cuarto para %s debe ser 'with'" msgstr "" #: templatetags/mptt_tags.py:154 #, python-format msgid "Si se proporcionan siete argumentos, el sexto argumento para %s tag debe ser 'in'" msgstr "" #: templatetags/mptt_tags.py:159 #, python-format msgid "" "Si se proporcionan ocho argumentos, el cuarto argumento para %s tag debe ser 'cumulative'" msgstr "" #: templatetags/mptt_tags.py:162 #, python-format msgid "Si se proporcionan ocho argumentos, el quinto argumento para %s tag debe ser 'count'" msgstr "" #: templatetags/mptt_tags.py:165 #, python-format msgid "si se proporcionan ocho argumentos, el septimo argumento para %s tag debe ser 'in'" msgstr "" #: templatetags/mptt_tags.py:268 #, python-format msgid "El nodo %s no es el primero en orden de profundidad" msgstr "" #: templatetags/mptt_tags.py:345 #, python-format msgid "%s tag requiere un queryset" msgstr "" django-mptt-0.8.0/mptt/locale/es_AR/0000755000076500000240000000000012635633364020452 5ustar cdestigterstaff00000000000000django-mptt-0.8.0/mptt/locale/es_AR/LC_MESSAGES/0000755000076500000240000000000012635633364022237 5ustar cdestigterstaff00000000000000django-mptt-0.8.0/mptt/locale/es_AR/LC_MESSAGES/django.mo0000644000076500000240000000567112627654434024051 0ustar cdestigterstaff000000000000006 @9`);+",2O    )@:6ZFLE%FkC&p0!9'<C*5    % #5 >Y 5 1 M RN L M J< 1     %s tag requires either three, seven or eight arguments%s tag requires three argumentsA node may not be made a child of any of its descendants.A node may not be made a child of itself.A node may not be made a sibling of any of its descendants.A node may not be made a sibling of itself.An invalid position was given: %s.Cannot insert a node which has already been saved.First childLast childLeft siblingRight siblingThe model %s has already been registered.drilldown_tree_for_node tag was given an invalid model field: %sdrilldown_tree_for_node tag was given an invalid model: %sfull_tree_for_model tag was given an invalid model: %sif eight arguments are given, fifth argument to %s tag must be 'count'if eight arguments are given, fourth argument to %s tag must be 'cumulative'if eight arguments are given, seventh argument to %s tag must be 'in'if seven arguments are given, fourth argument to %s tag must be 'with'if seven arguments are given, sixth argument to %s tag must be 'in'second argument to %s tag must be 'as'Project-Id-Version: Report-Msgid-Bugs-To: POT-Creation-Date: 2014-08-29 12:31+0200 PO-Revision-Date: 2015-10-11 21:39-0300 Last-Translator: Gonzalo Bustos Language-Team: Spanish (Argentina) Language: es_AR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n != 1); X-Generator: Poedit 1.6.10 El tag %s requiere tres, siete u ocho argumentosEl tag %s require tres argumentosUn nodo no puede ser hijo de alguno de sus descendientes.Un nodo no puede ser hijo de sí mismo.Un nodo no puede ser hermano de alguno de sus descendientes.Un nodo no puede ser hermano de sí mismo.Posición inválida: %s.No se puede insertar un nodo que ya ha sido guardado.Primer hijoÚltimo hijoHermano izquierdoHermano derechoEl modelo %s ya ha sido registrado.Campo de modelo inválido para tag drilldown_tree_for_node: %sModelo inválido para tag drilldown_tree_for_node: %sModelo inválido para tag full_tree_for_model: %sSi se proporcionan ocho argumentos, el quinto para el tag %s debe ser 'count'Si se proporcionan ocho argumentos, el cuarto para el tag %s debe ser 'cumulative'Si se proporcionan ocho argumentos, el séptimo para el tag %s debe ser 'in'Si se proporcionan siete argumentos, el cuarto para el tag %s debe ser 'with'Si se proporcionan siete argumentos, el sexto para el tag %s debe ser 'in'El segundo argumento para el tag %s debe ser 'as'django-mptt-0.8.0/mptt/locale/es_AR/LC_MESSAGES/django.po0000644000076500000240000000763612627654434024057 0ustar cdestigterstaff00000000000000# 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'" django-mptt-0.8.0/mptt/locale/fr/0000755000076500000240000000000012635633364020070 5ustar cdestigterstaff00000000000000django-mptt-0.8.0/mptt/locale/fr/LC_MESSAGES/0000755000076500000240000000000012635633364021655 5ustar cdestigterstaff00000000000000django-mptt-0.8.0/mptt/locale/fr/LC_MESSAGES/django.mo0000644000076500000240000000705012455555636023463 0ustar cdestigterstaff00000000000000)Z6>9^);+ *"42W'     @:`6FLEfFC&7@^f$ 0+ "\ < ) > ,% R +a ?  5  - < M *^  I ? ;$ S` X O R] N (      %(count)s %(name)s was changed successfully.%(count)s %(name)s were changed successfully.%s tag requires a queryset%s tag requires either three, seven or eight arguments%s tag requires three argumentsA node may not be made a child of any of its descendants.A node may not be made a child of itself.A node may not be made a sibling of any of its descendants.A node may not be made a sibling of itself.Add childAn invalid position was given: %s.Cannot insert a node which has already been saved.Database errorDelete selected %(verbose_name_plural)sFirst childLast childLeft siblingRight siblingSuccessfully deleted %s items.View on sitedrilldown_tree_for_node tag was given an invalid model field: %sdrilldown_tree_for_node tag was given an invalid model: %sfull_tree_for_model tag was given an invalid model: %sif eight arguments are given, fifth argument to %s tag must be 'count'if eight arguments are given, fourth argument to %s tag must be 'cumulative'if eight arguments are given, seventh argument to %s tag must be 'in'if seven arguments are given, fourth argument to %s tag must be 'with'if seven arguments are given, sixth argument to %s tag must be 'in'second argument to %s tag must be 'as'Project-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: POT-Creation-Date: 2011-07-26 19:41-0400 PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: FULL NAME Language-Team: LANGUAGE Language: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit %(count)s %(name)s a été modifié avec succès.%(count)s %(name)s ont été modifiés avec succès.le tag %s requiert un « queryset »le tag %s requiert trois, sept ou huit argumentsle tag %s requiert trois argumentsUne node ne peut être l'enfant d'aucun de ces déscendants.Une node ne peut être son propre enfant.Une node ne peut être voisine avec aucun de ces déscendants.Une node ne peut être voisine d'elle-même.Ajouter enfantUne position invalide à été fournie: %s.Impossible d'insérer une node qui à déjà été sauvegardéeErreur de base de donnéesSupprimer les %(verbose_name_plural)s sélectionnéesPremier enfantDernier enfantVoisin de gaucheVoisin de droite%s itèmes on été supprimé avec succèsVoir sur le sitele tag drilldown_tree_for_node à reçu un champs de modèle invalide: %sle tag drilldown_tree_for_node à reçu un modèle invalide: %sle tag full_tree_for_model à reçu un modèle invalide: %ssi huit arguments sont fournis, le cinquième argument du tag %s doit être 'count'si huit arguments sont fournis, le quatrième argument du tag %s doit être 'cumulative'si huit arguments sont fournis, le septième argument du tag %s doit être 'in'si sept arguments sont fournis, le quatrième argument du tag %s doit être 'with'si sept arguments sont fournis, le sixième argument du tag %s doit être 'in'le second argument de %s doit être 'as'django-mptt-0.8.0/mptt/locale/fr/LC_MESSAGES/django.po0000644000076500000240000001140712455555636023467 0ustar cdestigterstaff00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2011-07-26 19:41-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: admin.py:91 msgid "Database error" msgstr "Erreur de base de données" #: admin.py:127 #, python-format msgid "%(count)s %(name)s was changed successfully." msgid_plural "%(count)s %(name)s were changed successfully." msgstr[0] "%(count)s %(name)s a été modifié avec succès." msgstr[1] "%(count)s %(name)s ont été modifiés avec succès." #: admin.py:197 admin.py:199 msgid "Add child" msgstr "Ajouter enfant" #: admin.py:205 admin.py:207 msgid "View on site" msgstr "Voir sur le site" #: admin.py:219 #, python-format msgid "Successfully deleted %s items." msgstr "%s itèmes on été supprimé avec succès" #: admin.py:224 #, python-format msgid "Delete selected %(verbose_name_plural)s" msgstr "Supprimer les %(verbose_name_plural)s sélectionnées" #: forms.py:62 msgid "First child" msgstr "Premier enfant" #: forms.py:63 msgid "Last child" msgstr "Dernier enfant" #: forms.py:64 msgid "Left sibling" msgstr "Voisin de gauche" #: forms.py:65 msgid "Right sibling" msgstr "Voisin de droite" #: managers.py:197 msgid "Cannot insert a node which has already been saved." msgstr "Impossible d'insérer une node qui à déjà été sauvegardée" #: managers.py:373 managers.py:545 managers.py:581 managers.py:736 #, python-format msgid "An invalid position was given: %s." msgstr "Une position invalide à été fournie: %s." #: managers.py:531 managers.py:716 msgid "A node may not be made a sibling of itself." msgstr "Une node ne peut être voisine d'elle-même." #: managers.py:695 managers.py:817 msgid "A node may not be made a child of itself." msgstr "Une node ne peut être son propre enfant." #: managers.py:697 managers.py:819 msgid "A node may not be made a child of any of its descendants." msgstr "Une node ne peut être l'enfant d'aucun de ces déscendants." #: managers.py:718 msgid "A node may not be made a sibling of any of its descendants." msgstr "Une node ne peut être voisine avec aucun de ces déscendants." #: templatetags/mptt_tags.py:28 #, python-format msgid "full_tree_for_model tag was given an invalid model: %s" msgstr "le tag full_tree_for_model à reçu un modèle invalide: %s" #: templatetags/mptt_tags.py:49 #, python-format msgid "drilldown_tree_for_node tag was given an invalid model: %s" msgstr "le tag drilldown_tree_for_node à reçu un modèle invalide: %s" #: templatetags/mptt_tags.py:53 #, python-format msgid "drilldown_tree_for_node tag was given an invalid model field: %s" msgstr "le tag drilldown_tree_for_node à reçu un champs de modèle invalide: %s" #: templatetags/mptt_tags.py:78 #, python-format msgid "%s tag requires three arguments" msgstr "le tag %s requiert trois arguments" #: templatetags/mptt_tags.py:80 templatetags/mptt_tags.py:132 #, python-format msgid "second argument to %s tag must be 'as'" msgstr "le second argument de %s doit être 'as'" #: templatetags/mptt_tags.py:130 #, python-format msgid "%s tag requires either three, seven or eight arguments" msgstr "le tag %s requiert trois, sept ou huit arguments" #: templatetags/mptt_tags.py:135 #, python-format msgid "if seven arguments are given, fourth argument to %s tag must be 'with'" msgstr "" "si sept arguments sont fournis, le quatrième argument du tag %s doit être " "'with'" #: templatetags/mptt_tags.py:137 #, python-format msgid "if seven arguments are given, sixth argument to %s tag must be 'in'" msgstr "" "si sept arguments sont fournis, le sixième argument du tag %s doit être 'in'" #: templatetags/mptt_tags.py:141 #, python-format msgid "" "if eight arguments are given, fourth argument to %s tag must be 'cumulative'" msgstr "" "si huit arguments sont fournis, le quatrième argument du tag %s doit être " "'cumulative'" #: templatetags/mptt_tags.py:143 #, python-format msgid "if eight arguments are given, fifth argument to %s tag must be 'count'" msgstr "" "si huit arguments sont fournis, le cinquième argument du tag %s doit être " "'count'" #: templatetags/mptt_tags.py:145 #, python-format msgid "if eight arguments are given, seventh argument to %s tag must be 'in'" msgstr "" "si huit arguments sont fournis, le septième argument du tag %s doit être 'in'" #: templatetags/mptt_tags.py:296 #, python-format msgid "%s tag requires a queryset" msgstr "le tag %s requiert un « queryset »" django-mptt-0.8.0/mptt/locale/mn/0000755000076500000240000000000012635633364020073 5ustar cdestigterstaff00000000000000django-mptt-0.8.0/mptt/locale/mn/LC_MESSAGES/0000755000076500000240000000000012635633364021660 5ustar cdestigterstaff00000000000000django-mptt-0.8.0/mptt/locale/mn/LC_MESSAGES/django.mo0000644000076500000240000000703112515002575023447 0ustar cdestigterstaff000000000000006 @9`);+",2O    )@:6ZFLE%FkC&uX6U#EyZJ e U     ;4 bp b ^6 o r lx l jR [     %s tag requires either three, seven or eight arguments%s tag requires three argumentsA node may not be made a child of any of its descendants.A node may not be made a child of itself.A node may not be made a sibling of any of its descendants.A node may not be made a sibling of itself.An invalid position was given: %s.Cannot insert a node which has already been saved.First childLast childLeft siblingRight siblingThe model %s has already been registered.drilldown_tree_for_node tag was given an invalid model field: %sdrilldown_tree_for_node tag was given an invalid model: %sfull_tree_for_model tag was given an invalid model: %sif eight arguments are given, fifth argument to %s tag must be 'count'if eight arguments are given, fourth argument to %s tag must be 'cumulative'if eight arguments are given, seventh argument to %s tag must be 'in'if seven arguments are given, fourth argument to %s tag must be 'with'if seven arguments are given, sixth argument to %s tag must be 'in'second argument to %s tag must be 'as'Project-Id-Version: Mongolian translatioin Report-Msgid-Bugs-To: POT-Creation-Date: 2009-09-23 14:44+0200 PO-Revision-Date: 2014-03-04 23:32+0800 Last-Translator: Bayarkhuu Bataa Language-Team: Bayarkhuu Bataa MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Generator: Poedit 1.5.7 %s нь гурав, долоо эсвэл найман аргумент шаардана%s нь гурван аргумент шаарданаЗангилаа өөрийнхөө үр садын хүү байж болохгүй.Зангилаа өөрийнхөө хүү байж болохгүй.Зангилаа өөрийнхөө үр садын ах дүү байж болохгүй.Зангилаа өөрийнхөө ах дүү байж болохгүй.Буруу байрлал: %s.Өмнө нь орсон зангилааг дахиж оруулж болохгүй.Эхний хүүСүүлийн хүүЗүүн ах дүүсБаруун ах дүүс%s модел аль хэдийнэ бүртгэгдсэн.drilldown_tree_for_node нь буруу моделд тодорхойлогдсон байна: %sdrilldown_tree_for_node нь буруу моделд тодорхойлогдсон байна: %sfull_tree_for_model нь буруу моделд тодорхойлогдсон байна: %s%s руу найман аргумент дамжуулсан бол 5 дахь нь 'count' байх ёстой%s руу найман аргумент дамжуулсан бол 4 дэх нь 'cumulative' байх ёстой%s руу найман аргумент дамжуулсан бол 7 дахь нь 'in' байх ёстой%s руу долоон аргумент дамжуулсан бол 4 дэх нь 'with' байх ёстой%s руу долоон аргумент дамжуулсан бол 6 дэх нь 'in' байх ёстой%s руу дамжуулах хоёрдох аргумент нь 'as байх ёстой'django-mptt-0.8.0/mptt/locale/mn/LC_MESSAGES/django.po0000644000076500000240000001103112515002575023445 0ustar cdestigterstaff00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: Mongolian translation\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2009-09-23 14:44+0200\n" "PO-Revision-Date: 2014-03-04 23:32+0800\n" "Last-Translator: Bayarkhuu Bataa \n" "Language-Team: Bayarkhuu Bataa \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 1.5.7\n" #: __init__.py:34 #, python-format msgid "The model %s has already been registered." msgstr "%s модел аль хэдийнэ бүртгэгдсэн." #: forms.py:41 msgid "First child" msgstr "Эхний хүү" #: forms.py:42 msgid "Last child" msgstr "Сүүлийн хүү" #: forms.py:43 msgid "Left sibling" msgstr "Зүүн ах дүүс" #: forms.py:44 msgid "Right sibling" msgstr "Баруун ах дүүс" #: managers.py:121 msgid "Cannot insert a node which has already been saved." msgstr "Өмнө нь орсон зангилааг дахиж оруулж болохгүй." #: managers.py:306 managers.py:480 managers.py:516 managers.py:673 #, python-format msgid "An invalid position was given: %s." msgstr "Буруу байрлал: %s." #: managers.py:466 managers.py:653 msgid "A node may not be made a sibling of itself." msgstr "Зангилаа өөрийнхөө ах дүү байж болохгүй." #: managers.py:632 managers.py:753 msgid "A node may not be made a child of itself." msgstr "Зангилаа өөрийнхөө хүү байж болохгүй." #: managers.py:634 managers.py:755 msgid "A node may not be made a child of any of its descendants." msgstr "Зангилаа өөрийнхөө үр садын хүү байж болохгүй." #: managers.py:655 msgid "A node may not be made a sibling of any of its descendants." msgstr "Зангилаа өөрийнхөө үр садын ах дүү байж болохгүй." #: templatetags/mptt_tags.py:23 #, python-format msgid "full_tree_for_model tag was given an invalid model: %s" msgstr "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 "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 "drilldown_tree_for_node нь буруу моделд тодорхойлогдсон байна: %s" #: templatetags/mptt_tags.py:72 #, python-format msgid "%s tag requires three arguments" msgstr "%s нь гурван аргумент шаардана" #: templatetags/mptt_tags.py:74 templatetags/mptt_tags.py:125 #, python-format msgid "second argument to %s tag must be 'as'" msgstr "%s руу дамжуулах хоёрдох аргумент нь 'as байх ёстой'" #: templatetags/mptt_tags.py:123 #, python-format msgid "%s tag requires either three, seven or eight arguments" msgstr "%s нь гурав, долоо эсвэл найман аргумент шаардана" #: templatetags/mptt_tags.py:128 #, python-format msgid "if seven arguments are given, fourth argument to %s tag must be 'with'" msgstr "%s руу долоон аргумент дамжуулсан бол 4 дэх нь 'with' байх ёстой" #: templatetags/mptt_tags.py:130 #, python-format msgid "if seven arguments are given, sixth argument to %s tag must be 'in'" msgstr "%s руу долоон аргумент дамжуулсан бол 6 дэх нь 'in' байх ёстой" #: templatetags/mptt_tags.py:134 #, python-format msgid "" "if eight arguments are given, fourth argument to %s tag must be 'cumulative'" msgstr "%s руу найман аргумент дамжуулсан бол 4 дэх нь 'cumulative' байх ёстой" #: templatetags/mptt_tags.py:136 #, python-format msgid "if eight arguments are given, fifth argument to %s tag must be 'count'" msgstr "%s руу найман аргумент дамжуулсан бол 5 дахь нь 'count' байх ёстой" #: templatetags/mptt_tags.py:138 #, python-format msgid "if eight arguments are given, seventh argument to %s tag must be 'in'" msgstr "%s руу найман аргумент дамжуулсан бол 7 дахь нь 'in' байх ёстой" django-mptt-0.8.0/mptt/locale/nb/0000755000076500000240000000000012635633364020060 5ustar cdestigterstaff00000000000000django-mptt-0.8.0/mptt/locale/nb/LC_MESSAGES/0000755000076500000240000000000012635633364021645 5ustar cdestigterstaff00000000000000django-mptt-0.8.0/mptt/locale/nb/LC_MESSAGES/django.mo0000644000076500000240000000754112455555636023460 0ustar cdestigterstaff00000000000000 +Z6$[9{);+ G"Q2t'     >cK8@:)6dFLE/FuC0&1@X= 1 9* (d < + ! 9& ` $m      b 6d A > 9 TV Y SQYO31/     %(count)s %(name)s was changed successfully.%(count)s %(name)s were changed successfully.%s tag requires either three, seven or eight arguments%s tag requires three argumentsA node may not be made a child of any of its descendants.A node may not be made a child of itself.A node may not be made a sibling of any of its descendants.A node may not be made a sibling of itself.Add childAn invalid position was given: %s.Cannot insert a node which has already been saved.Database errorDelete selected %(verbose_name_plural)sFirst childInvalid parentLast childLeft siblingRight siblingSuccessfully deleted %s items.View on site`tree_manager_attr` is deprecated; just instantiate a TreeManager as a normal manager on your modelcache_tree_children was passed nodes in the wrong order!drilldown_tree_for_node tag was given an invalid model field: %sdrilldown_tree_for_node tag was given an invalid model: %sfull_tree_for_model tag was given an invalid model: %sif eight arguments are given, fifth argument to %s tag must be 'count'if eight arguments are given, fourth argument to %s tag must be 'cumulative'if eight arguments are given, seventh argument to %s tag must be 'in'if seven arguments are given, fourth argument to %s tag must be 'with'if seven arguments are given, sixth argument to %s tag must be 'in'register() expects a Django model class argumentsecond argument to %s tag must be 'as'Project-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: POT-Creation-Date: 2012-05-13 22:53+0200 PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: FULL NAME Language-Team: LANGUAGE Language: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit %(count)s %(name)s ble endret.%(count)s %(name)s ble endret.%s-taggen trenger tre, sju eller åtte argumenter%s-taggen trenger tre argumenterEn node kan ikke være barn av noen av sine etterkommere.En node kan ikke være barn av seg selv.En node kan ikke være søsken av noen av sine etterkommere.En node kan ikke være søsken av seg selv.Legg til barnEn ugyldig posisjon ble gitt: %s.Kan ikke sette inn en node som allerede har blitt lagret.DatabasefeilSlett valgte %(verbose_name_plural)sFørste barnUgyldig forelderSiste barnVenstre søskenHøyre søskenSlettet %s elementer.Vis på nettsted`tree_manager_attr` er foreldet. Bare instansier en TreeManager som en normal manager på modellencache_tree_children ble gitt noder i feil rekkefølge!drilldown_tree_for_node-taggen ble gitt et ugyldig modellfelt: %sdrilldown_tree_for_node-taggen blev gitt en ugyldig modell: %sfull_tree_for_model-taggen ble gitt en ugyldig modell: %shvis åtte argumenter er gitt, må det fjerde argumentet til %s-taggen være 'count'hvis åtte argumenter er gitt, må det fjerde argumentet til %s-taggen være 'cumulative'hvis åtte argumenter er gitt, skal det sjuende argumentet til %s-taggen være 'in'hvis sju argumenter er gitt, må det fjerde argumentet til %s-taggen være 'with'hvis sju argumenter er gitt, må det sjette argumentet til %s-taggen være 'in'register() forventer et Django-modellklasseargumentdet andre argumentet til %s-taggen må være 'as'django-mptt-0.8.0/mptt/locale/nb/LC_MESSAGES/django.po0000644000076500000240000001261512455555636023461 0ustar cdestigterstaff00000000000000# django-mptt in Norwegian bokmål. # django-mptt på Bokmål. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # Sigurd Gartmann , 2012. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2012-05-13 22:53+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: admin.py:96 msgid "Database error" msgstr "Databasefeil" #: admin.py:132 #, python-format msgid "%(count)s %(name)s was changed successfully." msgid_plural "%(count)s %(name)s were changed successfully." msgstr[0] "%(count)s %(name)s ble endret." msgstr[1] "%(count)s %(name)s ble endret." #: admin.py:207 admin.py:209 msgid "Add child" msgstr "Legg til barn" #: admin.py:215 admin.py:217 msgid "View on site" msgstr "Vis på nettsted" #: admin.py:229 #, python-format msgid "Successfully deleted %s items." msgstr "Slettet %s elementer." #: admin.py:234 #, python-format msgid "Delete selected %(verbose_name_plural)s" msgstr "Slett valgte %(verbose_name_plural)s" #: forms.py:72 msgid "First child" msgstr "Første barn" #: forms.py:73 msgid "Last child" msgstr "Siste barn" #: forms.py:74 msgid "Left sibling" msgstr "Venstre søsken" #: forms.py:75 msgid "Right sibling" msgstr "Høyre søsken" #: forms.py:177 msgid "Invalid parent" msgstr "Ugyldig forelder" #: managers.py:206 msgid "Cannot insert a node which has already been saved." msgstr "Kan ikke sette inn en node som allerede har blitt lagret." #: managers.py:385 managers.py:557 managers.py:593 managers.py:748 #, python-format msgid "An invalid position was given: %s." msgstr "En ugyldig posisjon ble gitt: %s." #: managers.py:543 managers.py:728 msgid "A node may not be made a sibling of itself." msgstr "En node kan ikke være søsken av seg selv." #: managers.py:707 managers.py:829 msgid "A node may not be made a child of itself." msgstr "En node kan ikke være barn av seg selv." #: managers.py:709 managers.py:831 msgid "A node may not be made a child of any of its descendants." msgstr "En node kan ikke være barn av noen av sine etterkommere." #: managers.py:730 msgid "A node may not be made a sibling of any of its descendants." msgstr "En node kan ikke være søsken av noen av sine etterkommere." #: models.py:44 msgid "" "`tree_manager_attr` is deprecated; just instantiate a TreeManager as a " "normal manager on your model" msgstr "`tree_manager_attr` er foreldet. Bare instansier en TreeManager som en normal manager på modellen" #: models.py:199 msgid "register() expects a Django model class argument" msgstr "register() forventer et Django-modellklasseargument" #: templatetags/mptt_tags.py:28 #, python-format msgid "full_tree_for_model tag was given an invalid model: %s" msgstr "full_tree_for_model-taggen ble gitt en ugyldig modell: %s" #: templatetags/mptt_tags.py:52 #, python-format msgid "drilldown_tree_for_node tag was given an invalid model: %s" msgstr "drilldown_tree_for_node-taggen blev gitt en ugyldig modell: %s" #: templatetags/mptt_tags.py:59 #, python-format msgid "drilldown_tree_for_node tag was given an invalid model field: %s" msgstr "drilldown_tree_for_node-taggen ble gitt et ugyldig modellfelt: %s" #: templatetags/mptt_tags.py:86 #, python-format msgid "%s tag requires three arguments" msgstr "%s-taggen trenger tre argumenter" #: templatetags/mptt_tags.py:88 templatetags/mptt_tags.py:143 #, python-format msgid "second argument to %s tag must be 'as'" msgstr "det andre argumentet til %s-taggen må være 'as'" #: templatetags/mptt_tags.py:140 #, python-format msgid "%s tag requires either three, seven or eight arguments" msgstr "%s-taggen trenger tre, sju eller åtte argumenter" #: templatetags/mptt_tags.py:147 #, python-format msgid "if seven arguments are given, fourth argument to %s tag must be 'with'" msgstr "" "hvis sju argumenter er gitt, må det fjerde argumentet til %s-taggen være " "'with'" #: templatetags/mptt_tags.py:150 #, python-format msgid "if seven arguments are given, sixth argument to %s tag must be 'in'" msgstr "" "hvis sju argumenter er gitt, må det sjette argumentet til %s-taggen være 'in'" #: templatetags/mptt_tags.py:155 #, python-format msgid "" "if eight arguments are given, fourth argument to %s tag must be 'cumulative'" msgstr "" "hvis åtte argumenter er gitt, må det fjerde argumentet til %s-taggen være " "'cumulative'" #: templatetags/mptt_tags.py:158 #, python-format msgid "if eight arguments are given, fifth argument to %s tag must be 'count'" msgstr "" "hvis åtte argumenter er gitt, må det fjerde argumentet til %s-taggen være " "'count'" #: templatetags/mptt_tags.py:161 #, python-format msgid "if eight arguments are given, seventh argument to %s tag must be 'in'" msgstr "" "hvis åtte argumenter er gitt, skal det sjuende argumentet til %s-taggen være " "'in'" #: templatetags/mptt_tags.py:251 msgid "cache_tree_children was passed nodes in the wrong order!" msgstr "cache_tree_children ble gitt noder i feil rekkefølge!" #: templatetags/mptt_tags.py:313 #, fuzzy, python-format msgid "%s tag requires a queryset" msgstr "%s-taggen trenger tre argumenter" #~ msgid "The model %s has already been registered." #~ msgstr "Modellen %s har allerede blitt registrert." django-mptt-0.8.0/mptt/locale/pl/0000755000076500000240000000000012635633364020074 5ustar cdestigterstaff00000000000000django-mptt-0.8.0/mptt/locale/pl/LC_MESSAGES/0000755000076500000240000000000012635633364021661 5ustar cdestigterstaff00000000000000django-mptt-0.8.0/mptt/locale/pl/LC_MESSAGES/django.mo0000644000076500000240000000740012455555636023466 0ustar cdestigterstaff00000000000000)Z6>9^);+ *"42W'     @:`6FLEfFC&7^u " 4 S ;O Y 8  #/ :S  %     # 3 GE B > X ^h V YWx/     %(count)s %(name)s was changed successfully.%(count)s %(name)s were changed successfully.%s tag requires a queryset%s tag requires either three, seven or eight arguments%s tag requires three argumentsA node may not be made a child of any of its descendants.A node may not be made a child of itself.A node may not be made a sibling of any of its descendants.A node may not be made a sibling of itself.Add childAn invalid position was given: %s.Cannot insert a node which has already been saved.Database errorDelete selected %(verbose_name_plural)sFirst childLast childLeft siblingRight siblingSuccessfully deleted %s items.View on sitedrilldown_tree_for_node tag was given an invalid model field: %sdrilldown_tree_for_node tag was given an invalid model: %sfull_tree_for_model tag was given an invalid model: %sif eight arguments are given, fifth argument to %s tag must be 'count'if eight arguments are given, fourth argument to %s tag must be 'cumulative'if eight arguments are given, seventh argument to %s tag must be 'in'if seven arguments are given, fourth argument to %s tag must be 'with'if seven arguments are given, sixth argument to %s tag must be 'in'second argument to %s tag must be 'as'Project-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: POT-Creation-Date: 2011-10-17 16:06+0200 PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: BARTOSZ BIAŁY Language-Team: LANGUAGE Language: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2) %(count)s %(name)s został zmieniony.%(count)s %(name)s zostały zmienione.%(count)s %(name)s zostało zmienionych.tag %s wymaga argumentu 'queryset'tag %s wymaga trzech, siedmiu lub ośmiu argumentówtag %s wymaga trzech argumentówWęzeł nie może zostać węzłem potomnym żadnego ze swoich węzłów potomnych.Węzeł nie może zostać swoim własnym węzłem potomnym.Węzeł nie może zostać węzłem równożędnym żadnego ze swoich węzłów potomnych.Węzeł nie może zostać swoim węzłem równożędnym.Dodaj podelementPodano niewłaściwą pozycję: %s.Nie można wstawić węzła, który został już zapisany.Błąd bazy danychUsuń wybrane %(verbose_name_plural)sPierwszy podelementOstatni podelementLewy podelementPrawy podelementSkutecznie usinięto %s elementów.Pokaż na stroniedo tagu drilldown_tree_for_node przekazano niewłaściwe pole model: %sdo tagu drilldown_tree_for_node przekazano niewłaściwy model: %sdo tagu full_tree_for_model przekazano niewłaściwy model: %sjeżeli podano osiem argumentów, to piątym argumentem tagu %s musi być słowo 'count'jeżeli podano osiem argumentów, to czwartym argumentem tagu %s musi być słowo 'cumulative'jeżeli podano osiem argumentów, to siódmym argumentem tagu %s musi być słowo 'in'jeżeli podano siedem argumentów, to czwartym argumentem tagu %s musi być słowo 'with'jeżeli podano siedem argumentów, to szóstym argumentem tagu %s musi być słowo 'in'drugim argumentem tagu %s musi być słowo 'as'django-mptt-0.8.0/mptt/locale/pl/LC_MESSAGES/django.po0000644000076500000240000001204212455555636023467 0ustar cdestigterstaff00000000000000# django-mptt in Polish. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the django-mptt package. # Bartosz Biały , 2011. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2011-10-17 16:06+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: BARTOSZ BIAŁY \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " "|| n%100>=20) ? 1 : 2)\n" #: admin.py:91 msgid "Database error" msgstr "Błąd bazy danych" #: admin.py:127 #, python-format msgid "%(count)s %(name)s was changed successfully." msgid_plural "%(count)s %(name)s were changed successfully." msgstr[0] "%(count)s %(name)s został zmieniony." msgstr[1] "%(count)s %(name)s zostały zmienione." msgstr[2] "%(count)s %(name)s zostało zmienionych." #: admin.py:197 admin.py:199 msgid "Add child" msgstr "Dodaj podelement" #: admin.py:205 admin.py:207 msgid "View on site" msgstr "Pokaż na stronie" #: admin.py:219 #, python-format msgid "Successfully deleted %s items." msgstr "Skutecznie usinięto %s elementów." #: admin.py:224 #, python-format msgid "Delete selected %(verbose_name_plural)s" msgstr "Usuń wybrane %(verbose_name_plural)s" #: forms.py:65 msgid "First child" msgstr "Pierwszy podelement" #: forms.py:66 msgid "Last child" msgstr "Ostatni podelement" #: forms.py:67 msgid "Left sibling" msgstr "Lewy podelement" #: forms.py:68 msgid "Right sibling" msgstr "Prawy podelement" #: managers.py:200 msgid "Cannot insert a node which has already been saved." msgstr "Nie można wstawić węzła, który został już zapisany." #: managers.py:379 managers.py:551 managers.py:587 managers.py:742 #, python-format msgid "An invalid position was given: %s." msgstr "Podano niewłaściwą pozycję: %s." #: managers.py:537 managers.py:722 msgid "A node may not be made a sibling of itself." msgstr "Węzeł nie może zostać swoim węzłem równożędnym." #: managers.py:701 managers.py:823 msgid "A node may not be made a child of itself." msgstr "Węzeł nie może zostać swoim własnym węzłem potomnym." #: managers.py:703 managers.py:825 msgid "A node may not be made a child of any of its descendants." msgstr "Węzeł nie może zostać węzłem potomnym żadnego ze swoich węzłów potomnych." #: managers.py:724 msgid "A node may not be made a sibling of any of its descendants." msgstr "Węzeł nie może zostać węzłem równożędnym żadnego ze swoich węzłów potomnych." #: templatetags/mptt_tags.py:29 #, python-format msgid "full_tree_for_model tag was given an invalid model: %s" msgstr "do tagu full_tree_for_model przekazano niewłaściwy model: %s" #: templatetags/mptt_tags.py:50 #, python-format msgid "drilldown_tree_for_node tag was given an invalid model: %s" msgstr "do tagu drilldown_tree_for_node przekazano niewłaściwy model: %s" #: templatetags/mptt_tags.py:54 #, python-format msgid "drilldown_tree_for_node tag was given an invalid model field: %s" msgstr "do tagu drilldown_tree_for_node przekazano niewłaściwe pole model: %s" #: templatetags/mptt_tags.py:89 templatetags/mptt_tags.py:176 #, python-format msgid "%s tag requires three arguments" msgstr "tag %s wymaga trzech argumentów" #: templatetags/mptt_tags.py:91 templatetags/mptt_tags.py:143 #: templatetags/mptt_tags.py:179 #, python-format msgid "second argument to %s tag must be 'as'" msgstr "drugim argumentem tagu %s musi być słowo 'as'" #: templatetags/mptt_tags.py:141 #, python-format msgid "%s tag requires either three, seven or eight arguments" msgstr "tag %s wymaga trzech, siedmiu lub ośmiu argumentów" #: templatetags/mptt_tags.py:146 #, python-format msgid "if seven arguments are given, fourth argument to %s tag must be 'with'" msgstr "jeżeli podano siedem argumentów, to czwartym argumentem tagu %s musi być słowo 'with'" #: templatetags/mptt_tags.py:148 #, python-format msgid "if seven arguments are given, sixth argument to %s tag must be 'in'" msgstr "jeżeli podano siedem argumentów, to szóstym argumentem tagu %s musi być słowo 'in'" #: templatetags/mptt_tags.py:152 #, python-format msgid "" "if eight arguments are given, fourth argument to %s tag must be 'cumulative'" msgstr "jeżeli podano osiem argumentów, to czwartym argumentem tagu %s musi być słowo 'cumulative'" #: templatetags/mptt_tags.py:154 #, python-format msgid "if eight arguments are given, fifth argument to %s tag must be 'count'" msgstr "jeżeli podano osiem argumentów, to piątym argumentem tagu %s musi być słowo 'count'" #: templatetags/mptt_tags.py:156 #, python-format msgid "if eight arguments are given, seventh argument to %s tag must be 'in'" msgstr "jeżeli podano osiem argumentów, to siódmym argumentem tagu %s musi być słowo 'in'" #: templatetags/mptt_tags.py:329 #, python-format msgid "%s tag requires a queryset" msgstr "tag %s wymaga argumentu 'queryset'" django-mptt-0.8.0/mptt/locale/pt_BR/0000755000076500000240000000000012635633364020467 5ustar cdestigterstaff00000000000000django-mptt-0.8.0/mptt/locale/pt_BR/LC_MESSAGES/0000755000076500000240000000000012635633364022254 5ustar cdestigterstaff00000000000000django-mptt-0.8.0/mptt/locale/pt_BR/LC_MESSAGES/django.mo0000644000076500000240000000561312515002575024047 0ustar cdestigterstaff000000000000006 @9`);+",2O    )@:6ZFLE%FkC&C.a :';(Py3  7$ 7\ 3 P U No O L /[     %s tag requires either three, seven or eight arguments%s tag requires three argumentsA node may not be made a child of any of its descendants.A node may not be made a child of itself.A node may not be made a sibling of any of its descendants.A node may not be made a sibling of itself.An invalid position was given: %s.Cannot insert a node which has already been saved.First childLast childLeft siblingRight siblingThe model %s has already been registered.drilldown_tree_for_node tag was given an invalid model field: %sdrilldown_tree_for_node tag was given an invalid model: %sfull_tree_for_model tag was given an invalid model: %sif eight arguments are given, fifth argument to %s tag must be 'count'if eight arguments are given, fourth argument to %s tag must be 'cumulative'if eight arguments are given, seventh argument to %s tag must be 'in'if seven arguments are given, fourth argument to %s tag must be 'with'if seven arguments are given, sixth argument to %s tag must be 'in'second argument to %s tag must be 'as'Project-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: POT-Creation-Date: 2014-08-19 16:30-300 PO-Revision-Date: 2014-08-19 16:30-300 Last-Translator: Luis Fagundes Language-Team: LANGUAGE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A tag %s requer três, sete ou oito argumentosA tag %s requer três argumentosUm nó não pode ser filho de nenhum de seus descendentes.Um nó não pode ser filho de si mesmo.Um nó não pode ser irmão de nenhum de seus descendentes.Um nó não pode ser irmão de si mesmo.Posição inválida: %s.Não é possível inserir um nó que já foi salvo.Primeiro filhoÚltimo filhoIrmão à esquerdaIrmão à direitaO modelo %s foi registrado.drilldown_tree_for_node recebeu um modelo inválido: %sdrilldown_tree_for_node recebeu um modelo inválido: %sfull_tree_for_model recebeu um modelo inválido: %sse oito argumentos são dados, o quinto argumento para a tag %s deve ser 'count'se oito argumentos são dados, o quarto argumento para a tag %s deve ser 'cumulative'se oito argumentos são dados, o sétimo argumento para a tag %s deve ser 'in'se sete argumentos são dados, o quarto argumento para a tag %s deve ser 'with'se sete argumentos são dados, o sexto argumento para a tag %s deve ser 'in'o segundo argumento para a tag %s deve ser 'as'django-mptt-0.8.0/mptt/locale/pt_BR/LC_MESSAGES/django.po0000644000076500000240000000762412515002575024056 0ustar cdestigterstaff00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2014-08-19 16:30-300\n" "PO-Revision-Date: 2014-08-19 16:30-300\n" "Last-Translator: Luis Fagundes \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: __init__.py:34 #, python-format msgid "The model %s has already been registered." msgstr "O modelo %s foi registrado." #: forms.py:41 msgid "First child" msgstr "Primeiro filho" #: forms.py:42 msgid "Last child" msgstr "Último filho" #: forms.py:43 msgid "Left sibling" msgstr "Irmão à esquerda" #: forms.py:44 msgid "Right sibling" msgstr "Irmão à direita" #: managers.py:121 msgid "Cannot insert a node which has already been saved." msgstr "Não é possível inserir um nó que já foi salvo." #: managers.py:306 managers.py:480 managers.py:516 managers.py:673 #, python-format msgid "An invalid position was given: %s." msgstr "Posição inválida: %s." #: managers.py:466 managers.py:653 msgid "A node may not be made a sibling of itself." msgstr "Um nó não pode ser irmão de si mesmo." #: managers.py:632 managers.py:753 msgid "A node may not be made a child of itself." msgstr "Um nó não pode ser filho de si mesmo." #: managers.py:634 managers.py:755 msgid "A node may not be made a child of any of its descendants." msgstr "Um nó não pode ser filho de nenhum de seus descendentes." #: managers.py:655 msgid "A node may not be made a sibling of any of its descendants." msgstr "Um nó não pode ser irmão de nenhum de seus descendentes." #: templatetags/mptt_tags.py:23 #, python-format msgid "full_tree_for_model tag was given an invalid model: %s" msgstr "full_tree_for_model recebeu um modelo inválido: %s" #: templatetags/mptt_tags.py:44 #, python-format msgid "drilldown_tree_for_node tag was given an invalid model: %s" msgstr "drilldown_tree_for_node recebeu um modelo inválido: %s" #: templatetags/mptt_tags.py:48 #, python-format msgid "drilldown_tree_for_node tag was given an invalid model field: %s" msgstr "drilldown_tree_for_node recebeu um modelo inválido: %s" #: templatetags/mptt_tags.py:72 #, python-format msgid "%s tag requires three arguments" msgstr "A tag %s requer três argumentos" #: templatetags/mptt_tags.py:74 templatetags/mptt_tags.py:125 #, python-format msgid "second argument to %s tag must be 'as'" msgstr "o segundo argumento para a tag %s deve ser 'as'" #: templatetags/mptt_tags.py:123 #, python-format msgid "%s tag requires either three, seven or eight arguments" msgstr "A tag %s requer três, sete ou oito argumentos" #: templatetags/mptt_tags.py:128 #, python-format msgid "if seven arguments are given, fourth argument to %s tag must be 'with'" msgstr "se sete argumentos são dados, o quarto argumento para a tag %s deve ser 'with'" #: templatetags/mptt_tags.py:130 #, python-format msgid "if seven arguments are given, sixth argument to %s tag must be 'in'" msgstr "se sete argumentos são dados, o sexto argumento para a tag %s deve ser 'in'" #: templatetags/mptt_tags.py:134 #, python-format msgid "" "if eight arguments are given, fourth argument to %s tag must be 'cumulative'" msgstr "" "se oito argumentos são dados, o quarto argumento para a tag %s deve ser 'cumulative'" #: templatetags/mptt_tags.py:136 #, python-format msgid "if eight arguments are given, fifth argument to %s tag must be 'count'" msgstr "se oito argumentos são dados, o quinto argumento para a tag %s deve ser 'count'" #: templatetags/mptt_tags.py:138 #, python-format msgid "if eight arguments are given, seventh argument to %s tag must be 'in'" msgstr "se oito argumentos são dados, o sétimo argumento para a tag %s deve ser 'in'" django-mptt-0.8.0/mptt/locale/ru/0000755000076500000240000000000012635633364020107 5ustar cdestigterstaff00000000000000django-mptt-0.8.0/mptt/locale/ru/LC_MESSAGES/0000755000076500000240000000000012635633364021674 5ustar cdestigterstaff00000000000000django-mptt-0.8.0/mptt/locale/ru/LC_MESSAGES/django.mo0000644000076500000240000001043412515002575023464 0ustar cdestigterstaff00000000000000)Z6>9^);+ *"42W'     @:`6FLEfFC&7^ % K + [E O [ OM  6 d "Y 9|  !   , K [h R N}f~ogoD     %(count)s %(name)s was changed successfully.%(count)s %(name)s were changed successfully.%s tag requires a queryset%s tag requires either three, seven or eight arguments%s tag requires three argumentsA node may not be made a child of any of its descendants.A node may not be made a child of itself.A node may not be made a sibling of any of its descendants.A node may not be made a sibling of itself.Add childAn invalid position was given: %s.Cannot insert a node which has already been saved.Database errorDelete selected %(verbose_name_plural)sFirst childLast childLeft siblingRight siblingSuccessfully deleted %s items.View on sitedrilldown_tree_for_node tag was given an invalid model field: %sdrilldown_tree_for_node tag was given an invalid model: %sfull_tree_for_model tag was given an invalid model: %sif eight arguments are given, fifth argument to %s tag must be 'count'if eight arguments are given, fourth argument to %s tag must be 'cumulative'if eight arguments are given, seventh argument to %s tag must be 'in'if seven arguments are given, fourth argument to %s tag must be 'with'if seven arguments are given, sixth argument to %s tag must be 'in'second argument to %s tag must be 'as'Project-Id-Version: django-mptt Report-Msgid-Bugs-To: POT-Creation-Date: 2013-08-28 17:46+0400 PO-Revision-Date: 2013-08-28 19:49+0400 Last-Translator: Sergey Vishnikin Language-Team: Language: ru_RU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2); %(count)s %(name)s успешно изменен.%(count)s %(name)s успешно изменены.%(count)s %(name)s успешно изменено.для %s требуется queryset%s требует три, семь или восемь аргументов%s требует три аргументаЭлемент не может быть потомком своего наследника.Элемент не может быть потомком самому себе.Элемент не может быть дочерним своему наследнику.Элемент не может быть дочерним самому себе.Добавить потомкаБыла дана неверная позиция: %s.Невозможно добавить элемент, который уже был сохранён.Ошибка базы данныхУдалить выбранное %(verbose_name_plural)sПервый потомокПоследний потомокЛевый братПравый братУспешно удалено %s узлов.Перейти на сайтдля drilldown_tree_for_node было дано неверное поле модели: %sдля drilldown_tree_for_node была дана неверная модель: %sдля full_tree_for_model была дана неверная модель: %sесли дано восемь аргументов, то пятый аргумент для %s должен быть 'count'если дано восемь аргументов, то четвёртый аргумент для %s должен быть 'cumulative'если дано восемь аргументов, то седьмой аргумент для %s должен быть 'in'если дано семь аргументов, то четвёртый аргумент для %s должен быть 'with'если дано семь аргументов, то шестой для %s должен быть 'in'вторым аргуметом для %s должен быть 'as'django-mptt-0.8.0/mptt/locale/ru/LC_MESSAGES/django.po0000644000076500000240000001271712515002575023475 0ustar cdestigterstaff00000000000000# django-mptt in russian # This file is distributed under the same license as the django-mptt package. # Translators: # Sergey Vishnikin , 2013. msgid "" msgstr "" "Project-Id-Version: django-mptt\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2013-08-28 17:46+0400\n" "PO-Revision-Date: 2013-08-28 19:49+0400\n" "Last-Translator: Sergey Vishnikin \n" "Language-Team: \n" "Language: ru_RU\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" #: admin.py:91 msgid "Database error" msgstr "Ошибка базы данных" #: admin.py:127 #, python-format msgid "%(count)s %(name)s was changed successfully." msgid_plural "%(count)s %(name)s were changed successfully." msgstr[0] "%(count)s %(name)s успешно изменен." msgstr[1] "%(count)s %(name)s успешно изменены." msgstr[2] "%(count)s %(name)s успешно изменено." #: admin.py:197 admin.py:199 msgid "Add child" msgstr "Добавить потомка" #: admin.py:205 admin.py:207 msgid "View on site" msgstr "Перейти на сайт" #: admin.py:219 #, python-format msgid "Successfully deleted %s items." msgstr "Успешно удалено %s узлов." #: admin.py:224 #, python-format msgid "Delete selected %(verbose_name_plural)s" msgstr "Удалить выбранное %(verbose_name_plural)s" #: forms.py:65 msgid "First child" msgstr "Первый потомок" #: forms.py:66 msgid "Last child" msgstr "Последний потомок" #: forms.py:67 msgid "Left sibling" msgstr "Левый брат" #: forms.py:68 msgid "Right sibling" msgstr "Правый брат" #: managers.py:200 msgid "Cannot insert a node which has already been saved." msgstr "Невозможно добавить элемент, который уже был сохранён." #: managers.py:379 managers.py:551 managers.py:587 managers.py:742 #, python-format msgid "An invalid position was given: %s." msgstr "Была дана неверная позиция: %s." #: managers.py:537 managers.py:722 msgid "A node may not be made a sibling of itself." msgstr "Элемент не может быть дочерним самому себе." #: managers.py:701 managers.py:823 msgid "A node may not be made a child of itself." msgstr "Элемент не может быть потомком самому себе." #: managers.py:703 managers.py:825 msgid "A node may not be made a child of any of its descendants." msgstr "Элемент не может быть потомком своего наследника." #: managers.py:724 msgid "A node may not be made a sibling of any of its descendants." msgstr "Элемент не может быть дочерним своему наследнику." #: templatetags/mptt_tags.py:28 #, python-format msgid "full_tree_for_model tag was given an invalid model: %s" msgstr "для full_tree_for_model была дана неверная модель: %s" #: templatetags/mptt_tags.py:49 #, python-format msgid "drilldown_tree_for_node tag was given an invalid model: %s" msgstr "для drilldown_tree_for_node была дана неверная модель: %s" #: templatetags/mptt_tags.py:53 #, python-format msgid "drilldown_tree_for_node tag was given an invalid model field: %s" msgstr "для drilldown_tree_for_node было дано неверное поле модели: %s" #: templatetags/mptt_tags.py:78 #, python-format msgid "%s tag requires three arguments" msgstr "%s требует три аргумента" #: templatetags/mptt_tags.py:80 templatetags/mptt_tags.py:132 #, python-format msgid "second argument to %s tag must be 'as'" msgstr "вторым аргуметом для %s должен быть 'as'" #: templatetags/mptt_tags.py:130 #, python-format msgid "%s tag requires either three, seven or eight arguments" msgstr "%s требует три, семь или восемь аргументов" #: templatetags/mptt_tags.py:135 #, python-format msgid "if seven arguments are given, fourth argument to %s tag must be 'with'" msgstr "если дано семь аргументов, то четвёртый аргумент для %s должен быть 'with'" #: templatetags/mptt_tags.py:137 #, python-format msgid "if seven arguments are given, sixth argument to %s tag must be 'in'" msgstr "если дано семь аргументов, то шестой для %s должен быть 'in'" #: templatetags/mptt_tags.py:141 #, python-format msgid "if eight arguments are given, fourth argument to %s tag must be 'cumulative'" msgstr "если дано восемь аргументов, то четвёртый аргумент для %s должен быть 'cumulative'" #: templatetags/mptt_tags.py:143 #, python-format msgid "if eight arguments are given, fifth argument to %s tag must be 'count'" msgstr "если дано восемь аргументов, то пятый аргумент для %s должен быть 'count'" #: templatetags/mptt_tags.py:145 #, python-format msgid "if eight arguments are given, seventh argument to %s tag must be 'in'" msgstr "если дано восемь аргументов, то седьмой аргумент для %s должен быть 'in'" #: templatetags/mptt_tags.py:296 #, python-format msgid "%s tag requires a queryset" msgstr "для %s требуется queryset" django-mptt-0.8.0/mptt/managers.py0000644000076500000240000014121612635624522020372 0ustar cdestigterstaff00000000000000""" A custom manager for working with trees of objects. """ 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 _ from mptt.exceptions import CantDisableUpdates, InvalidMove from mptt.querysets import TreeQuerySet from mptt.utils import _get_tree_model from mptt.signals import node_moved __all__ = ('TreeManager',) COUNT_SUBQUERY = """( SELECT COUNT(*) FROM %(rel_table)s WHERE %(mptt_fk)s = %(mptt_table)s.%(mptt_rel_to)s )""" CUMULATIVE_COUNT_SUBQUERY = """( SELECT COUNT(*) FROM %(rel_table)s WHERE %(mptt_fk)s IN ( SELECT m2.%(mptt_rel_to)s FROM %(mptt_table)s m2 WHERE m2.%(tree_id)s = %(mptt_table)s.%(tree_id)s AND m2.%(left)s BETWEEN %(mptt_table)s.%(left)s AND %(mptt_table)s.%(right)s ) )""" COUNT_SUBQUERY_M2M = """( SELECT COUNT(*) FROM %(rel_table)s j INNER JOIN %(rel_m2m_table)s k ON j.%(rel_pk)s = k.%(rel_m2m_column)s WHERE k.%(mptt_fk)s = %(mptt_table)s.%(mptt_pk)s )""" CUMULATIVE_COUNT_SUBQUERY_M2M = """( SELECT COUNT(*) FROM %(rel_table)s j INNER JOIN %(rel_m2m_table)s k ON j.%(rel_pk)s = k.%(rel_m2m_column)s WHERE k.%(mptt_fk)s IN ( SELECT m2.%(mptt_pk)s FROM %(mptt_table)s m2 WHERE m2.%(tree_id)s = %(mptt_table)s.%(tree_id)s AND m2.%(left)s BETWEEN %(mptt_table)s.%(left)s AND %(mptt_table)s.%(right)s ) )""" class TreeManager(models.Manager.from_queryset(TreeQuerySet)): """ A manager for working with trees of objects. """ def contribute_to_class(self, model, name): super(TreeManager, self).contribute_to_class(model, name) if not model._meta.abstract: self.tree_model = _get_tree_model(model) self._base_manager = None if self.tree_model is not model: # _base_manager is the treemanager on tree_model self._base_manager = self.tree_model._tree_manager def get_queryset(self, *args, **kwargs): """ Ensures that this manager always returns nodes in tree order. """ 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): """ Returns a queryset containing either the descendants ``direction == desc`` or the ancestors ``direction == asc`` of a given queryset. This function is not meant to be called directly, although there is no harm in doing so. Instead, it should be used via ``get_queryset_descendants()`` and/or ``get_queryset_ancestors()``. 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 opts = queryset.model._mptt_meta filters = Q() e = 'e' if include_self else '' max_op = 'lt' + e min_op = 'gt' + e if direction == 'asc': max_attr = opts.left_attr min_attr = opts.right_attr elif direction == 'desc': max_attr = opts.right_attr min_attr = opts.left_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).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 ) 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), 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 min_max = {'min': min_val, 'max': max_val} elif lft == next_lft: 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(**{ 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'], }) return self.filter(filters) def get_queryset_descendants(self, queryset, include_self=False): """ Returns a queryset containing the descendants of all nodes in the given queryset. If ``include_self=True``, nodes in ``queryset`` will also be included in the result. """ return self._get_queryset_relatives(queryset, 'desc', include_self) def get_queryset_ancestors(self, queryset, include_self=False): """ Returns a queryset containing the ancestors of all nodes in the given queryset. If ``include_self=True``, nodes in ``queryset`` will also be included in the result. """ return self._get_queryset_relatives(queryset, 'asc', include_self) @contextlib.contextmanager def disable_mptt_updates(self): """ Context manager. Disables mptt updates. NOTE that this context manager causes inconsistencies! MPTT model methods are not guaranteed to return the correct results. When to use this method: If used correctly, this method can be used to speed up bulk updates. This doesn't do anything clever. It *will* mess up your tree. You should follow this method with a call to ``TreeManager.rebuild()`` to ensure your tree stays sane, and you should wrap both calls in a transaction. This is best for updates that span a large part of the table. If you are doing localised changes (one tree, or a few trees) consider using ``delay_mptt_updates``. If you are making only minor changes to your tree, just let the updates happen. Transactions: This doesn't enforce any transactional behavior. You should wrap this in a transaction to ensure database consistency. If updates are already disabled on the model, this is a noop. Usage:: with transaction.atomic(): with MyNode.objects.disable_mptt_updates(): ## bulk updates. MyNode.objects.rebuild() """ # Error cases: if self.model._meta.abstract: # an abstract model. Design decision needed - do we disable # updates for all concrete models that derive from this model? I # vote no - that's a bit implicit and it's a weird use-case # anyway. Open to further discussion :) raise CantDisableUpdates( "You can't disable/delay mptt updates on %s," " it's an abstract model" % self.model.__name__ ) elif self.model._meta.proxy: # a proxy model. disabling updates would implicitly affect other # models using the db table. Caller should call this on the # manager for the concrete model instead, to make the behavior # explicit. raise CantDisableUpdates( "You can't disable/delay mptt updates on %s, it's a proxy" " model. Call the concrete model instead." % self.model.__name__ ) elif self.tree_model is not self.model: # a multiple-inheritance child of an MPTTModel. Disabling # updates may affect instances of other models in the tree. raise CantDisableUpdates( "You can't disable/delay mptt updates on %s, it doesn't" " contain the mptt fields." % self.model.__name__ ) if not self.model._mptt_updates_enabled: # already disabled, noop. yield else: self.model._set_mptt_updates_enabled(False) try: yield finally: self.model._set_mptt_updates_enabled(True) @contextlib.contextmanager def delay_mptt_updates(self): """ Context manager. Delays mptt updates until the end of a block of bulk processing. NOTE that this context manager causes inconsistencies! MPTT model methods are not guaranteed to return the correct results until the end of the context block. When to use this method: If used correctly, this method can be used to speed up bulk updates. This is best for updates in a localised area of the db table, especially if all the updates happen in a single tree and the rest of the forest is left untouched. No subsequent rebuild is necessary. ``delay_mptt_updates`` does a partial rebuild of the modified trees (not the whole table). If used indiscriminately, this can actually be much slower than just letting the updates occur when they're required. The worst case occurs when every tree in the table is modified just once. That results in a full rebuild of the table, which can be *very* slow. If your updates will modify most of the trees in the table (not a small number of trees), you should consider using ``TreeManager.disable_mptt_updates``, as it does much fewer queries. Transactions: This doesn't enforce any transactional behavior. You should wrap this in a transaction to ensure database consistency. Exceptions: If an exception occurs before the processing of the block, delayed updates will not be applied. Usage:: with transaction.atomic(): with MyNode.objects.delay_mptt_updates(): ## bulk updates. """ with self.disable_mptt_updates(): if self.model._mptt_is_tracking: # already tracking, noop. yield else: self.model._mptt_start_tracking() try: yield except Exception: # stop tracking, but discard results self.model._mptt_stop_tracking() raise results = self.model._mptt_stop_tracking() partial_rebuild = self.partial_rebuild for tree_id in results: partial_rebuild(tree_id) @property def parent_attr(self): return self.model._mptt_meta.parent_attr @property def left_attr(self): return self.model._mptt_meta.left_attr @property def right_attr(self): return self.model._mptt_meta.right_attr @property def tree_id_attr(self): return self.model._mptt_meta.tree_id_attr @property def level_attr(self): return self.model._mptt_meta.level_attr def _translate_lookups(self, **lookups): new_lookups = {} join_parts = '__'.join for k, v in lookups.items(): parts = k.split('__') new_parts = [] new_parts__append = new_parts.append for part in parts: new_parts__append(getattr(self, part + '_attr', part)) new_lookups[join_parts(new_parts)] = v return new_lookups def _mptt_filter(self, qs=None, **filters): """ Like ``self.filter()``, but translates name-agnostic filters for MPTT fields. """ if self._base_manager: return self._base_manager._mptt_filter(qs=qs, **filters) if qs is None: qs = self return qs.filter(**self._translate_lookups(**filters)) def _mptt_update(self, qs=None, **items): """ Like ``self.update()``, but translates name-agnostic MPTT fields. """ if self._base_manager: return self._base_manager._mptt_update(qs=qs, **items) if qs is None: qs = self return qs.update(**self._translate_lookups(**items)) def _get_connection(self, **hints): return connections[router.db_for_write(self.model, **hints)] def add_related_count(self, queryset, rel_model, rel_field, count_attr, cumulative=False): """ Adds a related item count to a given ``QuerySet`` using its ``extra`` method, for a ``Model`` class which has a relation to this ``Manager``'s ``Model`` class. Arguments: ``rel_model`` A ``Model`` class which has a relation to this `Manager``'s ``Model`` class. ``rel_field`` The name of the field in ``rel_model`` which holds the relation. ``count_attr`` The name of an attribute which should be added to each item in this ``QuerySet``, containing a count of how many instances of ``rel_model`` are related to it through ``rel_field``. ``cumulative`` If ``True``, the count will be for each item and all of its descendants, otherwise it will be for each item itself. """ connection = self._get_connection() qn = connection.ops.quote_name meta = self.model._meta mptt_field = rel_model._meta.get_field(rel_field) if isinstance(mptt_field, ManyToManyField): if cumulative: subquery = CUMULATIVE_COUNT_SUBQUERY_M2M % { 'rel_table': qn(rel_model._meta.db_table), 'rel_pk': qn(rel_model._meta.pk.column), 'rel_m2m_table': qn(mptt_field.m2m_db_table()), 'rel_m2m_column': qn(mptt_field.m2m_column_name()), 'mptt_fk': qn(mptt_field.m2m_reverse_name()), 'mptt_table': qn(self.tree_model._meta.db_table), 'mptt_pk': qn(meta.pk.column), 'tree_id': qn(meta.get_field(self.tree_id_attr).column), 'left': qn(meta.get_field(self.left_attr).column), 'right': qn(meta.get_field(self.right_attr).column), } else: subquery = COUNT_SUBQUERY_M2M % { 'rel_table': qn(rel_model._meta.db_table), 'rel_pk': qn(rel_model._meta.pk.column), 'rel_m2m_table': qn(mptt_field.m2m_db_table()), 'rel_m2m_column': qn(mptt_field.m2m_column_name()), 'mptt_fk': qn(mptt_field.m2m_reverse_name()), 'mptt_table': qn(self.tree_model._meta.db_table), 'mptt_pk': qn(meta.pk.column), } else: if cumulative: subquery = CUMULATIVE_COUNT_SUBQUERY % { 'rel_table': qn(rel_model._meta.db_table), 'mptt_fk': qn(rel_model._meta.get_field(rel_field).column), 'mptt_table': qn(self.tree_model._meta.db_table), 'mptt_rel_to': qn(mptt_field.rel.field_name), 'tree_id': qn(meta.get_field(self.tree_id_attr).column), 'left': qn(meta.get_field(self.left_attr).column), 'right': qn(meta.get_field(self.right_attr).column), } else: subquery = COUNT_SUBQUERY % { 'rel_table': qn(rel_model._meta.db_table), 'mptt_fk': qn(rel_model._meta.get_field(rel_field).column), 'mptt_table': qn(self.tree_model._meta.db_table), 'mptt_rel_to': qn(mptt_field.rel.field_name), } return queryset.extra(select={count_attr: subquery}) def insert_node(self, node, target, position='last-child', save=False, allow_existing_pk=False, refresh_target=True): """ Sets up the tree state for ``node`` (which has not yet been inserted into in the database) so it will be positioned relative to a given ``target`` node as specified by ``position`` (when appropriate) it is inserted, with any neccessary space already having been made for it. A ``target`` of ``None`` indicates that ``node`` should be the last root node. If ``save`` is ``True``, ``node``'s ``save()`` method will be called before it is returned. NOTE: This is a low-level method; it does NOT respect ``MPTTMeta.order_insertion_by``. In most cases you should just set the node's parent and let mptt call this during save. """ if self._base_manager: return self._base_manager.insert_node( node, target, position=position, save=save, allow_existing_pk=allow_existing_pk) if node.pk and not allow_existing_pk and self.filter(pk=node.pk).exists(): raise ValueError(_('Cannot insert a node which has already been saved.')) if target is None: tree_id = self._get_next_tree_id() setattr(node, self.left_attr, 1) setattr(node, self.right_attr, 2) setattr(node, self.level_attr, 0) setattr(node, self.tree_id_attr, tree_id) setattr(node, self.parent_attr, None) elif target.is_root_node() and position in ['left', 'right']: if refresh_target: # Ensure mptt values on target are not stale. target._mptt_refresh() target_tree_id = getattr(target, self.tree_id_attr) if position == 'left': tree_id = target_tree_id space_target = target_tree_id - 1 else: tree_id = target_tree_id + 1 space_target = target_tree_id self._create_tree_space(space_target) setattr(node, self.left_attr, 1) setattr(node, self.right_attr, 2) setattr(node, self.level_attr, 0) setattr(node, self.tree_id_attr, tree_id) setattr(node, self.parent_attr, None) else: setattr(node, self.left_attr, 0) setattr(node, self.level_attr, 0) if refresh_target: # Ensure mptt values on target are not stale. target._mptt_refresh() space_target, level, left, parent, right_shift = \ self._calculate_inter_tree_move_values(node, target, position) tree_id = getattr(target, self.tree_id_attr) self._create_space(2, space_target, tree_id) setattr(node, self.left_attr, -left) setattr(node, self.right_attr, -left + 1) setattr(node, self.level_attr, -level) setattr(node, self.tree_id_attr, tree_id) setattr(node, self.parent_attr, parent) if parent: self._post_insert_update_cached_parent_right(parent, right_shift) if save: node.save() return node 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, save=save, refresh_target=refresh_target) if self.tree_model._mptt_is_tracking: # delegate to insert_node and clean up the gaps later. return self.insert_node(node, target, position=position, save=save, allow_existing_pk=True, refresh_target=refresh_target) else: if target is None: if node.is_child_node(): self._make_child_root_node(node) elif target.is_root_node() and position in ('left', 'right'): self._make_sibling_of_root_node(node, target, position) else: if node.is_root_node(): self._move_root_node(node, target, position) else: self._move_child_node(node, target, position) def move_node(self, node, target, position='last-child'): """ Moves ``node`` relative to a given ``target`` node as specified by ``position`` (when appropriate), by examining both nodes and calling the appropriate method to perform the move. A ``target`` of ``None`` indicates that ``node`` should be turned into a root node. Valid values for ``position`` are ``'first-child'``, ``'last-child'``, ``'left'`` or ``'right'``. ``node`` will be modified to reflect its new tree state in the database. This method explicitly checks for ``node`` being made a sibling of a root node, as this is a special case due to our use of tree ids to order root nodes. NOTE: This is a low-level method; it does NOT respect ``MPTTMeta.order_insertion_by``. In most cases you should just 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): """ Returns the root node of the tree with the given id. """ if self._base_manager: return self._base_manager.root_node(tree_id) return self._mptt_filter(tree_id=tree_id, parent=None).get() def root_nodes(self): """ Creates a ``QuerySet`` containing root nodes. """ if self._base_manager: return self._base_manager.root_nodes() return self._mptt_filter(parent=None) def rebuild(self): """ Rebuilds all trees in the database table using `parent` link. """ if self._base_manager: return self._base_manager.rebuild() opts = self.model._mptt_meta qs = self._mptt_filter(parent=None) if opts.order_insertion_by: qs = qs.order_by(*opts.order_insertion_by) pks = qs.values_list('pk', flat=True) rebuild_helper = self._rebuild_helper idx = 0 for pk in pks: idx += 1 rebuild_helper(pk, 1, idx) rebuild.alters_data = True def partial_rebuild(self, tree_id): """ Partially rebuilds a tree i.e. It rebuilds only the tree with given ``tree_id`` in database table using ``parent`` link. """ if self._base_manager: return self._base_manager.partial_rebuild(tree_id) opts = self.model._mptt_meta qs = self._mptt_filter(parent=None, tree_id=tree_id) if opts.order_insertion_by: qs = qs.order_by(*opts.order_insertion_by) pks = qs.values_list('pk', flat=True) if not pks: return if len(pks) > 1: raise RuntimeError( "More than one root node with tree_id %d. That's invalid," " do a full rebuild." % tree_id) self._rebuild_helper(pks[0], 1, tree_id) def _rebuild_helper(self, pk, left, tree_id, level=0): opts = self.model._mptt_meta right = left + 1 qs = self._mptt_filter(parent__pk=pk) if opts.order_insertion_by: qs = qs.order_by(*opts.order_insertion_by) child_ids = qs.values_list('pk', flat=True) rebuild_helper = self._rebuild_helper for child_id in child_ids: right = rebuild_helper(child_id, right, tree_id, level + 1) qs = self.model._default_manager.filter(pk=pk) self._mptt_update( qs, left=left, right=right, level=level, tree_id=tree_id ) return right + 1 def _post_insert_update_cached_parent_right(self, instance, right_shift, seen=None): setattr(instance, self.right_attr, getattr(instance, self.right_attr) + right_shift) attr = '_%s_cache' % self.parent_attr if hasattr(instance, attr): parent = getattr(instance, attr) if parent: if not seen: seen = set() seen.add(instance) if parent in seen: # detect infinite recursion and throw an error raise InvalidMove self._post_insert_update_cached_parent_right(parent, right_shift, seen=seen) def _calculate_inter_tree_move_values(self, node, target, position): """ Calculates values required when moving ``node`` relative to ``target`` as specified by ``position``. """ left = getattr(node, self.left_attr) level = getattr(node, self.level_attr) target_left = getattr(target, self.left_attr) target_right = getattr(target, self.right_attr) target_level = getattr(target, self.level_attr) if position == 'last-child' or position == 'first-child': if position == 'last-child': space_target = target_right - 1 else: space_target = target_left level_change = level - target_level - 1 parent = target elif position == 'left' or position == 'right': if position == 'left': space_target = target_left - 1 else: space_target = target_right level_change = level - target_level parent = getattr(target, self.parent_attr) else: raise ValueError(_('An invalid position was given: %s.') % position) left_right_change = left - space_target - 1 right_shift = 0 if parent: right_shift = 2 * (node.get_descendant_count() + 1) return space_target, level_change, left_right_change, parent, right_shift def _close_gap(self, size, target, tree_id): """ Closes a gap of a certain ``size`` after the given ``target`` point in the tree identified by ``tree_id``. """ self._manage_space(-size, target, tree_id) def _create_space(self, size, target, tree_id): """ Creates a space of a certain ``size`` after the given ``target`` point in the tree identified by ``tree_id``. """ self._manage_space(size, target, tree_id) def _create_tree_space(self, target_tree_id, num_trees=1): """ Creates space for a new tree by incrementing all tree ids greater than ``target_tree_id``. """ qs = self._mptt_filter(tree_id__gt=target_tree_id) self._mptt_update(qs, tree_id=F(self.tree_id_attr) + num_trees) self.tree_model._mptt_track_tree_insertions(target_tree_id + 1, num_trees) def _get_next_tree_id(self): """ Determines the next largest unused tree id for the tree managed by this manager. """ max_tree_id = list(self.aggregate(Max(self.tree_id_attr)).values())[0] max_tree_id = max_tree_id or 0 return max_tree_id + 1 def _inter_tree_move_and_close_gap( self, node, level_change, left_right_change, new_tree_id, parent_pk=None): """ Removes ``node`` from its current tree, with the given set of changes being applied to ``node`` and its descendants, closing the gap left by moving ``node`` as it does so. If ``parent_pk`` is ``None``, this indicates that ``node`` is being moved to a brand new tree as its root node, and will thus have its parent field set to ``NULL``. Otherwise, ``node`` will have ``parent_pk`` set for its parent field. """ connection = self._get_connection(instance=node) qn = connection.ops.quote_name opts = self.model._meta inter_tree_move_query = """ UPDATE %(table)s SET %(level)s = CASE WHEN %(left)s >= %%s AND %(left)s <= %%s THEN %(level)s - %%s ELSE %(level)s END, %(tree_id)s = CASE WHEN %(left)s >= %%s AND %(left)s <= %%s THEN %%s ELSE %(tree_id)s END, %(left)s = CASE WHEN %(left)s >= %%s AND %(left)s <= %%s THEN %(left)s - %%s WHEN %(left)s > %%s THEN %(left)s - %%s ELSE %(left)s END, %(right)s = CASE WHEN %(right)s >= %%s AND %(right)s <= %%s THEN %(right)s - %%s WHEN %(right)s > %%s THEN %(right)s - %%s ELSE %(right)s END, %(parent)s = CASE WHEN %(pk)s = %%s THEN %(new_parent)s ELSE %(parent)s END WHERE %(tree_id)s = %%s""" % { 'table': qn(self.tree_model._meta.db_table), 'level': qn(opts.get_field(self.level_attr).column), 'left': qn(opts.get_field(self.left_attr).column), 'tree_id': qn(opts.get_field(self.tree_id_attr).column), 'right': qn(opts.get_field(self.right_attr).column), 'parent': qn(opts.get_field(self.parent_attr).column), 'pk': qn(opts.pk.column), 'new_parent': parent_pk is None and 'NULL' or '%s', } left = getattr(node, self.left_attr) right = getattr(node, self.right_attr) gap_size = right - left + 1 gap_target_left = left - 1 params = [ left, right, level_change, left, right, new_tree_id, left, right, left_right_change, gap_target_left, gap_size, left, right, left_right_change, gap_target_left, gap_size, node.pk, getattr(node, self.tree_id_attr) ] if parent_pk is not None: params.insert(-1, parent_pk) cursor = connection.cursor() cursor.execute(inter_tree_move_query, params) def _make_child_root_node(self, node, new_tree_id=None): """ Removes ``node`` from its tree, making it the root node of a new tree. If ``new_tree_id`` is not specified a new tree id will be generated. ``node`` will be modified to reflect its new tree state in the database. """ left = getattr(node, self.left_attr) right = getattr(node, self.right_attr) level = getattr(node, self.level_attr) if not new_tree_id: new_tree_id = self._get_next_tree_id() left_right_change = left - 1 self._inter_tree_move_and_close_gap(node, level, left_right_change, new_tree_id) # Update the node to be consistent with the updated # tree in the database. setattr(node, self.left_attr, left - left_right_change) setattr(node, self.right_attr, right - left_right_change) setattr(node, self.level_attr, 0) setattr(node, self.tree_id_attr, new_tree_id) setattr(node, self.parent_attr, None) node._mptt_cached_fields[self.parent_attr] = None def _make_sibling_of_root_node(self, node, target, position): """ Moves ``node``, making it a sibling of the given ``target`` root node as specified by ``position``. ``node`` will be modified to reflect its new tree state in the database. Since we use tree ids to reduce the number of rows affected by tree mangement during insertion and deletion, root nodes are not true siblings; thus, making an item a sibling of a root node is a special case which involves shuffling tree ids around. """ if node == target: raise InvalidMove(_('A node may not be made a sibling of itself.')) opts = self.model._meta tree_id = getattr(node, self.tree_id_attr) target_tree_id = getattr(target, self.tree_id_attr) if node.is_child_node(): if position == 'left': space_target = target_tree_id - 1 new_tree_id = target_tree_id elif position == 'right': space_target = target_tree_id new_tree_id = target_tree_id + 1 else: raise ValueError(_('An invalid position was given: %s.') % position) self._create_tree_space(space_target) if tree_id > space_target: # The node's tree id has been incremented in the # database - this change must be reflected in the node # object for the method call below to operate on the # correct tree. setattr(node, self.tree_id_attr, tree_id + 1) self._make_child_root_node(node, new_tree_id) else: if position == 'left': if target_tree_id > tree_id: left_sibling = target.get_previous_sibling() if node == left_sibling: return new_tree_id = getattr(left_sibling, self.tree_id_attr) lower_bound, upper_bound = tree_id, new_tree_id shift = -1 else: new_tree_id = target_tree_id lower_bound, upper_bound = new_tree_id, tree_id shift = 1 elif position == 'right': if target_tree_id > tree_id: new_tree_id = target_tree_id lower_bound, upper_bound = tree_id, target_tree_id shift = -1 else: right_sibling = target.get_next_sibling() if node == right_sibling: return new_tree_id = getattr(right_sibling, self.tree_id_attr) lower_bound, upper_bound = new_tree_id, tree_id shift = 1 else: raise ValueError(_('An invalid position was given: %s.') % position) connection = self._get_connection(instance=node) qn = connection.ops.quote_name root_sibling_query = """ UPDATE %(table)s SET %(tree_id)s = CASE WHEN %(tree_id)s = %%s THEN %%s ELSE %(tree_id)s + %%s END WHERE %(tree_id)s >= %%s AND %(tree_id)s <= %%s""" % { 'table': qn(self.tree_model._meta.db_table), 'tree_id': qn(opts.get_field(self.tree_id_attr).column), } cursor = connection.cursor() cursor.execute(root_sibling_query, [tree_id, new_tree_id, shift, lower_bound, upper_bound]) setattr(node, self.tree_id_attr, new_tree_id) def _manage_space(self, size, target, tree_id): """ Manages spaces in the tree identified by ``tree_id`` by changing the values of the left and right columns by ``size`` after the given ``target`` point. """ if self.tree_model._mptt_is_tracking: self.tree_model._mptt_track_tree_modified(tree_id) else: connection = self._get_connection() qn = connection.ops.quote_name opts = self.model._meta space_query = """ UPDATE %(table)s SET %(left)s = CASE WHEN %(left)s > %%s THEN %(left)s + %%s ELSE %(left)s END, %(right)s = CASE WHEN %(right)s > %%s THEN %(right)s + %%s ELSE %(right)s END WHERE %(tree_id)s = %%s AND (%(left)s > %%s OR %(right)s > %%s)""" % { 'table': qn(self.tree_model._meta.db_table), 'left': qn(opts.get_field(self.left_attr).column), 'right': qn(opts.get_field(self.right_attr).column), 'tree_id': qn(opts.get_field(self.tree_id_attr).column), } cursor = connection.cursor() cursor.execute(space_query, [target, size, target, size, tree_id, target, target]) def _move_child_node(self, node, target, position): """ Calls the appropriate method to move child node ``node`` relative to the given ``target`` node as specified by ``position``. """ tree_id = getattr(node, self.tree_id_attr) target_tree_id = getattr(target, self.tree_id_attr) if tree_id == target_tree_id: self._move_child_within_tree(node, target, position) else: self._move_child_to_new_tree(node, target, position) def _move_child_to_new_tree(self, node, target, position): """ Moves child node ``node`` to a different tree, inserting it relative to the given ``target`` node in the new tree as specified by ``position``. ``node`` will be modified to reflect its new tree state in the database. """ left = getattr(node, self.left_attr) right = getattr(node, self.right_attr) level = getattr(node, self.level_attr) new_tree_id = getattr(target, self.tree_id_attr) space_target, level_change, left_right_change, parent, new_parent_right = \ self._calculate_inter_tree_move_values(node, target, position) tree_width = right - left + 1 # Make space for the subtree which will be moved self._create_space(tree_width, space_target, new_tree_id) # Move the subtree self._inter_tree_move_and_close_gap( node, level_change, left_right_change, new_tree_id, parent.pk) # Update the node to be consistent with the updated # tree in the database. setattr(node, self.left_attr, left - left_right_change) setattr(node, self.right_attr, right - left_right_change) setattr(node, self.level_attr, level - level_change) setattr(node, self.tree_id_attr, new_tree_id) setattr(node, self.parent_attr, parent) node._mptt_cached_fields[self.parent_attr] = parent.pk def _move_child_within_tree(self, node, target, position): """ Moves child node ``node`` within its current tree relative to the given ``target`` node as specified by ``position``. ``node`` will be modified to reflect its new tree state in the database. """ left = getattr(node, self.left_attr) right = getattr(node, self.right_attr) level = getattr(node, self.level_attr) width = right - left + 1 tree_id = getattr(node, self.tree_id_attr) target_left = getattr(target, self.left_attr) target_right = getattr(target, self.right_attr) target_level = getattr(target, self.level_attr) if position == 'last-child' or position == 'first-child': if node == target: raise InvalidMove(_('A node may not be made a child of itself.')) elif left < target_left < right: raise InvalidMove(_('A node may not be made a child of any of its descendants.')) if position == 'last-child': if target_right > right: new_left = target_right - width new_right = target_right - 1 else: new_left = target_right new_right = target_right + width - 1 else: if target_left > left: new_left = target_left - width + 1 new_right = target_left else: new_left = target_left + 1 new_right = target_left + width level_change = level - target_level - 1 parent = target elif position == 'left' or position == 'right': if node == target: raise InvalidMove(_('A node may not be made a sibling of itself.')) elif left < target_left < right: raise InvalidMove(_('A node may not be made a sibling of any of its descendants.')) if position == 'left': if target_left > left: new_left = target_left - width new_right = target_left - 1 else: new_left = target_left new_right = target_left + width - 1 else: if target_right > right: new_left = target_right - width + 1 new_right = target_right else: new_left = target_right + 1 new_right = target_right + width level_change = level - target_level parent = getattr(target, self.parent_attr) else: raise ValueError(_('An invalid position was given: %s.') % position) left_boundary = min(left, new_left) right_boundary = max(right, new_right) left_right_change = new_left - left gap_size = width if left_right_change > 0: gap_size = -gap_size connection = self._get_connection(instance=node) qn = connection.ops.quote_name opts = self.model._meta # The level update must come before the left update to keep # MySQL happy - left seems to refer to the updated value # immediately after its update has been specified in the query # with MySQL, but not with SQLite or Postgres. move_subtree_query = """ UPDATE %(table)s SET %(level)s = CASE WHEN %(left)s >= %%s AND %(left)s <= %%s THEN %(level)s - %%s ELSE %(level)s END, %(left)s = CASE WHEN %(left)s >= %%s AND %(left)s <= %%s THEN %(left)s + %%s WHEN %(left)s >= %%s AND %(left)s <= %%s THEN %(left)s + %%s ELSE %(left)s END, %(right)s = CASE WHEN %(right)s >= %%s AND %(right)s <= %%s THEN %(right)s + %%s WHEN %(right)s >= %%s AND %(right)s <= %%s THEN %(right)s + %%s ELSE %(right)s END, %(parent)s = CASE WHEN %(pk)s = %%s THEN %%s ELSE %(parent)s END WHERE %(tree_id)s = %%s""" % { 'table': qn(self.tree_model._meta.db_table), 'level': qn(opts.get_field(self.level_attr).column), 'left': qn(opts.get_field(self.left_attr).column), 'right': qn(opts.get_field(self.right_attr).column), 'parent': qn(opts.get_field(self.parent_attr).column), 'pk': qn(opts.pk.column), 'tree_id': qn(opts.get_field(self.tree_id_attr).column), } cursor = connection.cursor() cursor.execute(move_subtree_query, [ left, right, level_change, left, right, left_right_change, left_boundary, right_boundary, gap_size, left, right, left_right_change, left_boundary, right_boundary, gap_size, node.pk, parent.pk, tree_id]) # Update the node to be consistent with the updated # tree in the database. setattr(node, self.left_attr, new_left) setattr(node, self.right_attr, new_right) setattr(node, self.level_attr, level - level_change) setattr(node, self.parent_attr, parent) node._mptt_cached_fields[self.parent_attr] = parent.pk def _move_root_node(self, node, target, position): """ Moves root node``node`` to a different tree, inserting it relative to the given ``target`` node as specified by ``position``. ``node`` will be modified to reflect its new tree state in the database. """ left = getattr(node, self.left_attr) right = getattr(node, self.right_attr) level = getattr(node, self.level_attr) tree_id = getattr(node, self.tree_id_attr) new_tree_id = getattr(target, self.tree_id_attr) width = right - left + 1 if node == target: raise InvalidMove(_('A node may not be made a child of itself.')) elif tree_id == new_tree_id: raise InvalidMove(_('A node may not be made a child of any of its descendants.')) space_target, level_change, left_right_change, parent, right_shift = \ self._calculate_inter_tree_move_values(node, target, position) # Create space for the tree which will be inserted self._create_space(width, space_target, new_tree_id) # Move the root node, making it a child node connection = self._get_connection(instance=node) qn = connection.ops.quote_name opts = self.model._meta move_tree_query = """ UPDATE %(table)s SET %(level)s = %(level)s - %%s, %(left)s = %(left)s - %%s, %(right)s = %(right)s - %%s, %(tree_id)s = %%s, %(parent)s = CASE WHEN %(pk)s = %%s THEN %%s ELSE %(parent)s END WHERE %(left)s >= %%s AND %(left)s <= %%s AND %(tree_id)s = %%s""" % { 'table': qn(self.tree_model._meta.db_table), 'level': qn(opts.get_field(self.level_attr).column), 'left': qn(opts.get_field(self.left_attr).column), 'right': qn(opts.get_field(self.right_attr).column), 'tree_id': qn(opts.get_field(self.tree_id_attr).column), 'parent': qn(opts.get_field(self.parent_attr).column), 'pk': qn(opts.pk.column), } cursor = connection.cursor() cursor.execute(move_tree_query, [ level_change, left_right_change, left_right_change, new_tree_id, node.pk, parent.pk, left, right, tree_id]) # Update the former root node to be consistent with the updated # tree in the database. setattr(node, self.left_attr, left - left_right_change) setattr(node, self.right_attr, right - left_right_change) setattr(node, self.level_attr, level - level_change) setattr(node, self.tree_id_attr, new_tree_id) setattr(node, self.parent_attr, parent) node._mptt_cached_fields[self.parent_attr] = parent.pk django-mptt-0.8.0/mptt/models.py0000644000076500000240000012031412635625275020062 0ustar cdestigterstaff00000000000000from __future__ import unicode_literals from functools import reduce, wraps import operator import threading 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 __all__ = ( 'TreeForeignKey', 'TreeOneToOneField', 'TreeManyToManyField', 'TreeManager', 'MPTTOptions', 'MPTTModelBase', 'MPTTModel', ) class _classproperty(object): def __init__(self, getter, setter=None): self.fget = getter self.fset = setter def __get__(self, cls, owner): return self.fget(owner) def __set__(self, cls, owner, value): if not self.fset: raise AttributeError("This classproperty is read only") self.fset(owner, value) class classpropertytype(property): def __init__(self, name, bases=(), members={}): return super(classpropertytype, self).__init__( members.get('__get__'), members.get('__set__'), members.get('__delete__'), members.get('__doc__') ) classproperty = classpropertytype('classproperty') class MPTTOptions(object): """ Options class for MPTT models. Use this as an inner class called ``MPTTMeta``:: class MyModel(MPTTModel): class MPTTMeta: order_insertion_by = ['name'] parent_attr = 'myparent' """ order_insertion_by = [] left_attr = 'lft' right_attr = 'rght' tree_id_attr = 'tree_id' level_attr = 'level' parent_attr = 'parent' def __init__(self, opts=None, **kwargs): # Override defaults with options provided if opts: opts = list(opts.__dict__.items()) else: opts = [] opts.extend(list(kwargs.items())) if 'tree_manager_attr' in [opt[0] for opt in opts]: raise ValueError( "`tree_manager_attr` has been removed; you should instantiate" " a TreeManager as a normal manager on your model instead.") for key, value in opts: if key[:2] == '__': continue setattr(self, key, value) # Normalize order_insertion_by to a list if isinstance(self.order_insertion_by, six.string_types): self.order_insertion_by = [self.order_insertion_by] elif isinstance(self.order_insertion_by, tuple): self.order_insertion_by = list(self.order_insertion_by) elif self.order_insertion_by is None: self.order_insertion_by = [] def __iter__(self): return ((k, v) for k, v in self.__dict__.items() if k[0] != '_') # Helper methods for accessing tree attributes on models. def get_raw_field_value(self, instance, field_name): """ Gets the value of the given fieldname for the instance. This is not the same as getattr(). This function will return IDs for foreignkeys etc, rather than doing a database query. """ field = instance._meta.get_field(field_name) return field.value_from_object(instance) def set_raw_field_value(self, instance, field_name, value): """ Sets the value of the given fieldname for the instance. This is not the same as setattr(). This function requires an ID for a foreignkey (etc) rather than an instance. """ field = instance._meta.get_field(field_name) setattr(instance, field.attname, value) def update_mptt_cached_fields(self, instance): """ Caches (in an instance._mptt_cached_fields dict) the original values of: - parent pk - fields specified in order_insertion_by 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,)) if self.order_insertion_by: for f in self.order_insertion_by: if f[0] == '-': f = f[1:] 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) def insertion_target_filters(self, instance, order_insertion_by): """ Creates a filter which matches suitable right siblings for ``node``, where insertion should maintain ordering according to the list of fields in ``order_insertion_by``. For example, given an ``order_insertion_by`` of ``['field1', 'field2', 'field3']``, the resulting filter should correspond to the following SQL:: field1 > %s OR (field1 = %s AND field2 > %s) OR (field1 = %s AND field2 = %s AND field3 > %s) """ fields = [] filters = [] fields__append = fields.append filters__append = filters.append and_ = operator.and_ or_ = operator.or_ for field_name in order_insertion_by: if field_name[0] == '-': field_name = field_name[1:] filter_suffix = '__lt' else: filter_suffix = '__gt' value = getattr(instance, field_name) if value is None: # node isn't saved yet. get the insertion value from pre_save. field = instance._meta.get_field(field_name) value = field.pre_save(instance, True) q = Q(**{field_name + filter_suffix: value}) filters__append(reduce(and_, [Q(**{f: v}) for f, v in fields] + [q])) fields__append((field_name, value)) return reduce(or_, filters) def get_ordered_insertion_target(self, node, parent): """ Attempts to retrieve a suitable right sibling for ``node`` underneath ``parent`` (which may be ``None`` in the case of root nodes) so that ordering by the fields specified by the node's class' ``order_insertion_by`` option is maintained. Returns ``None`` if no suitable sibling can be found. """ right_sibling = None # Optimisation - if the parent doesn't have descendants, # the node will always be its last child. if parent is None or parent.get_descendant_count() > 0: opts = node._mptt_meta order_by = opts.order_insertion_by[:] filters = self.insertion_target_filters(node, order_by) if parent: filters = filters & Q(**{opts.parent_attr: parent}) # Fall back on tree ordering if multiple child nodes have # the same values. order_by.append(opts.left_attr) else: filters = filters & Q(**{opts.parent_attr: None}) # Fall back on tree id ordering if multiple root nodes have # the same values. order_by.append(opts.tree_id_attr) queryset = node.__class__._tree_manager.filter(filters).order_by(*order_by) if node.pk: queryset = queryset.exclude(pk=node.pk) try: right_sibling = queryset[:1][0] except IndexError: # No suitable right sibling could be found pass return right_sibling class MPTTModelBase(ModelBase): """ Metaclass for MPTT models """ def __new__(meta, class_name, bases, class_dict): """ Create subclasses of MPTTModel. This: - adds the MPTT fields to the class - adds a TreeManager to the model """ if class_name == 'NewBase' and class_dict == {}: return super(MPTTModelBase, meta).__new__(meta, class_name, bases, class_dict) is_MPTTModel = False try: MPTTModel except NameError: is_MPTTModel = True MPTTMeta = class_dict.pop('MPTTMeta', None) if not MPTTMeta: class MPTTMeta: pass initial_options = frozenset(dir(MPTTMeta)) # extend MPTTMeta from base classes for base in bases: if hasattr(base, '_mptt_meta'): for name, value in base._mptt_meta: if name == 'tree_manager_attr': continue if name not in initial_options: setattr(MPTTMeta, name, value) class_dict['_mptt_meta'] = MPTTOptions(MPTTMeta) super_new = super(MPTTModelBase, meta).__new__ cls = super_new(meta, class_name, bases, class_dict) cls = meta.register(cls) # see error cases in TreeManager.disable_mptt_updates for the reasoning here. cls._mptt_tracking_base = None if is_MPTTModel: bases = [cls] else: bases = [base for base in cls.mro() if issubclass(base, MPTTModel)] for base in bases: if (not (base._meta.abstract or base._meta.proxy) and base._tree_manager.tree_model is base): cls._mptt_tracking_base = base break if cls is cls._mptt_tracking_base: cls._threadlocal = threading.local() # set on first access (to make threading errors more obvious): # cls._threadlocal.mptt_delayed_tree_changes = None return cls @classmethod def register(meta, cls, **kwargs): """ For the weird cases when you need to add tree-ness to an *existing* class. For other cases you should subclass MPTTModel instead of calling this. """ if not issubclass(cls, models.Model): raise ValueError(_("register() expects a Django model class argument")) if not hasattr(cls, '_mptt_meta'): cls._mptt_meta = MPTTOptions(**kwargs) abstract = getattr(cls._meta, 'abstract', False) try: MPTTModel except NameError: # We're defining the base class right now, so don't do anything # We only want to add this stuff to the subclasses. # (Otherwise if field names are customized, we'll end up adding two # copies) pass else: if not issubclass(cls, MPTTModel): bases = list(cls.__bases__) # strip out bases that are strict superclasses of MPTTModel. # (i.e. Model, object) # this helps linearize the type hierarchy if possible for i in range(len(bases) - 1, -1, -1): if issubclass(MPTTModel, bases[i]): del bases[i] bases.insert(0, MPTTModel) cls.__bases__ = tuple(bases) if _get_tree_model(cls) is cls: # HACK: _meta.get_field() doesn't work before AppCache.ready in Django>=1.8 # ( see https://code.djangoproject.com/ticket/24231 ) # So the only way to get existing fields is using local_fields on all superclasses. existing_field_names = set() for base in cls.mro(): if hasattr(base, '_meta'): existing_field_names.update([f.name for f in base._meta.local_fields]) for key in ('left_attr', 'right_attr', 'tree_id_attr', 'level_attr'): field_name = getattr(cls._mptt_meta, key) if field_name not in existing_field_names: field = models.PositiveIntegerField(db_index=True, editable=False) field.contribute_to_class(cls, field_name) # Add a tree manager, if there isn't one already if not abstract: manager = getattr(cls, 'objects', None) if manager is None: manager = cls._default_manager._copy_to_model(cls) manager.contribute_to_class(cls, 'objects') elif manager.model != cls: # manager was inherited manager = manager._copy_to_model(cls) manager.contribute_to_class(cls, 'objects') # make sure we have a tree manager somewhere tree_manager = None 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: tree_manager = TreeManager() tree_manager.contribute_to_class(cls, '_tree_manager') # avoid using ManagerDescriptor, so instances can refer to self._tree_manager setattr(cls, '_tree_manager', tree_manager) return cls def raise_if_unsaved(func): @wraps(func) def _fn(self, *args, **kwargs): if not self.pk: raise ValueError( 'Cannot call %(function)s on unsaved %(class)s instances' % {'function': func.__name__, 'class': self.__class__.__name__} ) return func(self, *args, **kwargs) return _fn class MPTTModel(six.with_metaclass(MPTTModelBase, models.Model)): """ Base class for tree models. """ _default_manager = TreeManager() class Meta: abstract = True def __init__(self, *args, **kwargs): super(MPTTModel, self).__init__(*args, **kwargs) self._mptt_meta.update_mptt_cached_fields(self) def _mpttfield(self, fieldname): translated_fieldname = getattr(self._mptt_meta, fieldname + '_attr') return getattr(self, translated_fieldname) @_classproperty def _mptt_updates_enabled(cls): if not cls._mptt_tracking_base: return True return getattr(cls._mptt_tracking_base._threadlocal, 'mptt_updates_enabled', True) # ideally this'd be part of the _mptt_updates_enabled classproperty, but it seems # that settable classproperties are very, very hard to do! suggestions please :) @classmethod def _set_mptt_updates_enabled(cls, value): assert cls is cls._mptt_tracking_base,\ "Can't enable or disable mptt updates on a non-tracking class." cls._threadlocal.mptt_updates_enabled = value @_classproperty def _mptt_is_tracking(cls): if not cls._mptt_tracking_base: return False if not hasattr(cls._threadlocal, 'mptt_delayed_tree_changes'): # happens the first time this is called from each thread cls._threadlocal.mptt_delayed_tree_changes = None return cls._threadlocal.mptt_delayed_tree_changes is not None @classmethod def _mptt_start_tracking(cls): assert cls is cls._mptt_tracking_base,\ "Can't start or stop mptt tracking on a non-tracking class." assert not cls._mptt_is_tracking, "mptt tracking is already started." cls._threadlocal.mptt_delayed_tree_changes = set() @classmethod def _mptt_stop_tracking(cls): assert cls is cls._mptt_tracking_base,\ "Can't start or stop mptt tracking on a non-tracking class." assert cls._mptt_is_tracking, "mptt tracking isn't started." results = cls._threadlocal.mptt_delayed_tree_changes cls._threadlocal.mptt_delayed_tree_changes = None return results @classmethod def _mptt_track_tree_modified(cls, tree_id): if not cls._mptt_is_tracking: return cls._threadlocal.mptt_delayed_tree_changes.add(tree_id) @classmethod def _mptt_track_tree_insertions(cls, tree_id, num_inserted): if not cls._mptt_is_tracking: return changes = cls._threadlocal.mptt_delayed_tree_changes if not num_inserted or not changes: return if num_inserted < 0: deleted = range(tree_id + num_inserted, -num_inserted) changes.difference_update(deleted) new_changes = set( (t + num_inserted if t >= tree_id else t) for t in changes) cls._threadlocal.mptt_delayed_tree_changes = new_changes @raise_if_unsaved def get_ancestors(self, ascending=False, include_self=False): """ Creates a ``QuerySet`` containing the ancestors of this model instance. This defaults to being in descending order (root ancestor first, immediate parent last); passing ``True`` for the ``ascending`` argument will reverse the ordering (immediate parent first, root ancestor last). If ``include_self`` is ``True``, the ``QuerySet`` will also include this model instance. """ if self.is_root_node(): if not include_self: return self._tree_manager.none() else: # Filter on pk for efficiency. qs = self._tree_manager.filter(pk=self.pk) else: opts = self._mptt_meta order_by = opts.left_attr if ascending: order_by = '-' + order_by left = getattr(self, opts.left_attr) right = getattr(self, opts.right_attr) if not include_self: left -= 1 right += 1 qs = self._tree_manager._mptt_filter( left__lte=left, right__gte=right, tree_id=self._mpttfield('tree_id'), ) qs = qs.order_by(order_by) if hasattr(self, '_mptt_use_cached_ancestors'): # Called during or after a `recursetree` tag. # There should be cached parents up to level 0. # So we can use them to avoid doing a query at all. ancestors = [] p = self if not include_self: p = getattr(p, opts.parent_attr) while p is not None: ancestors.append(p) p = getattr(p, opts.parent_attr) ancestors.reverse() qs._result_cache = ancestors return qs @raise_if_unsaved def get_family(self): """ Returns a ``QuerySet`` containing the ancestors, the model itself and the descendants, in tree order. """ opts = self._mptt_meta left = getattr(self, opts.left_attr) right = getattr(self, opts.right_attr) ancestors = Q(**{ "%s__lte" % opts.left_attr: left, "%s__gte" % opts.right_attr: right, opts.tree_id_attr: self._mpttfield('tree_id'), }) descendants = Q(**{ "%s__gte" % opts.left_attr: left, "%s__lte" % opts.left_attr: right, opts.tree_id_attr: self._mpttfield('tree_id'), }) return self._tree_manager.filter(ancestors | descendants) @raise_if_unsaved def get_children(self): """ Returns a ``QuerySet`` containing the immediate children of this model instance, in tree order. The benefit of using this method over the reverse relation provided by the ORM to the instance's children is that a database query can be avoided in the case where the instance is a leaf node (it has no children). If called from a template where the tree has been walked by the ``cache_tree_children`` filter, no database query is required. """ if hasattr(self, '_cached_children'): qs = self._tree_manager.filter(pk__in=[n.pk for n in self._cached_children]) qs._result_cache = self._cached_children return qs else: if self.is_leaf_node(): return self._tree_manager.none() return self._tree_manager._mptt_filter(parent=self) @raise_if_unsaved def get_descendants(self, include_self=False): """ Creates a ``QuerySet`` containing descendants of this model instance, in tree order. If ``include_self`` is ``True``, the ``QuerySet`` will also include this model instance. """ if self.is_leaf_node(): if not include_self: return self._tree_manager.none() else: return self._tree_manager.filter(pk=self.pk) opts = self._mptt_meta left = getattr(self, opts.left_attr) right = getattr(self, opts.right_attr) if not include_self: left += 1 right -= 1 return self._tree_manager._mptt_filter( tree_id=self._mpttfield('tree_id'), left__gte=left, left__lte=right ) def get_descendant_count(self): """ Returns the number of descendants this model instance has. """ if self._mpttfield('right') is None: # node not saved yet return 0 else: return (self._mpttfield('right') - self._mpttfield('left') - 1) // 2 @raise_if_unsaved def get_leafnodes(self, include_self=False): """ Creates a ``QuerySet`` containing leafnodes of this model instance, in tree order. If ``include_self`` is ``True``, the ``QuerySet`` will also include this model instance (if it is a leaf node) """ descendants = self.get_descendants(include_self=include_self) return self._tree_manager._mptt_filter( descendants, left=(models.F(self._mptt_meta.right_attr) - 1) ) @raise_if_unsaved def get_next_sibling(self, *filter_args, **filter_kwargs): """ Returns this model instance's next sibling in the tree, or ``None`` if it doesn't have a next sibling. """ qs = self._tree_manager.filter(*filter_args, **filter_kwargs) if self.is_root_node(): qs = self._tree_manager._mptt_filter( qs, parent=None, tree_id__gt=self._mpttfield('tree_id'), ) else: qs = self._tree_manager._mptt_filter( qs, parent__pk=getattr(self, self._mptt_meta.parent_attr + '_id'), left__gt=self._mpttfield('right'), ) siblings = qs[:1] return siblings and siblings[0] or None @raise_if_unsaved def get_previous_sibling(self, *filter_args, **filter_kwargs): """ Returns this model instance's previous sibling in the tree, or ``None`` if it doesn't have a previous sibling. """ opts = self._mptt_meta qs = self._tree_manager.filter(*filter_args, **filter_kwargs) if self.is_root_node(): qs = self._tree_manager._mptt_filter( qs, parent=None, tree_id__lt=self._mpttfield('tree_id'), ) qs = qs.order_by('-' + opts.tree_id_attr) else: qs = self._tree_manager._mptt_filter( qs, parent__pk=getattr(self, opts.parent_attr + '_id'), right__lt=self._mpttfield('left'), ) qs = qs.order_by('-' + opts.right_attr) siblings = qs[:1] return siblings and siblings[0] or None @raise_if_unsaved def get_root(self): """ Returns the root node of this model instance's tree. """ if self.is_root_node() and type(self) == self._tree_manager.tree_model: return self return self._tree_manager._mptt_filter( tree_id=self._mpttfield('tree_id'), parent=None, ).get() @raise_if_unsaved def get_siblings(self, include_self=False): """ Creates a ``QuerySet`` containing siblings of this model instance. Root nodes are considered to be siblings of other root nodes. If ``include_self`` is ``True``, the ``QuerySet`` will also include this model instance. """ if self.is_root_node(): queryset = self._tree_manager._mptt_filter(parent=None) else: parent_id = getattr(self, self._mptt_meta.parent_attr + '_id') queryset = self._tree_manager._mptt_filter(parent__pk=parent_id) if not include_self: queryset = queryset.exclude(pk=self.pk) return queryset def get_level(self): """ Returns the level of this node (distance from root) """ 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): """ 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) def is_child_node(self): """ Returns ``True`` if this model instance is a child node, ``False`` otherwise. """ return not self.is_root_node() def is_leaf_node(self): """ Returns ``True`` if this model instance is a leaf node (it has no children), ``False`` otherwise. """ return not self.get_descendant_count() def is_root_node(self): """ Returns ``True`` if this model instance is a root node, ``False`` otherwise. """ return getattr(self, self._mptt_meta.parent_attr + '_id') is None @raise_if_unsaved def is_descendant_of(self, other, include_self=False): """ Returns ``True`` if this model is a descendant of the given node, ``False`` otherwise. If include_self is True, also returns True if the two nodes are the same node. """ opts = self._mptt_meta if include_self and other.pk == self.pk: return True if getattr(self, opts.tree_id_attr) != getattr(other, opts.tree_id_attr): return False else: left = getattr(self, opts.left_attr) right = getattr(self, opts.right_attr) return ( left > getattr(other, opts.left_attr) and right < getattr(other, opts.right_attr)) @raise_if_unsaved def is_ancestor_of(self, other, include_self=False): """ Returns ``True`` if this model is an ancestor of the given node, ``False`` otherwise. If include_self is True, also returns True if the two nodes are the same node. """ if include_self and other.pk == self.pk: return True return other.is_descendant_of(self) def move_to(self, target, position='first-child'): """ Convenience method for calling ``TreeManager.move_node`` with this model instance. NOTE: This is a low-level method; it does NOT respect ``MPTTMeta.order_insertion_by``. In most cases you should just move the node yourself by setting node.parent. """ self._tree_manager.move_node(self, target, position) def _is_saved(self, using=None): if not self.pk or self._mpttfield('tree_id') is None: return False opts = self._meta if opts.pk.rel is None: return True else: if not hasattr(self, '_mptt_saved'): manager = self.__class__._base_manager manager = manager.using(using) self._mptt_saved = manager.filter(pk=self.pk).exists() return self._mptt_saved def _get_user_field_names(self): """ Returns the list of user defined (i.e. non-mptt internal) field names. """ 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) for field in self._meta.fields: 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 def save(self, *args, **kwargs): """ If this is a new node, sets tree fields up before it is inserted into the database, making room in the tree structure as neccessary, defaulting to making the new node the last child of its parent. It the node's left and right edge indicators already been set, we take this as indication that the node has already been set up for insertion, so its tree fields are left untouched. If this is an existing node and its parent has been changed, performs reparenting in the tree structure, defaulting to making the node the last child of its new parent. In either case, if the node's class has its ``order_insertion_by`` tree option set, the node will be inserted or moved to the appropriate position to maintain ordering by the specified field. """ do_updates = self.__class__._mptt_updates_enabled track_updates = self.__class__._mptt_is_tracking opts = self._mptt_meta if not (do_updates or track_updates): # inside manager.disable_mptt_updates(), don't do any updates. # unless we're also inside TreeManager.delay_mptt_updates() if self._mpttfield('left') is None: # we need to set *some* values, though don't care too much what. parent = getattr(self, '_%s_cache' % opts.parent_attr, None) # if we have a cached parent, have a stab at getting # possibly-correct values. otherwise, meh. if parent: left = parent._mpttfield('left') + 1 setattr(self, opts.left_attr, left) setattr(self, opts.right_attr, left + 1) setattr(self, opts.level_attr, parent._mpttfield('level') + 1) setattr(self, opts.tree_id_attr, parent._mpttfield('tree_id')) self._tree_manager._post_insert_update_cached_parent_right(parent, 2) else: setattr(self, opts.left_attr, 1) setattr(self, opts.right_attr, 2) setattr(self, opts.level_attr, 0) setattr(self, opts.tree_id_attr, 0) return super(MPTTModel, self).save(*args, **kwargs) parent_id = opts.get_raw_field_value(self, opts.parent_attr) # determine whether this instance is already in the db 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] 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: for field_name, old_value in self._mptt_cached_fields.items(): 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: same_order = True self.__class__._mptt_track_tree_modified(self._mpttfield('tree_id')) elif (not do_updates) and not same_order and old_parent_id is None: # the old tree no longer exists, so we need to collapse it. collapse_old_tree = self._mpttfield('tree_id') parent = getattr(self, opts.parent_attr) tree_id = parent._mpttfield('tree_id') left = parent._mpttfield('left') + 1 self.__class__._mptt_track_tree_modified(tree_id) setattr(self, opts.tree_id_attr, tree_id) setattr(self, opts.left_attr, left) setattr(self, opts.right_attr, left + 1) setattr(self, opts.level_attr, parent._mpttfield('level') + 1) same_order = True if not same_order: opts.set_raw_field_value(self, opts.parent_attr, old_parent_id) try: right_sibling = None if opts.order_insertion_by: right_sibling = opts.get_ordered_insertion_target( self, getattr(self, opts.parent_attr)) if parent_id is not None: parent = getattr(self, opts.parent_attr) # If we aren't already a descendant of the new parent, # we need to update the parent.rght so things like # get_children and get_descendant_count work correctly. update_cached_parent = ( getattr(self, opts.tree_id_attr) != getattr(parent, opts.tree_id_attr) or getattr(self, opts.left_attr) < getattr(parent, opts.left_attr) 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) else: # Default movement if parent_id is None: root_nodes = self._tree_manager.root_nodes() try: 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) except IndexError: pass else: self._tree_manager._move_node( self, parent, 'last-child', save=False) if parent_id is not None and update_cached_parent: # Update rght of cached parent right_shift = 2 * (self.get_descendant_count() + 1) self._tree_manager._post_insert_update_cached_parent_right( parent, right_shift) finally: # 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: # When not using delayed/disabled updates, # 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() else: # new node, do an insert if (getattr(self, opts.left_attr) and getattr(self, opts.right_attr)): # This node has already been set up for insertion. pass else: parent = getattr(self, opts.parent_attr) right_sibling = None # if we're inside delay_mptt_updates, don't do queries to find # sibling position. instead, do default insertion. correct # positions will be found during partial rebuild later. # *unless* this is a root node. (as update tracking doesn't # handle re-ordering of trees.) if do_updates or parent is None: if opts.order_insertion_by: 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) if parent: # since we didn't insert into parent, we have to update parent.rght # here instead of in TreeManager.insert_node() right_shift = 2 * (self.get_descendant_count() + 1) self._tree_manager._post_insert_update_cached_parent_right( parent, right_shift) else: # Default insertion self.insert_at(parent, position='last-child', allow_existing_pk=True) try: super(MPTTModel, self).save(*args, **kwargs) finally: if collapse_old_tree is not None: self._tree_manager._create_tree_space(collapse_old_tree, -1) self._mptt_saved = True opts.update_mptt_cached_fields(self) save.alters_data = True def delete(self, *args, **kwargs): """Calling ``delete`` on a node will delete it as well as its full subtree, as opposed to reattaching all the subnodes to its parent node. There are no argument specific to a MPTT model, all the arguments will be passed directly to the django's ``Model.delete``. ``delete`` will not return anything. """ tree_width = (self._mpttfield('right') - self._mpttfield('left') + 1) target_right = self._mpttfield('right') tree_id = self._mpttfield('tree_id') self._tree_manager._close_gap(tree_width, target_right, tree_id) parent = getattr(self, '_%s_cache' % self._mptt_meta.parent_attr, None) if parent: right_shift = -self.get_descendant_count() - 2 self._tree_manager._post_insert_update_cached_parent_right(parent, right_shift) super(MPTTModel, self).delete(*args, **kwargs) delete.alters_data = True def _mptt_refresh(self): if not self.pk: return manager = type(self)._tree_manager opts = self._mptt_meta values = manager.filter(pk=self.pk).values( opts.left_attr, opts.right_attr, opts.level_attr, opts.tree_id_attr, )[0] for k, v in values.items(): setattr(self, k, v) django-mptt-0.8.0/mptt/querysets.py0000644000076500000240000000142012635612714020631 0ustar cdestigterstaff00000000000000from django.db import models from mptt import utils class TreeQuerySet(models.query.QuerySet): def get_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): """ 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) django-mptt-0.8.0/mptt/settings.py0000644000076500000240000000031512515002575020422 0ustar cdestigterstaff00000000000000from django.conf import settings """Default level indicator. By default is `'---'`.""" DEFAULT_LEVEL_INDICATOR = getattr(settings, 'MPTT_DEFAULT_LEVEL_INDICATOR', '---') django-mptt-0.8.0/mptt/signals.py0000644000076500000240000000053212635077450020232 0ustar cdestigterstaff00000000000000import 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' ]) django-mptt-0.8.0/mptt/templates/0000755000076500000240000000000012635633364020220 5ustar cdestigterstaff00000000000000django-mptt-0.8.0/mptt/templates/admin/0000755000076500000240000000000012635633365021311 5ustar cdestigterstaff00000000000000django-mptt-0.8.0/mptt/templates/admin/grappelli_mptt_change_list.html0000644000076500000240000000037012462512456027555 0ustar cdestigterstaff00000000000000{% extends "admin/change_list.html" %} {% load admin_list i18n mptt_admin %} {% block result_list %} {% mptt_result_list cl %} {% if action_form and actions_on_bottom and cl.full_result_count %}{% admin_actions %}{% endif %} {% endblock %}django-mptt-0.8.0/mptt/templates/admin/grappelli_mptt_change_list_results.html0000644000076500000240000000212312627655215031340 0ustar cdestigterstaff00000000000000{% if result_hidden_fields %}
{# DIV for HTML validation #} {% for item in result_hidden_fields %}{{ item }}{% endfor %}
{% endif %} {% if results %}
{% for header in result_headers %} {% endfor %} {% for result in results %} {% if result.form.non_field_errors %} {% endif %} {% for item in result %}{{ item }}{% endfor %} {% endfor %}
{{ header.text|capfirst }}
{{ result.form.non_field_errors }}
{% endif %}django-mptt-0.8.0/mptt/templates/admin/mptt_change_list.html0000644000076500000240000000053412455555636025531 0ustar cdestigterstaff00000000000000{% extends "admin/change_list.html" %} {% load admin_list i18n mptt_admin %} {% block result_list %} {% if action_form and actions_on_top and cl.full_result_count %}{% admin_actions %}{% endif %} {% mptt_result_list cl %} {% if action_form and actions_on_bottom and cl.full_result_count %}{% admin_actions %}{% endif %} {% endblock %} django-mptt-0.8.0/mptt/templates/admin/mptt_change_list_results.html0000644000076500000240000000146512627654434027313 0ustar cdestigterstaff00000000000000{% if result_hidden_fields %}
{# DIV for HTML validation #} {% for item in result_hidden_fields %}{{ item }}{% endfor %}
{% endif %} {% if results %}
{% for header in result_headers %} {% endfor %} {% for result in results %} {% if result.form.non_field_errors %} {% endif %} {% for item in result %}{{ item }}{% endfor %} {% endfor %}
{{ header.text|capfirst }}
{{ result.form.non_field_errors }}
{% endif %} django-mptt-0.8.0/mptt/templatetags/0000755000076500000240000000000012635633365020715 5ustar cdestigterstaff00000000000000django-mptt-0.8.0/mptt/templatetags/__init__.py0000644000076500000240000000005012462512456023014 0ustar cdestigterstaff00000000000000from __future__ import unicode_literals django-mptt-0.8.0/mptt/templatetags/mptt_admin.py0000644000076500000240000001666212634472470023433 0ustar cdestigterstaff00000000000000from __future__ import unicode_literals from django.conf import settings from django.contrib.admin.templatetags.admin_list import ( result_hidden_fields, _boolean_icon, result_headers) from django.contrib.admin.utils import lookup_field, display_for_field from django.core.exceptions import ObjectDoesNotExist from django.db import models from django.template import Library from django.utils.encoding import smart_text, force_text from django.utils.html import escape, conditional_escape from django.utils.safestring import mark_safe from django.utils.translation import get_language_bidi register = Library() MPTT_ADMIN_LEVEL_INDENT = getattr(settings, 'MPTT_ADMIN_LEVEL_INDENT', 10) IS_GRAPPELLI_INSTALLED = True if 'grappelli' in settings.INSTALLED_APPS else False def get_empty_value_display(cl): if hasattr(cl.model_admin, 'get_empty_value_display'): return cl.model_admin.get_empty_value_display() else: # Django < 1.9 from django.contrib.admin.views.main import EMPTY_CHANGELIST_VALUE return EMPTY_CHANGELIST_VALUE ### # Ripped from contrib.admin's (1.3.1) items_for_result tag. # The only difference is we're indenting nodes according to their level. def mptt_items_for_result(cl, result, form): """ Generates the actual list of data. """ first = True pk = cl.lookup_opts.pk.attname # #### MPTT ADDITION START # figure out which field to indent mptt_indent_field = getattr(cl.model_admin, 'mptt_indent_field', None) if not mptt_indent_field: for field_name in cl.list_display: try: f = cl.lookup_opts.get_field(field_name) except models.FieldDoesNotExist: if (mptt_indent_field is None and field_name != 'action_checkbox'): mptt_indent_field = field_name else: # first model field, use this one mptt_indent_field = field_name break # #### MPTT ADDITION END # figure out how much to indent mptt_level_indent = getattr(cl.model_admin, 'mptt_level_indent', MPTT_ADMIN_LEVEL_INDENT) for field_name in cl.list_display: row_class = '' try: f, attr, value = lookup_field(field_name, result, cl.model_admin) except (AttributeError, ObjectDoesNotExist): result_repr = get_empty_value_display(cl) else: if f is None: if field_name == 'action_checkbox': row_class = ' class="action-checkbox"' allow_tags = getattr(attr, 'allow_tags', False) boolean = getattr(attr, 'boolean', False) if boolean: allow_tags = True result_repr = _boolean_icon(value) else: result_repr = smart_text(value) # Strip HTML tags in the resulting text, except if the # function has an "allow_tags" attribute set to True. if not allow_tags: result_repr = conditional_escape(result_repr) else: result_repr = mark_safe(result_repr) else: if isinstance(f.rel, models.ManyToOneRel): field_val = getattr(result, f.name) if field_val is None: result_repr = get_empty_value_display(cl) else: result_repr = escape(field_val) else: try: result_repr = display_for_field(value, f) except TypeError: # Changed in Django 1.9, now takes 3 arguments result_repr = display_for_field( value, f, get_empty_value_display(cl)) if isinstance(f, models.DateField)\ or isinstance(f, models.TimeField)\ or isinstance(f, models.ForeignKey): row_class = ' class="nowrap"' if force_text(result_repr) == '': result_repr = mark_safe(' ') # #### MPTT ADDITION START if field_name == mptt_indent_field: level = getattr(result, result._mptt_meta.level_attr) padding_attr = ' style="padding-%s:%spx"' % ( 'right' if get_language_bidi() else 'left', 5 + mptt_level_indent * level) else: padding_attr = '' # #### MPTT ADDITION END # If list_display_links not defined, add the link tag to the first field if (first and not cl.list_display_links) or field_name in cl.list_display_links: table_tag = {True: 'th', False: 'td'}[first] first = False url = cl.url_for_result(result) # Convert the pk to something that can be used in Javascript. # Problem cases are long ints (23L) and non-ASCII strings. if cl.to_field: attr = str(cl.to_field) else: attr = pk value = result.serializable_value(attr) result_id = repr(force_text(value))[1:] # #### MPTT SUBSTITUTION START yield mark_safe('<%s%s%s>%s' % ( table_tag, row_class, padding_attr, url, (cl.is_popup and ' onclick="opener.dismissRelatedLookupPopup(window, %s); return false;"' % result_id or ''), # noqa conditional_escape(result_repr), table_tag)) # #### MPTT SUBSTITUTION END else: # By default the fields come from ModelAdmin.list_editable, but if we pull # the fields out of the form instead of list_editable custom admins # can provide fields on a per request basis if (form and field_name in form.fields and not ( field_name == cl.model._meta.pk.name and form[cl.model._meta.pk.name].is_hidden)): bf = form[field_name] result_repr = mark_safe(force_text(bf.errors) + force_text(bf)) else: result_repr = conditional_escape(result_repr) # #### MPTT SUBSTITUTION START yield mark_safe('%s' % (row_class, padding_attr, result_repr)) # #### MPTT SUBSTITUTION END if form and not form[cl.model._meta.pk.name].is_hidden: yield mark_safe('%s' % force_text(form[cl.model._meta.pk.name])) def mptt_results(cl): if cl.formset: for res, form in zip(cl.result_list, cl.formset.forms): yield list(mptt_items_for_result(cl, res, form)) else: for res in cl.result_list: yield list(mptt_items_for_result(cl, res, None)) def mptt_result_list(cl): """ Displays the headers and data list together """ return {'cl': cl, 'result_hidden_fields': list(result_hidden_fields(cl)), 'result_headers': list(result_headers(cl)), 'results': list(mptt_results(cl))} # custom template is merely so we can strip out sortable-ness from the column headers # Based on admin/change_list_results.html (1.3.1) if IS_GRAPPELLI_INSTALLED: mptt_result_list = register.inclusion_tag( "admin/grappelli_mptt_change_list_results.html")(mptt_result_list) else: mptt_result_list = register.inclusion_tag( "admin/mptt_change_list_results.html")(mptt_result_list) django-mptt-0.8.0/mptt/templatetags/mptt_tags.py0000644000076500000240000002357312633172611023271 0ustar cdestigterstaff00000000000000""" Template tags for working with lists of model instances which represent trees. """ from __future__ import unicode_literals from django import template from django.apps import apps from django.db.models.fields import FieldDoesNotExist from django.utils.encoding import force_text from django.utils.safestring import mark_safe from django.utils.translation import ugettext as _ from mptt.utils import (drilldown_tree_for_node, get_cached_trees, tree_item_iterator) register = template.Library() # ## ITERATIVE TAGS class FullTreeForModelNode(template.Node): def __init__(self, model, context_var): self.model = model self.context_var = context_var def render(self, context): cls = apps.get_model(*self.model.split('.')) if cls is None: raise template.TemplateSyntaxError( _('full_tree_for_model tag was given an invalid model: %s') % self.model ) context[self.context_var] = cls._tree_manager.all() return '' class DrilldownTreeForNodeNode(template.Node): def __init__(self, node, context_var, foreign_key=None, count_attr=None, cumulative=False): self.node = template.Variable(node) self.context_var = context_var self.foreign_key = foreign_key self.count_attr = count_attr self.cumulative = cumulative def render(self, context): # Let any VariableDoesNotExist raised bubble up args = [self.node.resolve(context)] if self.foreign_key is not None: app_label, model_name, fk_attr = self.foreign_key.split('.') cls = apps.get_model(app_label, model_name) if cls is None: raise template.TemplateSyntaxError( _('drilldown_tree_for_node tag was given an invalid model: %s') % '.'.join([app_label, model_name]) ) try: cls._meta.get_field(fk_attr) except FieldDoesNotExist: raise template.TemplateSyntaxError( _('drilldown_tree_for_node tag was given an invalid model field: %s') % fk_attr ) args.extend([cls, fk_attr, self.count_attr, self.cumulative]) context[self.context_var] = drilldown_tree_for_node(*args) return '' @register.tag def full_tree_for_model(parser, token): """ Populates a template variable with a ``QuerySet`` containing the full tree for a given model. Usage:: {% full_tree_for_model [model] as [varname] %} The model is specified in ``[appname].[modelname]`` format. Example:: {% full_tree_for_model tests.Genre as genres %} """ bits = token.contents.split() if len(bits) != 4: raise template.TemplateSyntaxError(_('%s tag requires three arguments') % bits[0]) if bits[2] != 'as': raise template.TemplateSyntaxError(_("second argument to %s tag must be 'as'") % bits[0]) return FullTreeForModelNode(bits[1], bits[3]) @register.tag('drilldown_tree_for_node') def do_drilldown_tree_for_node(parser, token): """ Populates a template variable with the drilldown tree for a given node, optionally counting the number of items associated with its children. A drilldown tree consists of a node's ancestors, itself and its immediate children. For example, a drilldown tree for a book category "Personal Finance" might look something like:: Books Business, Finance & Law Personal Finance Budgeting (220) Financial Planning (670) Usage:: {% drilldown_tree_for_node [node] as [varname] %} Extended usage:: {% drilldown_tree_for_node [node] as [varname] count [foreign_key] in [count_attr] %} {% drilldown_tree_for_node [node] as [varname] cumulative count [foreign_key] in [count_attr] %} The foreign key is specified in ``[appname].[modelname].[fieldname]`` format, where ``fieldname`` is the name of a field in the specified model which relates it to the given node's model. When this form is used, a ``count_attr`` attribute on each child of the given node in the drilldown tree will contain a count of the number of items associated with it through the given foreign key. If cumulative is also specified, this count will be for items related to the child node and all of its descendants. Examples:: {% drilldown_tree_for_node genre as drilldown %} {% drilldown_tree_for_node genre as drilldown count tests.Game.genre in game_count %} {% drilldown_tree_for_node genre as drilldown cumulative count tests.Game.genre in game_count %} """ # noqa bits = token.contents.split() len_bits = len(bits) if len_bits not in (4, 8, 9): raise template.TemplateSyntaxError( _('%s tag requires either three, seven or eight arguments') % bits[0]) if bits[2] != 'as': raise template.TemplateSyntaxError( _("second argument to %s tag must be 'as'") % bits[0]) if len_bits == 8: if bits[4] != 'count': raise template.TemplateSyntaxError( _("if seven arguments are given, fourth argument to %s tag must be 'with'") % bits[0]) if bits[6] != 'in': raise template.TemplateSyntaxError( _("if seven arguments are given, sixth argument to %s tag must be 'in'") % bits[0]) return DrilldownTreeForNodeNode(bits[1], bits[3], bits[5], bits[7]) elif len_bits == 9: if bits[4] != 'cumulative': raise template.TemplateSyntaxError( _("if eight arguments are given, fourth argument to %s tag must be 'cumulative'") % bits[0]) if bits[5] != 'count': raise template.TemplateSyntaxError( _("if eight arguments are given, fifth argument to %s tag must be 'count'") % bits[0]) if bits[7] != 'in': raise template.TemplateSyntaxError( _("if eight arguments are given, seventh argument to %s tag must be 'in'") % bits[0]) return DrilldownTreeForNodeNode(bits[1], bits[3], bits[6], bits[8], cumulative=True) else: return DrilldownTreeForNodeNode(bits[1], bits[3]) @register.filter def tree_info(items, features=None): """ Given a list of tree items, produces doubles of a tree item and a ``dict`` containing information about the tree structure around the item, with the following contents: new_level ``True`` if the current item is the start of a new level in the tree, ``False`` otherwise. closed_levels A list of levels which end after the current item. This will be an empty list if the next item is at the same level as the current item. Using this filter with unpacking in a ``{% for %}`` tag, you should have enough information about the tree structure to create a hierarchical representation of the tree. Example:: {% for genre,structure in genres|tree_info %} {% if structure.new_level %}
  • {% else %}
  • {% endif %} {{ genre.name }} {% for level in structure.closed_levels %}
{% endfor %} {% endfor %} """ kwargs = {} if features: feature_names = features.split(',') if 'ancestors' in feature_names: kwargs['ancestors'] = True return tree_item_iterator(items, **kwargs) @register.filter def tree_path(items, separator=' :: '): """ Creates a tree path represented by a list of ``items`` by joining the items with a ``separator``. Each path item will be coerced to unicode, so a list of model instances may be given if required. Example:: {{ some_list|tree_path }} {{ some_node.get_ancestors|tree_path:" > " }} """ return separator.join(force_text(i) for i in items) # ## RECURSIVE TAGS @register.filter def cache_tree_children(queryset): """ Alias to `mptt.utils.get_cached_trees`. """ return get_cached_trees(queryset) class RecurseTreeNode(template.Node): def __init__(self, template_nodes, queryset_var): self.template_nodes = template_nodes self.queryset_var = queryset_var def _render_node(self, context, node): bits = [] context.push() for child in node.get_children(): bits.append(self._render_node(context, child)) context['node'] = node context['children'] = mark_safe(''.join(bits)) rendered = self.template_nodes.render(context) context.pop() return rendered def render(self, context): queryset = self.queryset_var.resolve(context) roots = cache_tree_children(queryset) bits = [self._render_node(context, node) for node in roots] return ''.join(bits) @register.tag def recursetree(parser, token): """ Iterates over the nodes in the tree, and renders the contained block for each node. This tag will recursively render children into the template variable {{ children }}. Only one database query is required (children are cached for the whole tree) Usage:
    {% recursetree nodes %}
  • {{ node.name }} {% if not node.is_leaf_node %}
      {{ children }}
    {% endif %}
  • {% endrecursetree %}
""" bits = token.contents.split() if len(bits) != 2: raise template.TemplateSyntaxError(_('%s tag requires a queryset') % bits[0]) queryset_var = template.Variable(bits[1]) template_nodes = parser.parse(('endrecursetree',)) parser.delete_first_token() return RecurseTreeNode(template_nodes, queryset_var) django-mptt-0.8.0/mptt/utils.py0000644000076500000240000002375712633346036017745 0ustar cdestigterstaff00000000000000""" Utilities for working with lists of model instances which represent trees. """ from __future__ import unicode_literals import copy import csv import itertools import sys from django.utils.six import next, text_type from django.utils.six.moves import zip from django.utils.translation import ugettext as _ __all__ = ('previous_current_next', 'tree_item_iterator', 'drilldown_tree_for_node', 'get_cached_trees',) def previous_current_next(items): """ From http://www.wordaligned.org/articles/zippy-triples-served-with-python Creates an iterator which returns (previous, current, next) triples, with ``None`` filling in when there is no previous or next available. """ extend = itertools.chain([None], items, [None]) prev, cur, nex = itertools.tee(extend, 3) # Advancing an iterator twice when we know there are two items (the # two Nones at the start and at the end) will never fail except if # `items` is some funny StopIteration-raising generator. There's no point # in swallowing this exception. next(cur) next(nex) next(nex) return zip(prev, cur, nex) def tree_item_iterator(items, ancestors=False): """ Given a list of tree items, iterates over the list, generating two-tuples of the current tree item and a ``dict`` containing information about the tree structure around the item, with the following keys: ``'new_level'`` ``True`` if the current item is the start of a new level in the tree, ``False`` otherwise. ``'closed_levels'`` A list of levels which end after the current item. This will be an empty list if the next item is at the same level as the current item. If ``ancestors`` is ``True``, the following key will also be available: ``'ancestors'`` A list of unicode representations of the ancestors of the current node, in descending order (root node first, immediate parent last). For example: given the sample tree below, the contents of the list which would be available under the ``'ancestors'`` key are given on the right:: Books -> [] Sci-fi -> [u'Books'] Dystopian Futures -> [u'Books', u'Sci-fi'] """ structure = {} opts = None first_item_level = 0 for previous, current, next_ in previous_current_next(items): if opts is None: opts = current._mptt_meta current_level = getattr(current, opts.level_attr) if previous: structure['new_level'] = (getattr(previous, opts.level_attr) < current_level) if ancestors: # If the previous node was the end of any number of # levels, remove the appropriate number of ancestors # from the list. if structure['closed_levels']: structure['ancestors'] = \ structure['ancestors'][:-len(structure['closed_levels'])] # If the current node is the start of a new level, add its # parent to the ancestors list. if structure['new_level']: structure['ancestors'].append(text_type(previous)) else: structure['new_level'] = True if ancestors: # Set up the ancestors list on the first item structure['ancestors'] = [] first_item_level = current_level if next_: structure['closed_levels'] = list(range( current_level, getattr(next_, opts.level_attr), -1)) else: # All remaining levels need to be closed structure['closed_levels'] = list(range( current_level, first_item_level - 1, -1)) # Return a deep copy of the structure dict so this function can # be used in situations where the iterator is consumed # immediately. yield current, copy.deepcopy(structure) def drilldown_tree_for_node(node, rel_cls=None, rel_field=None, count_attr=None, cumulative=False): """ Creates a drilldown tree for the given node. A drilldown tree consists of a node's ancestors, itself and its immediate children, all in tree order. Optional arguments may be given to specify a ``Model`` class which is related to the node's class, for the purpose of adding related item counts to the node's children: ``rel_cls`` A ``Model`` class which has a relation to the node's class. ``rel_field`` The name of the field in ``rel_cls`` which holds the relation to the node's class. ``count_attr`` The name of an attribute which should be added to each child in the drilldown tree, containing a count of how many instances of ``rel_cls`` are related through ``rel_field``. ``cumulative`` If ``True``, the count will be for each child and all of its descendants, otherwise it will be for each child itself. """ if rel_cls and rel_field and count_attr: children = node._tree_manager.add_related_count( node.get_children(), rel_cls, rel_field, count_attr, cumulative) else: children = node.get_children() return itertools.chain(node.get_ancestors(), [node], children) def print_debug_info(qs, file=None): """ Given an mptt queryset, prints some debug information to stdout. Use this when things go wrong. Please include the output from this method when filing bug issues. """ opts = qs.model._mptt_meta writer = csv.writer(sys.stdout if file is None else file) header = ( 'pk', opts.level_attr, '%s_id' % opts.parent_attr, opts.tree_id_attr, opts.left_attr, opts.right_attr, 'pretty', ) writer.writerow(header) for n in qs.order_by('tree_id', 'lft'): level = getattr(n, opts.level_attr) row = [] for field in header[:-1]: row.append(getattr(n, field)) row.append('%s%s' % ('- ' * level, text_type(n).encode('utf-8'))) writer.writerow(row) def _get_tree_model(model_class): # Find the model that contains the tree fields. # This is a weird way of going about it, but Django doesn't let us access # the fields list to detect where the tree fields actually are, # because the app cache hasn't been loaded yet. # So, it *should* be the *last* concrete MPTTModel subclass in the mro(). bases = list(model_class.mro()) while bases: b = bases.pop() # NOTE can't use `issubclass(b, MPTTModel)` here because we can't import MPTTModel yet! # So hasattr(b, '_mptt_meta') will have to do. if hasattr(b, '_mptt_meta') and not (b._meta.abstract or b._meta.proxy): return b return None def get_cached_trees(queryset): """ 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. NOTE: nodes _must_ be passed in the correct (depth-first) order. If they aren't, a ValueError will be raised. 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()` """ current_path = [] top_nodes = [] if queryset: # Get the model's parent-attribute name parent_attr = queryset[0]._mptt_meta.parent_attr root_level = None for obj in queryset: # Get the current mptt node level node_level = obj.get_level() if root_level is None: # First iteration, so set the root level to the top node level root_level = node_level if node_level < root_level: # ``queryset`` was a list or other iterable (unable to order), # and was provided in an order other than depth-first raise ValueError( _('Node %s not in depth-first order') % (type(queryset),) ) # Set up the attribute on the node that will store cached children, # which is used by ``MPTTModel.get_children`` obj._cached_children = [] # Remove nodes not in the current branch while len(current_path) > node_level - root_level: current_path.pop(-1) if node_level == root_level: # Add the root to the list of top nodes, which will be returned top_nodes.append(obj) else: # Cache the parent on the current node, and attach the current # node to the parent's list of children _parent = current_path[-1] setattr(obj, parent_attr, _parent) _parent._cached_children.append(obj) if root_level == 0: # get_ancestors() can use .parent.parent.parent... setattr(obj, '_mptt_use_cached_ancestors', True) # Add the current node to end of the current path - the last node # in the current path is the parent for the next iteration, unless # the next iteration is higher up the tree (a new branch), in which # case the paths below it (e.g., this one) will be removed from the # current path during the next iteration current_path.append(obj) return top_nodes django-mptt-0.8.0/NOTES0000644000076500000240000004522212455555636016063 0ustar cdestigterstaff00000000000000============================= Django MPTT Development Notes ============================= This document contains notes related to use cases/reasoning behind and implementation details for Django MPTT features. I've not worked with this particular kind of hierarchical data structure before to any degree of complexity, so you can consider this my "working out" :) Reparenting ----------- Since it's not unreasonable to assume a good percentage of the people who use this application will also be using the ``django.contrib.admin`` application or ``forms.ModelForm`` to edit their data, and since in these cases only the parent field will be editable if users have allowed ``mptt.register`` to create tree fields for them, it would be nice if Django MPTT automatically took care of the tree when a ``Model`` instance has its parent changed. When the parent of a tree node is changed, its left, right, level and tree id may also be updated to keep the integrity of the tree structure intact. In this case, we'll assume the node which was changed should become the last child of its parent. The following diagram depicts a representation of a nested set which we'll base some basic reparenting examples on - hopefully, by observing the resulting tree structures, we can come up with algorithms for different reparenting scenarios:: __________________________________________________________________________ | Root 1 | | ________________________________ ________________________________ | | | Child 1.1 | | Child 1.2 | | | | ___________ ___________ | | ___________ ___________ | | | | | C 1.1.1 | | C 1.1.2 | | | | C 1.2.1 | | C 1.2.2 | | | 1 2 3___________4 5___________6 7 8 9___________10 11__________12 13 14 | |________________________________| |________________________________| | |__________________________________________________________________________| Extract Root Node (Remove Parent) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If the node's previous parent was not ``None`` and the node's new parent is ``None``, we need to make it into a new root. For example, were we to change Child 1.2's parent to ``None``, we should end up with the following structure:: ______________________________________ | Root 1 | | ________________________________ | | | Child 1.1 | | | | ___________ ___________ | | | | | C 1.1.1 | | C 1.1.2 | | | 1 2 3___________4 5___________6 7 8 | |________________________________| | |______________________________________| ____________________________________ | Root 2 | | _____________ _____________ | | | Child 2.1 | | Child 2.2 | | 1 2_____________3 4_____________5 6 |____________________________________| The following steps should translate an existing node and its descendants into the new format required: 1. The new root node's level will have to become ``0``, and the levels of any descendants will decrease by the same amount, so just subtract the root node's current level from all affected nodes:: new_level = current_level - new_root_node.level 2. The new root node's left value will have to become ``1``. Since the node's number of descendants hasn't changed, we can simply use the node's current left value to adjust all left and right values by the amount required:: new_left = current_left - new_root_node.left + 1 new_right = current_right - new_root_node.left + 1 3. A new tree id will be generated for the new root node, so update the node and all descendants with this value. This can be expressed as a single SQL query:: UPDATE nodes SET level = level - [NODE_LEVEL], left = left - [NODE_LEFT - 1], right = right - [NODE_LEFT - 1], tree_id = [NEW_TREE_ID] WHERE left BETWEEN [NODE_LEFT] AND [NODE_RIGHT] AND tree_id = [CURRENT_TREE_ID] Now we have to fix the original tree, as there's a hole the size of the node we just moved. We can calculate the size of the gap using the node's left and right values, updating the original tree accordingly:: UPDATE nodes SET left = left - [NODE_RIGHT - NODE_LEFT + 1] WHERE left > [NODE_LEFT] AND tree_id = [CURRENT_TREE_ID] UPDATE nodes SET right = right - [NODE_RIGHT - NODE_LEFT + 1] WHERE right > [NODE_RIGHT] AND tree_id = [CURRENT_TREE_ID] Insert Tree (Add Parent) ~~~~~~~~~~~~~~~~~~~~~~~~ If the node's previous parent was ``None`` and the node's new parent is not ``None``, we need to move the entire tree it was the root node for. First, we need to make some space for the tree to be moved into the new parent's tree. This is the same as the process used when creating a new child node, where we add ``2`` to all left and right values which are greater than or equal to the right value of the parent into which the new node will be inserted. In this case, we want to use the width of the tree we'll be inserting, which is ``right - left + 1``. For any node without children, this would be ``2``, which is why we add that amount when creating a new node. This seems like the kind of operation which could be extracted out into a reusable function at some stage. Steps to translate the node and its descendants to the new format required: 1. The node's level (``0``, as it's a root node) will have to become one greater than its new parent's level. We can add this amount to the node and all its descendants to get the correct levels:: new_level = current_level + new_parent.level + 1 2. The node's left value (``1``, as it's a root node) will have to become the current right value of its new parent (look at the diagrams above if this doesn't make sense - imagine inserting Root 2 back into Root 1). Add the difference between the node's left value and the new parent's right value to all left and right values of the node and its descendants:: new_left = current_left + new_parent.right - 1 new_right = current_right + new_parent.right - 1 3. Update the node and all descendants with the tree id of the tree they're moving to. This is a similar query to that used when creating new root nodes from existing child nodes. We can omit the left value from the ``WHERE`` statement in this case, since we'll be operating on a whole tree, but this also looks like something which could be extracted into a reusable function at some stage:: UPDATE nodes SET level = level + [PARENT_LEVEL + 1], left = left + [PARENT_RIGHT - 1], right = right + [PARENT_RIGHT - 1], tree_id = [PARENT_TREE_ID] WHERE tree_id = [CURRENT_TREE_ID] Move Within Tree (Change Parent, Same Tree) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Original Tree:: __________________________________________________________________________ | Root 1 | | ________________________________ ________________________________ | | | Child 1.1 | | Child 1.2 | | | | ___________ ___________ | | ___________ ___________ | | | | | C 1.1.1 | | C 1.1.2 | | | | C 1.2.1 | | C 1.2.2 | | | 1 2 3___________4 5___________6 7 8 9___________10 11__________12 13 14 | |________________________________| |________________________________| | |__________________________________________________________________________| C 1.2.2 -> Root 1:: ____________________________________________________________________________ | Root 1 | | ________________________________ _________________ _____________ | | | Child 1.1 | | Child 1.2 | | Child 1.3 | | | | ___________ ___________ | | ___________ | | | | | | | C 1.1.1 | | C 1.1.2 | | | | C 1.2.1 | | | | | 1 2 3___________4 5___________6 7 8 9___________10 11 12 13 14 | |________________________________| |_________________| |_____________| | |____________________________________________________________________________| |________________| | Affected area old_left = 11, new_left = 12 old_right = 12, new_right = 13 left_right_change = 1 target_left = 1, target_right = 14 all other affected lefts and rights decreased by 2 C 1.2.2 -> Child 1.1:: __________________________________________________________________________ | Root 1 | | _______________________________________________ _________________ | | | Child 1.1 | | Child 1.2 | | | | ___________ ___________ ___________ | | ___________ | | | | | C 1.1.1 | | C 1.1.2 | | C 1.1.3 | | | | C 1.2.1 | | | 1 2 3___________4 5___________6 7___________8 9 10 11__________12 13 14 | |_______________________________________________| |_________________| | |__________________________________________________________________________| |________________________________| | Affected area old_left = 11, new_left = 7 old_right = 12, new_right = 8 left_right_change = -4 target_left = 2, target_right = 7 all other affected lefts and rights increased by 2 C 1.1.2 -> Root 1:: ____________________________________________________________________________ | Root 1 | | _________________ ________________________________ _____________ | | | Child 1.1 | | Child 1.2 | | Child 1.3 | | | | ___________ | | ___________ ___________ | | | | | | | C 1.1.1 | | | | C 1.2.1 | | C 1.2.2 | | | | | 1 2 3___________4 5 6 7___________8 9___________10 11 12 13 14 | |_________________| |________________________________| |_____________| | |____________________________________________________________________________| |____________________________________________________| | Affected area old_left = 5, new_left = 12 old_right = 6, new_right = 13 left_right_change = 7 target_left = 1, target_right = 14 all other affected lefts and rights decreased by 2 Child 1.2 -> Child 1.1:: ______________________________________________________________________________ | Root 1 | | ________________________________________________________________________ | | | Child 1.1 | | | | ___________ ___________ ____________________________________ | | | | | C 1.1.1 | | C 1.1.2 | | C 1.1.3 | | | | | | | | | | _____________ _____________ | | | | | | | | | | | C 1.1.3.1 | | C 1.1.3.2 | | | | 1 2 3 4 5 6 7 8_____________9 10____________11 12 13 14 | | |___________| |___________| |____________________________________| | | | |________________________________________________________________________| | |______________________________________________________________________________| |_______________________________________| | Affected area old_left = 8, new_left = 7 old_right = 13, new_right = 12 left_right_change = -1 target_left = 2, target_right = 7 all other affected lefts and rights increased by 6 From the diagrams above, the area affected by moving a subtree within the same tree appears to be confined to the section of the tree between the subtree's lower and upper bounds of the subtree's old and new left and right values. Affected nodes which are not in the subtree being moved appear to be changed by the width of the subtree, with a sign inverse to that of the left_right_change. Node Movement ------------- For automatic reparenting, we've been making the node which has had its parent changed the last child of its new parent node but, outside of that, we may want to position a node in other ways relative to a given target node, say to make it the target node's immediate sibling on either side or its first child. Drawing those trees was pretty tedious, so we'll use this kind of tree representation from now on, as seen in the tests. In order, the fields listed are: id, parent_id, tree_id, level, left, right:: 1 - 1 0 1 14 1 2 1 1 1 2 7 2 3 2 1 2 3 4 3 4 2 1 2 5 6 4 5 1 1 1 8 13 5 6 5 1 2 9 10 6 7 5 1 2 11 12 7 Same Tree, Children ~~~~~~~~~~~~~~~~~~~ Last Child Calculation (derived from previous trees):: if target_right > right: new_left = target_right - subtree_width new_right = target_right - 1 else: new_left = target_right new_right = target_right + subtree_width - 1 Moving "up" the tree:: 1 - 1 0 1 14 1 2 1 1 1 2 9 2 7 2 1 2 3 4 => 7 3 2 1 2 5 6 3 4 2 1 2 7 8 4 5 1 1 1 10 13 5 6 5 1 2 11 12 6 node = 7 target_node = 2 left = 11, right = 12 new_left = 3, new_right = 4 target_left = 2, target_right = 7 affected area = 3 to 12 all other affected lefts and rights increased by 2 1 - 1 0 1 14 1 2 1 1 1 2 13 2 5 2 1 2 3 8 => 5 6 5 1 3 4 5 6 7 5 1 3 6 7 7 3 2 1 2 9 10 3 4 2 1 2 11 12 4 node = 5 target_node = 2 left = 8, right = 13 new_left = 3, new_right = 8 target_left = 2, target_right = 7 affected area = 3 to 13 all other affected lefts and rights increased by 6 Moving "down" the tree:: 1 - 1 0 1 14 1 2 1 1 1 2 5 2 3 2 1 2 3 4 3 5 1 1 1 6 13 5 4 5 1 2 7 8 => 4 6 5 1 2 9 10 6 7 5 1 2 11 12 7 node = 4 target_node = 5 left = 5, right = 6 new_left = 7, new_right = 8 target_left = 8, target_right = 13 affected area = 5 to 8 all other affected lefts and rights decreased by 2 1 - 1 0 1 14 1 5 1 1 1 2 13 5 2 5 1 2 3 8 => 2 3 2 1 3 4 5 3 4 2 1 3 6 7 4 6 5 1 2 9 10 6 7 5 1 2 11 12 7 node = 2 target_node = 5 left = 2, right = 9 new_left = 3, new_right = 8 target_left = 8, target_right = 13 affected area = 2 to 8 all other affected lefts and rights decreased by 6 First Child Calculation:: if target_left > left: new_left = target_left - subtree_width + 1 new_right = target_left else: new_left = target_left + 1 new_right = target_left + subtree_width Same Tree, Siblings ~~~~~~~~~~~~~~~~~~~ Moving "up" the tree:: 1 - 1 0 1 14 1 2 1 1 1 2 9 2 3 2 1 2 3 4 3 7 2 1 2 5 6 => 7 4 2 1 2 7 8 4 5 1 1 1 10 13 5 6 5 1 2 11 12 6 Left sibling: node = 7 target_node = 4 left = 11, right = 12 new_left = 5, new_right = 6 target_left = 5, target_right = 6 affected area = 5 to 12 all other affected lefts and rights increased by 2 Right sibling: node = 7 target_node = 3 left = 11, right = 12 new_left = 5, new_right = 6 target_left = 3, target_right = 4 affected area = 3 to 12 all other affected lefts and rights increased by 2 1 - 1 0 1 14 1 2 1 1 1 2 13 2 3 2 1 2 3 4 3 5 2 1 2 5 10 => 5 6 5 1 3 6 7 6 7 5 1 3 8 9 7 4 2 1 2 11 12 4 Left sibling: node = 5 target_node = 4 left = 8, right = 13 new_left = 5, new_right = 10 target_left = 5, target_right = 6 affected area = 5 to 13 all other affected lefts and rights increased by 6 Right sibling: node = 5 target_node = 3 left = 8, right = 13 new_left = 5, new_right = 10 target_left = 3, target_right = 4 affected area = 3 to 13 all other affected lefts and rights increased by 6 Moving "down" the tree:: 1 - 1 0 1 14 1 2 1 1 1 2 5 2 4 2 1 2 3 4 4 5 1 1 1 6 13 5 6 5 1 2 7 8 6 3 5 1 2 9 10 => 3 7 5 1 2 11 12 7 Left sibling: node = 3 target_node = 7 left = 3, right = 4 new_left = 9, new_right = 10 target_left = 11, target_right = 12 affected area = 4 to 10 all other affected lefts and rights decreased by 2 Right sibling: node = 3 target_node = 6 left = 3, right = 4 new_left = 9, new_right = 10 target_left = 9, target_right = 10 affected area = 4 to 10 all other affected lefts and rights decreased by 2 1 - 1 0 1 14 1 5 1 1 1 2 13 5 6 5 1 2 3 4 6 2 6 1 2 5 10 => 2 3 2 1 3 6 7 3 4 2 1 3 8 9 4 7 5 1 2 11 12 7 Left sibling: node = 2 target_node = 7 left = 2, right = 7 new_left = 5, new_right = 10 target_left = 11, target_right = 12 affected area = 2 to 10 all other affected lefts and rights decreased by 6 Right sibling: node = 2 target_node = 6 left = 2, right = 7 new_left = 5, new_right = 10 target_left = 9, target_right = 10 affected area = 2 to 10 all other affected lefts and rights decreased by 6 Derived Calculations:: Left sibling: if target_left > left: new_left = target_left - subtree_width new_right = target_left - 1 else: new_left = target_left new_right = target_left + subtree_width - 1 if target_right > right: new_left = target_right - subtree_width + 1 new_right = target_right else: new_left = target_right + 1 new_right = target_right + subtree_width django-mptt-0.8.0/PKG-INFO0000644000076500000240000000216112635633365016334 0ustar cdestigterstaff00000000000000Metadata-Version: 1.1 Name: django-mptt 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 Author: Craig de Stigter Author-email: craig.ds@gmail.com License: UNKNOWN Description: UNKNOWN Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Environment :: Web Environment Classifier: Framework :: Django Classifier: Intended Audience :: Developers 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.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.2 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Utilities django-mptt-0.8.0/README.rst0000644000076500000240000000521712632166014016720 0ustar cdestigterstaff00000000000000=========== django-mptt =========== Utilities for implementing Modified Preorder Tree Traversal with your Django Models and working with trees of Model instances. .. image:: https://secure.travis-ci.org/django-mptt/django-mptt.png?branch=master :alt: Build Status :target: https://travis-ci.org/django-mptt/django-mptt Project home: http://github.com/django-mptt/django-mptt/ Documentation: http://django-mptt.github.io/django-mptt/ Discussion group: http://groups.google.com/group/django-mptt-dev What is Modified Preorder Tree Traversal? ========================================= MPTT is a technique for storing hierarchical data in a database. The aim is to make retrieval operations very efficient. The trade-off for this efficiency is that performing inserts and moving items around the tree is more involved, as there's some extra work required to keep the tree structure in a good state at all times. Here are a few articles about MPTT to whet your appetite and provide details about how the technique itself works: * `Trees in SQL`_ * `Storing Hierarchical Data in a Database`_ * `Managing Hierarchical Data in MySQL`_ .. _`Trees in SQL`: http://www.ibase.ru/devinfo/DBMSTrees/sqltrees.html .. _`Storing Hierarchical Data in a Database`: http://www.sitepoint.com/print/hierarchical-data-database What is ``django-mptt``? ======================== ``django-mptt`` is a reusable Django app which aims to make it easy for you to use MPTT with your own Django models. It takes care of the details of managing a database table as a tree structure and provides tools for working with trees of model instances. Requirements ------------ * Python 2.7 or 3.2+ * A supported version of django (currently 1.8+) Feature overview ---------------- * Simple registration of models - fields required for tree structure will be added automatically. * The tree structure is automatically updated when you create or delete model instances, or change an instance's parent. * Each level of the tree is automatically sorted by a field (or fields) of your choice. * New model methods are added to each registered model for: * changing position in the tree * retrieving ancestors, siblings, descendants * counting descendants * other tree-related operations * A ``TreeManager`` manager is added to all registered models. This provides methods to: * move nodes around a tree, or into a different tree * insert a node anywhere in a tree * rebuild the MPTT fields for the tree (useful when you do bulk updates outside of django) * Form fields for tree models. * Utility functions for tree models. * Template tags and filters for rendering trees. django-mptt-0.8.0/setup.cfg0000644000076500000240000000024312635633365017057 0ustar cdestigterstaff00000000000000[flake8] exclude = venv,.tox,migrations,docs/conf.py max-line-length = 99 [bdist_wheel] universal = 1 [egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 django-mptt-0.8.0/setup.py0000755000076500000240000000366112635466021016753 0ustar cdestigterstaff00000000000000#!/usr/bin/env python from __future__ import unicode_literals from mptt import VERSION requires = (str('Django>=1.8'),) try: from setuptools import setup kwargs = {str('install_requires'): requires} except ImportError: from distutils.core import setup kwargs = {str('requires'): requires} # Dynamically calculate the version based on mptt.VERSION version_tuple = VERSION version = ".".join(str(v) for v in version_tuple) # on py3, all these are text strings # on py2, they're all byte strings. # ... and that's how setuptools likes it. setup( name=str('django-mptt'), description=str('''Utilities for implementing Modified Preorder Tree Traversal with your Django Models and working with trees of Model instances.'''), version=version, author=str('Craig de Stigter'), author_email=str('craig.ds@gmail.com'), url=str('http://github.com/django-mptt/django-mptt'), packages=[str('mptt'), str('mptt.templatetags')], test_requires=('mock-django>=0.6.7',), package_data={str('mptt'): [str('templates/admin/*'), str('locale/*/*/*.*')]}, classifiers=[ str('Development Status :: 4 - Beta'), str('Environment :: Web Environment'), str('Framework :: Django'), str('Intended Audience :: Developers'), str('License :: OSI Approved :: MIT License'), str('Operating System :: OS Independent'), str('Programming Language :: Python'), str("Programming Language :: Python :: 2"), str("Programming Language :: Python :: 2.7"), str("Programming Language :: Python :: 3"), str("Programming Language :: Python :: 3.2"), str("Programming Language :: Python :: 3.3"), str("Programming Language :: Python :: 3.4"), str("Programming Language :: Python :: Implementation :: CPython"), str("Programming Language :: Python :: Implementation :: PyPy"), str('Topic :: Utilities'), ], **kwargs ) django-mptt-0.8.0/tests/0000755000076500000240000000000012635633365016401 5ustar cdestigterstaff00000000000000django-mptt-0.8.0/tests/.coveragerc0000644000076500000240000000067112515002575020514 0ustar cdestigterstaff00000000000000[run] include = */mptt/* [report] exclude_lines = pragma: no cover # Don't complain about missing debug-only code: def __unicode__ def __repr__ if self\.debug # Don't complain if tests don't hit defensive assertion code: raise AssertionError raise NotImplementedError # Don't complain if non-runnable code isn't run: if 0: if __name__ == .__main__.: omit = */tests/* show_missing = True django-mptt-0.8.0/tests/.gitignore0000644000076500000240000000002212515002575020351 0ustar cdestigterstaff00000000000000.coverage htmlcov django-mptt-0.8.0/tests/__init__.py0000644000076500000240000000005012462512456020500 0ustar cdestigterstaff00000000000000from __future__ import unicode_literals django-mptt-0.8.0/tests/__init__.pyc0000644000076500000240000000034212635626177020657 0ustar cdestigterstaff00000000000000 .TcsddlmZdS(i(sunicode_literalsN(t __future__tunicode_literals(((t8/Users/cdestigter/checkout/django-mptt/tests/__init__.pytsdjango-mptt-0.8.0/tests/__pycache__/0000755000076500000240000000000012635633365020611 5ustar cdestigterstaff00000000000000django-mptt-0.8.0/tests/__pycache__/__init__.cpython-32.pyc0000664000076500000240000000034512226574507024774 0ustar cdestigterstaff00000000000000l IuRc@sddlmZdS(i(uunicode_literalsN(u __future__uunicode_literals(((u7/home/cdestigter/checkout/django-mptt/tests/__init__.pyusdjango-mptt-0.8.0/tests/__pycache__/__init__.cpython-33.pyc0000644000076500000240000000035212465236477024777 0ustar cdestigterstaff00000000000000 .T(c@sddlmZdS(i(uunicode_literalsN(u __future__uunicode_literals(((u8/Users/cdestigter/checkout/django-mptt/tests/__init__.pyusdjango-mptt-0.8.0/tests/__pycache__/__init__.cpython-34.pyc0000644000076500000240000000030212601577276024770 0ustar cdestigterstaff00000000000000 .T(@sddlmZdS))unicode_literalsN) __future__rrr8/Users/cdestigter/checkout/django-mptt/tests/__init__.pysdjango-mptt-0.8.0/tests/__pycache__/settings.cpython-32.pyc0000664000076500000240000000133112226574455025073 0ustar cdestigterstaff00000000000000l IuRc@seddlmZddlZejjeZdZiidd6dd6d6Z dZ d Z d Z dS(i(uunicode_literalsNudjango.db.backends.sqlite3uENGINEu mydatabaseuNAMEudefaultudjango.contrib.authudjango.contrib.contenttypesumpttumyappu/static/uabc123T(udjango.contrib.authudjango.contrib.contenttypesumpttumyapp( u __future__uunicode_literalsuosupathudirnameu__file__uDIRNAMEuTrueuDEBUGu DATABASESuINSTALLED_APPSu STATIC_URLu SECRET_KEY(((u7/home/cdestigter/checkout/django-mptt/tests/settings.pyus django-mptt-0.8.0/tests/__pycache__/settings.cpython-33.pyc0000644000076500000240000000422512522224015025056 0ustar cdestigterstaff00000000000000 }4Uc@swddlmZddlZejjeZdZiidd6dd6d6Z dZ dZ dZ d Z d!ZdZdS("i(uunicode_literalsNudjango.db.backends.sqlite3uENGINEu mydatabaseuNAMEudefaultudjango.contrib.adminudjango.contrib.authudjango.contrib.contenttypesudjango.contrib.messagesudjango.contrib.sessionsumpttumyappu/static/uabc123u)django.middleware.common.CommonMiddlewareu4django.contrib.sessions.middleware.SessionMiddlewareu)django.middleware.csrf.CsrfViewMiddlewareu7django.contrib.auth.middleware.AuthenticationMiddlewareu4django.contrib.messages.middleware.MessageMiddlewareu+django.contrib.auth.context_processors.authu$django.core.context_processors.debugu#django.core.context_processors.i18nu$django.core.context_processors.mediau%django.core.context_processors.staticu&django.core.context_processors.requestu3django.contrib.messages.context_processors.messagesu myapp.urlsT(udjango.contrib.adminudjango.contrib.authudjango.contrib.contenttypesudjango.contrib.messagesudjango.contrib.sessionsumpttumyapp(u)django.middleware.common.CommonMiddlewareu4django.contrib.sessions.middleware.SessionMiddlewareu)django.middleware.csrf.CsrfViewMiddlewareu7django.contrib.auth.middleware.AuthenticationMiddlewareu4django.contrib.messages.middleware.MessageMiddleware(u+django.contrib.auth.context_processors.authu$django.core.context_processors.debugu#django.core.context_processors.i18nu$django.core.context_processors.mediau%django.core.context_processors.staticu&django.core.context_processors.requestu3django.contrib.messages.context_processors.messages(u __future__uunicode_literalsuosupathudirnameu__file__uDIRNAMEuTrueuDEBUGu DATABASESuINSTALLED_APPSu STATIC_URLu SECRET_KEYuMIDDLEWARE_CLASSESuTEMPLATE_CONTEXT_PROCESSORSu ROOT_URLCONF(((u8/Users/cdestigter/checkout/django-mptt/tests/settings.pyus: django-mptt-0.8.0/tests/__pycache__/settings.cpython-34.pyc0000644000076500000240000000273412635100363025066 0ustar cdestigterstaff00000000000000 8urV@sddlmZddlZejjeZdZiidd6dd6d6Zd$Z dZ dZ d%Z idd6gd6dd6iddddgd 6d!6gZ d"Zd#ZdS)&)unicode_literalsNTzdjango.db.backends.sqlite3ENGINEZ mydatabaseNAMEdefaultdjango.contrib.admindjango.contrib.authdjango.contrib.contenttypesdjango.contrib.messagesdjango.contrib.sessionsmpttmyappz/static/Zabc123)django.middleware.common.CommonMiddleware4django.contrib.sessions.middleware.SessionMiddleware)django.middleware.csrf.CsrfViewMiddleware7django.contrib.auth.middleware.AuthenticationMiddleware4django.contrib.messages.middleware.MessageMiddleware>django.contrib.auth.middleware.SessionAuthenticationMiddlewarez/django.template.backends.django.DjangoTemplatesBACKENDZDIRSZAPP_DIRSz(django.template.context_processors.debugz*django.template.context_processors.requestz+django.contrib.auth.context_processors.authz3django.contrib.messages.context_processors.messagesZcontext_processorsOPTIONSz myapp.urlszmyapp.SwappedInModel)rrrr r r r )r rrrrr) __future__rospathdirname__file__ZDIRNAMEDEBUG DATABASESINSTALLED_APPS STATIC_URL SECRET_KEYMIDDLEWARE_CLASSES TEMPLATESZ ROOT_URLCONFZMPTT_SWAPPABLE_MODELr!r!8/Users/cdestigter/checkout/django-mptt/tests/settings.pysB django-mptt-0.8.0/tests/myapp/0000755000076500000240000000000012635633365017527 5ustar cdestigterstaff00000000000000django-mptt-0.8.0/tests/myapp/__init__.py0000644000076500000240000000005012462512456021626 0ustar cdestigterstaff00000000000000from __future__ import unicode_literals django-mptt-0.8.0/tests/myapp/__init__.pyc0000644000076500000240000000035012635626177022004 0ustar cdestigterstaff00000000000000 .TcsddlmZdS(i(sunicode_literalsN(t __future__tunicode_literals(((t>/Users/cdestigter/checkout/django-mptt/tests/myapp/__init__.pytsdjango-mptt-0.8.0/tests/myapp/__pycache__/0000755000076500000240000000000012635633365021737 5ustar cdestigterstaff00000000000000django-mptt-0.8.0/tests/myapp/__pycache__/__init__.cpython-32.pyc0000664000076500000240000000035312226574455026123 0ustar cdestigterstaff00000000000000l IuRc@sddlmZdS(i(uunicode_literalsN(u __future__uunicode_literals(((u=/home/cdestigter/checkout/django-mptt/tests/myapp/__init__.pyusdjango-mptt-0.8.0/tests/myapp/__pycache__/__init__.cpython-33.pyc0000644000076500000240000000036012465236017026112 0ustar cdestigterstaff00000000000000 .T(c@sddlmZdS(i(uunicode_literalsN(u __future__uunicode_literals(((u>/Users/cdestigter/checkout/django-mptt/tests/myapp/__init__.pyusdjango-mptt-0.8.0/tests/myapp/__pycache__/__init__.cpython-34.pyc0000644000076500000240000000031012601577276026115 0ustar cdestigterstaff00000000000000 .T(@sddlmZdS))unicode_literalsN) __future__rrr>/Users/cdestigter/checkout/django-mptt/tests/myapp/__init__.pysdjango-mptt-0.8.0/tests/myapp/__pycache__/admin.cpython-34.pyc0000644000076500000240000000076112601577276025460 0ustar cdestigterstaff00000000000000 }4U@s]ddlmZddlmZddlmZGdddeZejjeedS))admin)MPTTModelAdmin)Categoryc@seZdZdS) CategoryAdminN)__name__ __module__ __qualname__r r ;/Users/cdestigter/checkout/django-mptt/tests/myapp/admin.pyrs rN) django.contribrZ mptt.adminr myapp.modelsrrsiteregisterr r r r sdjango-mptt-0.8.0/tests/myapp/__pycache__/models.cpython-32.pyc0000664000076500000240000003364012226575732025653 0ustar cdestigterstaff00000000000000l WZRc@sddlmZddlmZddlmZddlmZddlZddl m Z m Z ddl m Z Gdd e ZeGd d e ZeGd d e ZGdde ZeGdde ZGdde ZeGdde ZGdde ZGdde ZeGdde ZGddeZeGdde ZGd d!e ZGd"d#eZGd$d%e ZGd&d'eZGd(d)e ZGd*d+eZGd,d-eZ Gd.d/eZ!Gd0d1eZ"Gd2d3eZ#Gd4d5e#Z$Gd6d7e Z%e ed8d=d9d=j'ed:ej(ed;d>dS(?i(uunicode_literals(uGroup(umodels(upython_2_unicode_compatibleN(u MPTTModeluTreeForeignKey(u TreeManagercBs|EeZdS(N(u__name__u __module__(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuCustomTreeManager s uCustomTreeManagercsY|EeZejddZejddd dd ddZdZfdZ S( u max_lengthi2uselfunullublanku related_nameuchildrencCs|jS(N(uname(uself((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyu__str__scstt|jdS(N(usuperuCategoryudelete(uself(u __class__(u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyudeletesT( u__name__u __module__umodelsu CharFieldunameu ForeignKeyuTrueuparentu__str__udelete(u __locals__((u __class__u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuCategorys ! uCategorycBsP|EeZejdddd Zejddd dd ddZdZd S( u max_lengthi2uuniqueuselfunullublanku related_nameuchildrencCs|jS(N(uname(uself((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyu__str__ sNT( u__name__u __module__umodelsu CharFielduTrueunameu ForeignKeyuparentu__str__(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuGenres !uGenrecBs/|EeZejdddddddZdS(uselfunullublanku related_nameuchildrenNT(u__name__u __module__umodelsu ForeignKeyuTrueuparent(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuInsert$s uInsertcBsr|EeZejddZejZejZej ddd dd ddZ GddZ d Z d S( u max_lengthi2uselfunullublanku related_nameuchildrencBs|EeZdddgZdS(unameusizeu-dateN(u__name__u __module__uorder_insertion_by(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuMPTTMeta/s uMPTTMetacCs|jS(N(uname(uself((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyu__str__2sNT(u__name__u __module__umodelsu CharFieldunameuPositiveIntegerFieldusizeu DateFieldudateu ForeignKeyuTrueuparentuMPTTMetau__str__(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyu MultiOrder(s   !u MultiOrdercBs?|EeZejdddddddZGddZdS( uselfunullublanku related_nameuchildrencBs&|EeZdZdZdZdZdS(udoesuzisumadnessuworkN(u__name__u __module__u left_attru right_attru level_attru tree_id_attr(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuMPTTMeta9s uMPTTMetaNT(u__name__u __module__umodelsu ForeignKeyuTrueuparentuMPTTMeta(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuNode6s !uNodecBsZ|EeZejddZejddd dd ddZGddZd Z d S( u max_lengthi2uselfunullublanku related_nameuchildrencBs|EeZdgZdS(unameN(u__name__u __module__uorder_insertion_by(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuMPTTMetaEs uMPTTMetacCs|jS(N(uname(uself((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyu__str__HsNT( u__name__u __module__umodelsu CharFieldunameu ForeignKeyuTrueuparentuMPTTMetau__str__(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuOrderedInsertion@s !uOrderedInsertioncBs/|EeZejdddddddZdS(uselfunullublanku related_nameuchildrenNT(u__name__u __module__umodelsu ForeignKeyuTrueuparent(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuTreeLs uTreecBsB|EeZejdddddddZGddeZdS( uselfunullublanku related_nameuchildrencBs|EeZdZdS(utestingN(u__name__u __module__u left_attr(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuMPTTMetaSs uMPTTMetaNT(u__name__u __module__umodelsu ForeignKeyuTrueuparentuobjectuMPTTMeta(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuNewStyleMPTTMetaPs !uNewStyleMPTTMetacBs_|EeZejddZejddd dd ddZejZ e Z dZ dS( u max_lengthi2uselfunullublanku related_nameuchildrencCs|jS(N(uname(uself((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyu__str__`sNT( u__name__u __module__umodelsu CharFieldunameu ForeignKeyuTrueuparentuManageruobjectsuCustomTreeManagerumy_tree_manageru__str__(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuPersonWs !  uPersoncBs |EeZejddZdS(u max_lengthi2N(u__name__u __module__umodelsu CharFieldutype(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuStudentds uStudentc Bsh|EeZejdddd ZejddZejddd dd dd dd Z d Z d S(u db_columnumy_custom_nameu primary_keyu max_lengthi2uselfunullublanku related_nameuchildrenumy_cusom_parentcCs|jS(N(uname(uself((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyu__str__osNT( u__name__u __module__umodelsu AutoFielduTrueumy_idu CharFieldunameu ForeignKeyuparentu__str__(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyu CustomPKNamehs u CustomPKNamecBs/|EeZejdddddddZdS(uselfunullublanku related_nameuchildrenNT(u__name__u __module__umodelsu ForeignKeyuTrueuparent(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuMultiTableInheritanceA1ws uMultiTableInheritanceA1cBs |EeZejddZdS(u max_lengthi2N(u__name__u __module__umodelsu CharFielduname(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuMultiTableInheritanceA2{s uMultiTableInheritanceA2cBs |EeZejddZdS(u max_lengthi2N(u__name__u __module__umodelsu CharFielduname(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuMultiTableInheritanceB1s uMultiTableInheritanceB1cBs/|EeZejdddddddZdS(uselfunullublanku related_nameuchildrenNT(u__name__u __module__umodelsu ForeignKeyuTrueuparent(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuMultiTableInheritanceB2s uMultiTableInheritanceB2cBsQ|EeZejddd dd ddZejddZGddZd S( uselfunullublanku related_nameuchildrenu max_lengthi2cBs|EeZdZdS(NT(u__name__u __module__uTrueuabstract(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuMetas uMetaNT( u__name__u __module__umodelsu ForeignKeyuTrueuparentu CharFieldughostsuMeta(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyu AbstractModels !u AbstractModelcBs |EeZejddZdS(u max_lengthi2N(u__name__u __module__umodelsu CharFielduname(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyu ConcreteModels u ConcreteModelcBs|EeZGddZdS(cBs|EeZdZdS(NT(u__name__u __module__uTrueuabstract(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuMetas uMetaN(u__name__u __module__uMeta(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuAbstractConcreteAbstracts uAbstractConcreteAbstractcBs|EeZdS(N(u__name__u __module__(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyu ConcreteAbstractConcreteAbstracts u ConcreteAbstractConcreteAbstractcBs|EeZdS(N(u__name__u __module__(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuConcreteConcretes uConcreteConcretecBs|EeZGddZdS(cBs|EeZdZdS(NT(u__name__u __module__uTrueuproxy(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuMetas uMetaN(u__name__u __module__uMeta(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuSingleProxyModels uSingleProxyModelcBs|EeZGddZdS(cBs|EeZdZdS(NT(u__name__u __module__uTrueuproxy(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuMetas uMetaN(u__name__u __module__uMeta(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuDoubleProxyModels uDoubleProxyModelcBsQ|EeZejddd dd ddZejdd ZGddZdS( uselfunullublanku related_nameuchildrenu auto_now_addcBs|EeZdZdS(unowN(unow(u__name__u __module__uorder_insertion_by(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuMPTTMetas uMPTTMetaNT( u__name__u __module__umodelsu ForeignKeyuTrueuparentu DateTimeFieldunowuMPTTMeta(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuAutoNowDateFieldModels !uAutoNowDateFieldModelublankunulluparentuorder_insertion_byunameT(uname()u __future__uunicode_literalsudjango.contrib.auth.modelsuGroupu django.dbumodelsudjango.utils.encodingupython_2_unicode_compatibleumpttu mptt.modelsu MPTTModeluTreeForeignKeyu mptt.managersu TreeManageruCustomTreeManageruCategoryuGenreuInsertu MultiOrderuNodeuOrderedInsertionuTreeuNewStyleMPTTMetauPersonuStudentu CustomPKNameuMultiTableInheritanceA1uMultiTableInheritanceA2uMultiTableInheritanceB1uMultiTableInheritanceB2u AbstractModelu ConcreteModeluAbstractConcreteAbstractu ConcreteAbstractConcreteAbstractuConcreteConcreteuSingleProxyModeluDoubleProxyModeluAutoNowDateFieldModeluTrueucontribute_to_classuregister(((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyusL             "django-mptt-0.8.0/tests/myapp/__pycache__/models.cpython-33.pyc0000664000076500000240000003717612226575740025663 0ustar cdestigterstaff00000000000000 WZR?c@sddlmZddlmZddlmZddlmZddlZddl m Z m Z ddl m Z Gdd d e ZeGd d d e ZeGd d d e ZGddde ZeGddde ZGddde ZeGddde ZGddde ZGddde ZeGddde ZGdddeZeGddde ZGd d!d!e ZGd"d#d#eZGd$d%d%e ZGd&d'd'eZGd(d)d)e ZGd*d+d+eZGd,d-d-eZ Gd.d/d/eZ!Gd0d1d1eZ"Gd2d3d3eZ#Gd4d5d5e#Z$Gd6d7d7e Z%e ed8d=d9d=j'ed:ej(ed;d>dS(?i(uunicode_literals(uGroup(umodels(upython_2_unicode_compatibleN(u MPTTModeluTreeForeignKey(u TreeManagercBs|EeZdZdS(uCustomTreeManagerN(u__name__u __module__u __qualname__(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuCustomTreeManager suCustomTreeManagercse|EeZdZejddZejddd dd ddZdd Z fd d Z S( uCategoryu max_lengthi2uselfunullublanku related_nameuchildrencCs|jS(N(uname(uself((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyu__str__suCategory.__str__cstt|jdS(N(usuperuCategoryudelete(uself(u __class__(u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyudeletesuCategory.deleteT( u__name__u __module__u __qualname__umodelsu CharFieldunameu ForeignKeyuTrueuparentu__str__udelete(u __locals__((u __class__u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuCategorys! uCategorycBsY|EeZdZejdddd Zejddd dd ddZd d Z d S( uGenreu max_lengthi2uuniqueuselfunullublanku related_nameuchildrencCs|jS(N(uname(uself((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyu__str__ su Genre.__str__NT( u__name__u __module__u __qualname__umodelsu CharFielduTrueunameu ForeignKeyuparentu__str__(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuGenres!uGenrecBs5|EeZdZejdddddddZdS(uInsertuselfunullublanku related_nameuchildrenNT(u__name__u __module__u __qualname__umodelsu ForeignKeyuTrueuparent(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuInsert$suInsertcBs~|EeZdZejddZejZejZ ej ddd dd ddZ Gdd d Z d d Zd S(u MultiOrderu max_lengthi2uselfunullublanku related_nameuchildrencBs#|EeZdZdddgZdS(uMultiOrder.MPTTMetaunameusizeu-dateN(u__name__u __module__u __qualname__uorder_insertion_by(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuMPTTMeta/suMPTTMetacCs|jS(N(uname(uself((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyu__str__2suMultiOrder.__str__NT(u__name__u __module__u __qualname__umodelsu CharFieldunameuPositiveIntegerFieldusizeu DateFieldudateu ForeignKeyuTrueuparentuMPTTMetau__str__(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyu MultiOrder(s   !u MultiOrdercBsH|EeZdZejddd dd ddZGdddZdS( uNodeuselfunullublanku related_nameuchildrencBs,|EeZdZdZdZdZdZdS(u Node.MPTTMetaudoesuzisumadnessuworkN(u__name__u __module__u __qualname__u left_attru right_attru level_attru tree_id_attr(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuMPTTMeta9suMPTTMetaNT(u__name__u __module__u __qualname__umodelsu ForeignKeyuTrueuparentuMPTTMeta(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuNode6s!uNodecBsf|EeZdZejddZejddd dd ddZGdd d Z d d Z d S(uOrderedInsertionu max_lengthi2uselfunullublanku related_nameuchildrencBs|EeZdZdgZdS(uOrderedInsertion.MPTTMetaunameN(u__name__u __module__u __qualname__uorder_insertion_by(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuMPTTMetaEsuMPTTMetacCs|jS(N(uname(uself((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyu__str__HsuOrderedInsertion.__str__NT( u__name__u __module__u __qualname__umodelsu CharFieldunameu ForeignKeyuTrueuparentuMPTTMetau__str__(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuOrderedInsertion@s!uOrderedInsertioncBs5|EeZdZejdddddddZdS(uTreeuselfunullublanku related_nameuchildrenNT(u__name__u __module__u __qualname__umodelsu ForeignKeyuTrueuparent(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuTreeLsuTreecBsK|EeZdZejddd dd ddZGdddeZdS( uNewStyleMPTTMetauselfunullublanku related_nameuchildrencBs|EeZdZdZdS(uNewStyleMPTTMeta.MPTTMetautestingN(u__name__u __module__u __qualname__u left_attr(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuMPTTMetaSsuMPTTMetaNT( u__name__u __module__u __qualname__umodelsu ForeignKeyuTrueuparentuobjectuMPTTMeta(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuNewStyleMPTTMetaPs!uNewStyleMPTTMetacBsh|EeZdZejddZejddd dd ddZej Z e Z dd Z d S( uPersonu max_lengthi2uselfunullublanku related_nameuchildrencCs|jS(N(uname(uself((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyu__str__`suPerson.__str__NT(u__name__u __module__u __qualname__umodelsu CharFieldunameu ForeignKeyuTrueuparentuManageruobjectsuCustomTreeManagerumy_tree_manageru__str__(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuPersonWs !  uPersoncBs&|EeZdZejddZdS(uStudentu max_lengthi2N(u__name__u __module__u __qualname__umodelsu CharFieldutype(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuStudentdsuStudentc Bsq|EeZdZejddddZejddZej dddddd d dd Z d d Z dS(u CustomPKNameu db_columnumy_custom_nameu primary_keyu max_lengthi2uselfunullublanku related_nameuchildrenumy_cusom_parentcCs|jS(N(uname(uself((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyu__str__osuCustomPKName.__str__NT( u__name__u __module__u __qualname__umodelsu AutoFielduTrueumy_idu CharFieldunameu ForeignKeyuparentu__str__(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyu CustomPKNamehs u CustomPKNamecBs5|EeZdZejdddddddZdS(uMultiTableInheritanceA1uselfunullublanku related_nameuchildrenNT(u__name__u __module__u __qualname__umodelsu ForeignKeyuTrueuparent(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuMultiTableInheritanceA1wsuMultiTableInheritanceA1cBs&|EeZdZejddZdS(uMultiTableInheritanceA2u max_lengthi2N(u__name__u __module__u __qualname__umodelsu CharFielduname(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuMultiTableInheritanceA2{suMultiTableInheritanceA2cBs&|EeZdZejddZdS(uMultiTableInheritanceB1u max_lengthi2N(u__name__u __module__u __qualname__umodelsu CharFielduname(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuMultiTableInheritanceB1suMultiTableInheritanceB1cBs5|EeZdZejdddddddZdS(uMultiTableInheritanceB2uselfunullublanku related_nameuchildrenNT(u__name__u __module__u __qualname__umodelsu ForeignKeyuTrueuparent(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuMultiTableInheritanceB2suMultiTableInheritanceB2cBsZ|EeZdZejddd dd ddZejddZGdd d Z d S( u AbstractModeluselfunullublanku related_nameuchildrenu max_lengthi2cBs|EeZdZdZdS(uAbstractModel.MetaNT(u__name__u __module__u __qualname__uTrueuabstract(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuMetasuMetaNT( u__name__u __module__u __qualname__umodelsu ForeignKeyuTrueuparentu CharFieldughostsuMeta(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyu AbstractModels!u AbstractModelcBs&|EeZdZejddZdS(u ConcreteModelu max_lengthi2N(u__name__u __module__u __qualname__umodelsu CharFielduname(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyu ConcreteModelsu ConcreteModelcBs'|EeZdZGdddZdS(uAbstractConcreteAbstractcBs|EeZdZdZdS(uAbstractConcreteAbstract.MetaNT(u__name__u __module__u __qualname__uTrueuabstract(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuMetasuMetaN(u__name__u __module__u __qualname__uMeta(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuAbstractConcreteAbstractsuAbstractConcreteAbstractcBs|EeZdZdS(u ConcreteAbstractConcreteAbstractN(u__name__u __module__u __qualname__(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyu ConcreteAbstractConcreteAbstractsu ConcreteAbstractConcreteAbstractcBs|EeZdZdS(uConcreteConcreteN(u__name__u __module__u __qualname__(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuConcreteConcretesuConcreteConcretecBs'|EeZdZGdddZdS(uSingleProxyModelcBs|EeZdZdZdS(uSingleProxyModel.MetaNT(u__name__u __module__u __qualname__uTrueuproxy(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuMetasuMetaN(u__name__u __module__u __qualname__uMeta(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuSingleProxyModelsuSingleProxyModelcBs'|EeZdZGdddZdS(uDoubleProxyModelcBs|EeZdZdZdS(uDoubleProxyModel.MetaNT(u__name__u __module__u __qualname__uTrueuproxy(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuMetasuMetaN(u__name__u __module__u __qualname__uMeta(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuDoubleProxyModelsuDoubleProxyModelcBsZ|EeZdZejddd dd ddZejdd ZGdddZ d S( uAutoNowDateFieldModeluselfunullublanku related_nameuchildrenu auto_now_addcBs|EeZdZdZdS(uAutoNowDateFieldModel.MPTTMetaunowN(unow(u__name__u __module__u __qualname__uorder_insertion_by(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuMPTTMetasuMPTTMetaNT( u__name__u __module__u __qualname__umodelsu ForeignKeyuTrueuparentu DateTimeFieldunowuMPTTMeta(u __locals__((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyuAutoNowDateFieldModels!uAutoNowDateFieldModelublankunulluparentuorder_insertion_byunameT(uname()u __future__uunicode_literalsudjango.contrib.auth.modelsuGroupu django.dbumodelsudjango.utils.encodingupython_2_unicode_compatibleumpttu mptt.modelsu MPTTModeluTreeForeignKeyu mptt.managersu TreeManageruCustomTreeManageruCategoryuGenreuInsertu MultiOrderuNodeuOrderedInsertionuTreeuNewStyleMPTTMetauPersonuStudentu CustomPKNameuMultiTableInheritanceA1uMultiTableInheritanceA2uMultiTableInheritanceB1uMultiTableInheritanceB2u AbstractModelu ConcreteModeluAbstractConcreteAbstractu ConcreteAbstractConcreteAbstractuConcreteConcreteuSingleProxyModeluDoubleProxyModeluAutoNowDateFieldModeluTrueucontribute_to_classuregister(((u;/home/cdestigter/checkout/django-mptt/tests/myapp/models.pyusL         "django-mptt-0.8.0/tests/myapp/__pycache__/models.cpython-34.pyc0000644000076500000240000002753712635100364025650 0ustar cdestigterstaff00000000000000 8urVU@sddlmZddlmZddlmZddlZddlmZm Z m Z ddl m Z ddl mZddlmZGd d d eZGd d d eZeGd dde ZeGdddejZeGddde ZGdddejZGddde ZeGddde ZGddde ZeGddde ZGddde ZGdd d e ZeGd!d"d"e ZGd#d$d$eZeGd%d&d&e Z Gd'd(d(ejZ!Gd)d*d*e Z"Gd+d,d,e"Z#Gd-d.d.e Z$Gd/d0d0e$Z%Gd1d2d2e Z&Gd3d4d4e&Z'Gd5d6d6e'Z(Gd7d8d8e'Z)Gd9d:d:e'Z*Gd;d<d<e'Z+Gd=d>d>e+Z,Gd?d@d@e Z-GdAdBdBe Z.GdCdDdDe Z/GdEdFdFejZ0ee0dGdHdIdHdJej1j2e0dKej3e0dLdNdS)O)unicode_literals)models)python_2_unicode_compatibleN)TreeForeignKeyTreeOneToOneFieldTreeManyToManyField) MPTTModel) TreeManager)QuerySetc@seZdZddZdS)CustomTreeQuerysetcCsdS)N)selfr r r9r r r rr=fs   r=c @sheZdZejddZedddddddd ejZGd d d Z d d Z dS)OrderedInsertionrrr rTrr r!r"c@seZdZdgZdS)zOrderedInsertion.MPTTMetar$N)rrrr8r r r rr9{s r9cCs|jS)N)r$)r r r rr%~szOrderedInsertion.__str__N) rrrrr)r$rr*r+r9r%r r r rrDts  rDc @s7eZdZeddddddddejZdS) Treer rTrr r!r"N)rrrrrr*r+r r r rrEs rEc @sMeZdZeddddddddejZGdd d eZd S) NewStyleMPTTMetar rTrr r!r"c@seZdZdZdS)zNewStyleMPTTMeta.MPTTMetaZtestingN)rrrr?r r r rr9s r9N) rrrrrr*r+objectr9r r r rrFs  rFc @sdeZdZejddZedddddddd ejZe Z e Z d d Z d S) Personrrr rTrr r!r"cCs|jS)N)r$)r r r rr%szPerson.__str__N) rrrrr)r$rr*r+robjects_default_managerr%r r r rrHs   rHc@s"eZdZejddZdS)StudentrrN)rrrrr)typer r r rrKs rKc @sseZdZejddddZejddZedddd dd d dd d ej Z ddZ dS) CustomPKName db_columnZmy_custom_name primary_keyTrrr rrr r!Zmy_cusom_parentr"cCs|jS)N)r$)r r r rr%szCustomPKName.__str__N) rrrr AutoFieldZmy_idr)r$rr*r+r%r r r rrMs   rMc@sXeZdZeedddejZeedddejZ e eddZ dS)ReferencingModelr +r"N) rrrrrrr*fkrZonerm2mr r r rrQs rQc @s7eZdZeddddddddejZdS) MultiTableInheritanceA1r rTrr r!r"N)rrrrrr*r+r r r rrUs rUc@s"eZdZejddZdS)MultiTableInheritanceA2rrN)rrrrr)r$r r r rrVs rVc@s"eZdZejddZdS)MultiTableInheritanceB1rrN)rrrrr)r$r r r rrWs rWc @s7eZdZeddddddddejZdS) MultiTableInheritanceB2r rTrr r!r"N)rrrrrr*r+r r r rrXs rXc @s\eZdZeddddddddejZejdd ZGd d d Z d S) AbstractModelr rTrr r!r"rrc@seZdZdZdS)zAbstractModel.MetaTN)rrrabstractr r r rMetas r[N) rrrrrr*r+r)Zghostsr[r r r rrYs  rYc@s"eZdZejddZdS) ConcreteModelrrN)rrrrr)r$r r r rr\s r\c@s#eZdZGdddZdS)AbstractConcreteAbstractc@seZdZdZdS)zAbstractConcreteAbstract.MetaTN)rrrrZr r r rr[s r[N)rrrr[r r r rr]s r]c@seZdZdS) ConcreteAbstractConcreteAbstractN)rrrr r r rr^s r^c@seZdZdS)ConcreteConcreteN)rrrr r r rr_s r_c@s,eZdZeZGdddZdS)SingleProxyModelc@seZdZdZdS)zSingleProxyModel.MetaTN)rrrproxyr r r rr[s r[N)rrrrrIr[r r r rr`s  r`c@s#eZdZGdddZdS)DoubleProxyModelc@seZdZdZdS)zDoubleProxyModel.MetaTN)rrrrar r r rr[s r[N)rrrr[r r r rrbs rbc @sJeZdZeddddddddejZGdd d Zd S) SwappableModelr rTrr r!r"c@seZdZdZdS)zSwappableModel.MetaMPTT_SWAPPABLE_MODELN)rrr swappabler r r rr[s r[N)rrrrrr*r+r[r r r rrcs  rcc @sIeZdZeddddddddejZejdd Zd S) SwappedInModelr rTrr r!r"rrN) rrrrrr*r+r)r$r r r rrf s  rfc @s\eZdZeddddddddejZejddZGd d d Z d S) AutoNowDateFieldModelr rTrr r!r" auto_now_addc@seZdZdZdS)zAutoNowDateFieldModel.MPTTMetanowN)znow)rrrr8r r r rr9s r9N) rrrrrr*r+ DateTimeFieldrir9r r r rrgs  rgc@s"eZdZejddZdS)Grouprr/N)rrrrr)r$r r r rrks rkrTrr"r+r8r$)zname)4 __future__r django.dbrdjango.utils.encodingrmptt mptt.fieldsrrr mptt.modelsr mptt.managersr django.db.models.queryr r rrModelr.r2r3r5r6r=rDrErFrHrKrMrQrUrVrWrXrYr\r]r^r_r`rbrcrfrgrkr*contribute_to_classregisterr r r rsb               django-mptt-0.8.0/tests/myapp/__pycache__/tests.cpython-32.pyc0000664000076500000240000013513712226603202025516 0ustar cdestigterstaff00000000000000l [[Rc @s!ddlmZddlZddlZddlZddlZddlZddlmZddl m Z ddl m Z ddl mZddlmZmZddlmZdd lmZmZmZyddlZWnek rd8ZYnXdd lmZmZdd lmZdd l m!Z!dd l"m#Z#ddl$m%Z%m&Z&m'Z'm(Z(m)Z)m*Z*m+Z+m,Z,dZ-ej.d9krdZ-ndZ/ej0dej1Z2dZ3GddeZ4Gdde4Z5Gdde4Z6Gdde4Z7Gdde4Z8Gdde4Z9Gd d!e4Z:er_Gd"d#e4Z;nGd$d%e4Z<Gd&d'e4Z=Gd(d)e4Z>Gd*d+e4Z?Gd,d-e4Z@Gd.d/e4ZAGd0d1e4ZBGd2d3e4ZCGd4d5e4ZDGd6d7e4ZEdS(:i(uunicode_literalsN(uadmin(uGroup(u get_models(umodelform_factory(uTemplateuContext(uTestCase(u string_typesuPY3ub(uCantDisableUpdatesu InvalidMove(u MPTTAdminForm(u MPTTModel(ucache_tree_children(uCategoryuGenreu CustomPKNameuSingleProxyModeluDoubleProxyModelu ConcreteModeluOrderedInsertionuAutoNowDateFieldModeliicsct|dr-t|jddd}nt|}|djdjfd|DS(u Creates pertinent tree details for the given list of nodes. The fields are: id parent_id tree_id level left right uorder_byutree_idulftupkiu c sug|]k}d|jt|djp.dt|jt|jt|jt|jfqS(u%s %s %s %s %s %su%s_idu-(upkugetattru parent_attru tree_id_attru level_attru left_attru right_attr(u.0un(uopts(u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyu -s (uhasattrulistuorder_byu _mptt_metaujoin(unodes((uoptsu:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyuget_tree_details#s   u^\s+cCstjd|jS(u Trims leading whitespace from the given text specifying tree details so triple-quoted strings can be used to provide tree details in a readable format (says who?), to be compared with the result of using the ``get_tree_details`` function. u(uleading_whitespace_reusuburstrip(utext((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyu tree_details6scBs|EeZdZdS(cCsqt|tst|}nt|}t|tsHt|}nt|}|j||d||fS(Nu %r != %r(u isinstanceu string_typesuget_tree_detailsu tree_detailsu assertEqual(uselfutree1utree2((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyuassertTreeEqualAs  N(u__name__u __module__uassertTreeEqual(u __locals__((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyu TreeTestCase@s u TreeTestCasecBs|EeZdZdS(c Cs"Gdd}|}tj}|t_ttjjtjjtd}tj }|j }t rt j dd|}|jt|n|jddl}|j|jdd d|jd d |t_|j}|r|j|d |jnWdQXWdQXdS( NcBs,|EeZdZdZdZdZdS(uuutf8cSs|j|7_dS(N(ucontent(uselfutext((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyuwriteQscSsdS(N((uself((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyuflushTsN(u__name__u __module__ucontentuencodinguwriteuflush(u __locals__((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyu DummyStreamMs  u DummyStreamu doctests.txtu \bu(["\'])u\1iumodule_relativeu optionflagsuencodinguutf-8u F(usysustdoutuopenuosupathujoinudirnameu__file__utempfileuNamedTemporaryFileureaduPY3ureusubuwriteubuflushudoctestutestfileunameuFalseuIGNORE_EXCEPTION_DETAILucontentufail( uselfu DummyStreamu dummy_streamubeforeufutemputextudoctestucontent((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_run_doctestLs,   *       N(u__name__u __module__utest_run_doctest(u __locals__((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyuDocTestTestCaseKs uDocTestTestCasecBsn|EeZdZdgZdZdZdZdZdZdZ dZ d Z d Z d S( u Test that trees are in the appropriate state after reparenting and that reparented items have the correct tree attributes defined, should they be required for use after a save. u genres.jsoncCsXtjjdd}d|_|j|j|gd|jtjjddS(Nuidiu 6 - 3 0 1 6u 1 - 1 0 1 10 2 1 1 1 2 9 3 2 1 2 3 4 4 2 1 2 5 6 5 2 1 2 7 8 9 - 2 0 1 6 10 9 2 1 2 3 11 9 2 1 4 5 6 - 3 0 1 6 7 6 3 1 2 3 8 6 3 1 4 5 (uGenreuobjectsugetuNoneuparentusaveuassertTreeEqualuall(uselfushmup((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_new_root_from_subtrees    cCsXtjjdd}d|_|j|j|gd|jtjjddS(Nuidiu 3 - 3 0 1 2u 1 - 1 0 1 14 2 1 1 1 2 7 4 2 1 2 3 4 5 2 1 2 5 6 6 1 1 1 8 13 7 6 1 2 9 10 8 6 1 2 11 12 9 - 2 0 1 6 10 9 2 1 2 3 11 9 2 1 4 5 3 - 3 0 1 2 (uGenreuobjectsugetuNoneuparentusaveuassertTreeEqualuall(uselfu platformer_2d((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyu%test_new_root_from_leaf_with_siblingss    cCstjjdd}tjjdd}||_|j|j|gd|j|gd|jtjjddS(Nuidii u 1 9 2 1 6 21u 9 - 2 0 1 22u  9 - 2 0 1 22 10 9 2 1 2 3 11 9 2 1 4 5 1 9 2 1 6 21 2 1 2 2 7 14 3 2 2 3 8 9 4 2 2 3 10 11 5 2 2 3 12 13 6 1 2 2 15 20 7 6 2 3 16 17 8 6 2 3 18 19 (uGenreuobjectsugetuparentusaveuassertTreeEqualuall(uselfuactionurpg((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_new_child_from_roots   cCstjjdd}tjjdd}||_|j|j|gd|j|gd|jtjjddS(Nuidii u 8 9 2 1 6 7u 9 - 2 0 1 8u 1 - 1 0 1 14 2 1 1 1 2 9 3 2 1 2 3 4 4 2 1 2 5 6 5 2 1 2 7 8 6 1 1 1 10 13 7 6 1 2 11 12 9 - 2 0 1 8 10 9 2 1 2 3 11 9 2 1 4 5 8 9 2 1 6 7 (uGenreuobjectsugetuparentusaveuassertTreeEqualuall(uselfushmup_horizontalurpg((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_move_leaf_to_other_trees   cCstjjdd}tjjdd}||_|j|j|gd|j|gd|jtjjddS(Nuidii u 6 11 2 2 5 10u 11 9 2 1 4 11u 1 - 1 0 1 10 2 1 1 1 2 9 3 2 1 2 3 4 4 2 1 2 5 6 5 2 1 2 7 8 9 - 2 0 1 12 10 9 2 1 2 3 11 9 2 1 4 11 6 11 2 2 5 10 7 6 2 3 6 7 8 6 2 3 8 9 (uGenreuobjectsugetuparentusaveuassertTreeEqualuall(uselfushmuputrpg((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_move_subtree_to_other_trees   cCstjjdd}tjjdd}||_|j|j|gd|j|gd|jtjjddS(Nuidiiu 8 1 1 1 14 15u 1 - 1 0 1 16u 1 - 1 0 1 16 2 1 1 1 2 9 3 2 1 2 3 4 4 2 1 2 5 6 5 2 1 2 7 8 6 1 1 1 10 13 7 6 1 2 11 12 8 1 1 1 14 15 9 - 2 0 1 6 10 9 2 1 2 3 11 9 2 1 4 5 (uGenreuobjectsugetuparentusaveuassertTreeEqualuall(uselfushmup_horizontaluaction((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_move_child_up_levels   cCstjjdd}tjjdd}||_|j|j|gd|j|gd|jtjjddS(Nuidiiu 6 2 1 2 9 14u 2 1 1 1 2 15u 1 - 1 0 1 16 2 1 1 1 2 15 3 2 1 2 3 4 4 2 1 2 5 6 5 2 1 2 7 8 6 2 1 2 9 14 7 6 1 3 10 11 8 6 1 3 12 13 9 - 2 0 1 6 10 9 2 1 2 3 11 9 2 1 4 5 (uGenreuobjectsugetuparentusaveuassertTreeEqualuall(uselfushmupu platformer((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_move_subtree_down_levels   cCsXtjjdd}tjjdd}|j||j|j|j|dS(Nupki i(uGenreuobjectsugetumove_tousaveu assertEqualuparent(uselfurpguaction((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyu test_move_tos   cCstjjdd}||_tjjdd}||_|jt|j|jt|jtjjdd}||_||_|jt|j|jt|j|j|j||j|j|dS(Nuidiii(uGenreuobjectsugetuparentu assertRaisesu InvalidMoveusaveu assertEqual(uselfuactionu platformeru platformer_4d((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_invalid_moves"s    N( u__name__u __module__u__doc__ufixturesutest_new_root_from_subtreeu%test_new_root_from_leaf_with_siblingsutest_new_child_from_rootutest_move_leaf_to_other_treeutest_move_subtree_to_other_treeutest_move_child_up_levelutest_move_subtree_down_levelu test_move_toutest_invalid_moves(u __locals__((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyuReparentingTestCases          uReparentingTestCasecBsJ|EeZdZdgZdZdZdZdZdZdS(uf Tests that the tree structure is maintained appropriately in various deletion scenarios. ucategories.jsoncCstddjtjjddddd tddjtjjddddd |jtjjd tjjddj|jtjjd dS( NunameuPreceding rootuidiuleftusaveuFollowing rooturightu7 11 - 1 0 1 2 1 - 2 0 1 20 2 1 2 1 2 7 3 2 2 2 3 4 4 2 2 2 5 6 5 1 2 1 8 13 6 5 2 2 9 10 7 5 2 2 11 12 8 1 2 1 14 19 9 8 2 2 15 16 10 8 2 2 17 18 12 - 3 0 1 2 u; 11 - 1 0 1 2 12 - 3 0 1 2 T(uCategoryu insert_atuobjectsugetuTrueuassertTreeEqualualludelete(uself((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_delete_root_nodeKs! !   cCs6tjjddj|jtjjddS(Nuidi u 1 - 1 0 1 18 2 1 1 1 2 7 3 2 1 2 3 4 4 2 1 2 5 6 5 1 1 1 8 13 6 5 1 2 9 10 7 5 1 2 11 12 8 1 1 1 14 17 10 8 1 2 15 16 (uCategoryuobjectsugetudeleteuassertTreeEqualuall(uself((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyu#test_delete_last_node_with_siblingsgs cCs6tjjddj|jtjjddS(Nuidiu 1 - 1 0 1 14 2 1 1 1 2 7 3 2 1 2 3 4 4 2 1 2 5 6 5 1 1 1 8 13 6 5 1 2 9 10 7 5 1 2 11 12 (uCategoryuobjectsugetudeleteuassertTreeEqualuall(uself((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyu&test_delete_last_node_with_descendantsuscCstjjdd}|j}|j|jd|j|jtjjd|j|jdtjjd|j }|j|jddS(Nuidiiu 1 - 1 0 1 18 2 1 1 1 2 7 3 2 1 2 3 4 4 2 1 2 5 6 5 1 1 1 8 11 7 5 1 2 9 10 8 1 1 1 12 17 9 8 1 2 13 14 10 8 1 2 15 16 iupk( uCategoryuobjectsugetuparentu assertEqualuget_descendant_countudeleteuassertTreeEqualuallupk(uselfuchilduparent((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_delete_node_with_siblingss   cCs6tjjddj|jtjjddS(u Regression test for Issue 23 - we used to use pre_delete, which resulted in tree cleanup being performed for every node being deleted, rather than just the node on which ``delete()`` was called. uidiu 1 - 1 0 1 14 2 1 1 1 2 7 3 2 1 2 3 4 4 2 1 2 5 6 8 1 1 1 8 13 9 8 1 2 9 10 10 8 1 2 11 12 N(uCategoryuobjectsugetudeleteuassertTreeEqualuall(uself((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyu.test_delete_node_with_descendants_and_siblingssN( u__name__u __module__u__doc__ufixturesutest_delete_root_nodeu#test_delete_last_node_with_siblingsu&test_delete_last_node_with_descendantsutest_delete_node_with_siblingsu.test_delete_node_with_descendants_and_siblings(u __locals__((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyuDeletionTestCaseDs     uDeletionTestCasecBs|EeZdS(N(u__name__u __module__(u __locals__((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyuIntraTreeMovementTestCases uIntraTreeMovementTestCasecBs|EeZdS(N(u__name__u __module__(u __locals__((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyuInterTreeMovementTestCases uInterTreeMovementTestCasecBs|EeZdS(N(u__name__u __module__(u __locals__((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyuPositionedInsertionTestCases uPositionedInsertionTestCasecBs&|EeZdZdgZdZdS(u. Tests for FeinCMSModelAdmin. ucategories.jsoncCs[ddlm}|ttj}tjjdd}|jd|j|dkdS(uQ The action column should have an "add" button inserted. i(uFeinCMSModelAdminuidiu*N( u mptt.adminuFeinCMSModelAdminuCategoryuadminusiteuobjectsugetu assertTrueu_actions_column(uselfuFeinCMSModelAdminu model_adminucategory((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_actions_columns N(u__name__u __module__u__doc__ufixturesutest_actions_column(u __locals__((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyuFeinCMSModelAdminTestCases  uFeinCMSModelAdminTestCasecBs |EeZdZdZdS(cCstj}|jdd}|jddd||jddd||jdd}|jddd||jddd||jdd dS( Nunameuc1uc11uparentuc12uc2uc21uc22uc3(u CustomPKNameuobjectsucreate(uselfumanageruc1uc2((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyusetUps cCs8tjjdd}|j}|j|dkdS(Nunameuc12(u CustomPKNameuobjectsugetuget_next_siblingu assertTrueuNone(uselfurootusib((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_get_next_siblings N(u__name__u __module__usetUputest_get_next_sibling(u __locals__((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyuCustomPKNameTestCases  uCustomPKNameTestCasecBs_|EeZdZdZdZdZdZdZdZdZ dZ d S( cCstjjdd|_tjjddd|j|_tjjddd|j|_tjjdd|_|jtjjddS(Nunameuaubuparentucudui 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 ( u ConcreteModeluobjectsucreateuaubucuduassertTreeEqualuall(uself((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyusetUps !!c Cs|jtj|jtj|jttjjj|jtj|jtjtjj%|j tj|j tjWdQX|jtj|jtjdS(N( u assertTrueu ConcreteModelu_mptt_updates_enableduSingleProxyModelu assertRaisesuCantDisableUpdatesuobjectsudisable_mptt_updatesu __enter__u assertFalse(uself((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_single_proxysc Cs|jtj|jtj|jttjjj|jtj|jtjtjj%|j tj|j tjWdQX|jtj|jtjdS(N( u assertTrueu ConcreteModelu_mptt_updates_enableduDoubleProxyModelu assertRaisesuCantDisableUpdatesuobjectsudisable_mptt_updatesu __enter__u assertFalse(uself((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_double_proxyscCs|jdftjjP|jd!tjjddd|jWdQX|jtjjdWdQXWdQX|jtjjddS(Niiunameueuparentu 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 5 4 2 1 2 3 u 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 5 4 2 1 2 3 (uassertNumQueriesu ConcreteModeluobjectsudisable_mptt_updatesucreateuduassertTreeEqualuall(uself((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_insert_child s"cCs|jd]tjjG|jdtjjddWdQX|jtjjdWdQXWdQX|jtjjddS(Niiunameueu 5 - 0 0 1 2 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 u 5 - 0 0 1 2 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 (uassertNumQueriesu ConcreteModeluobjectsudisable_mptt_updatesucreateuassertTreeEqualuall(uself((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_insert_root"scCs|jdtjtjjT|jdt!|j|j_|jjWdQX|j tjj dWdQXWdQX|j tjj ddS(Niiu 1 - 1 0 1 6 2 1 1 1 2 3 3 2 1 1 4 5 4 - 2 0 1 2 ui 1 - 1 0 1 6 2 1 1 1 2 3 3 2 1 1 4 5 4 - 2 0 1 2 ( uassertNumQueriesuextra_queries_per_updateu ConcreteModeluobjectsudisable_mptt_updatesubucuparentusaveuassertTreeEqualuall(uself((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_move_node_same_tree8scCs|jdtjtjjT|jdt!|j|j_|jjWdQX|j tjj dWdQXWdQX|j tjj ddS(Niiu 1 - 1 0 1 6 2 1 1 1 2 3 3 4 1 1 4 5 4 - 2 0 1 2 ui 1 - 1 0 1 6 2 1 1 1 2 3 3 4 1 1 4 5 4 - 2 0 1 2 ( uassertNumQueriesuextra_queries_per_updateu ConcreteModeluobjectsudisable_mptt_updatesuducuparentusaveuassertTreeEqualuall(uself((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_move_node_different_treePscCs|jdtgtjjQ|jdtd|j_|jjWdQX|j tjj dWdQXWdQX|j tjj ddS(Niiu 1 - 1 0 1 6 2 1 1 1 2 3 3 - 1 1 4 5 4 - 2 0 1 2 ui 1 - 1 0 1 6 2 1 1 1 2 3 3 - 1 1 4 5 4 - 2 0 1 2 ( uassertNumQueriesuextra_queries_per_updateu ConcreteModeluobjectsudisable_mptt_updatesuNoneucuparentusaveuassertTreeEqualuall(uself((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_move_node_to_rootgs cCs|jdtjtjjT|jdt!|j|j_|jjWdQX|j tjj dWdQXWdQX|j tjj ddS(Niiu 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 3 2 0 1 2 ui 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 3 2 0 1 2 ( uassertNumQueriesuextra_queries_per_updateu ConcreteModeluobjectsudisable_mptt_updatesucuduparentusaveuassertTreeEqualuall(uself((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_move_root_to_child~sN( u__name__u __module__usetUputest_single_proxyutest_double_proxyutest_insert_childutest_insert_rootutest_move_node_same_treeutest_move_node_different_treeutest_move_node_to_rootutest_move_root_to_child(u __locals__((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyuDisabledUpdatesTestCases        uDisabledUpdatesTestCasecBs_|EeZdZdZdZdZdZdZdZdZ dZ d S( cCstjjdd|_tjjddd|j|_tjjddd|j|_tjjdd|_tjjdd|_|jtjj ddS( Nunameuaubuparentucuduzu 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 5 - 3 0 1 2 ( u ConcreteModeluobjectsucreateuaubucuduzuassertTreeEqualuall(uself((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyusetUps!!c Cs|jtj|jtj|jttjjj|jtj|jtjtjj%|j tj|j tjWdQX|jtj|jtjdS(N( u assertFalseu ConcreteModelu_mptt_is_trackinguSingleProxyModelu assertRaisesuCantDisableUpdatesuobjectsudelay_mptt_updatesu __enter__u assertTrue(uself((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyu test_proxyscCsptjjK|jtjtjj|jtjWdQX|jtjWdQX|jtjdS(N(u ConcreteModeluobjectsudelay_mptt_updatesu assertTrueu_mptt_is_trackingu assertFalse(uself((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_double_context_managers cCs|jdftjjP|jd!tjjddd|jWdQX|jtjjdWdQXWdQX|jtjjddS(Niiunameueuparentu 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 6 4 2 1 2 3 5 - 3 0 1 2 u 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 4 6 4 2 1 2 3 5 - 3 0 1 2 (uassertNumQueriesu ConcreteModeluobjectsudelay_mptt_updatesucreateuduassertTreeEqualuall(uself((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_insert_childs"cCs|jd]tjjG|jdtjjddWdQX|jtjjdWdQXWdQX|jtjjddS(Niiunameueu 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 5 - 3 0 1 2 6 - 4 0 1 2 u 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 5 - 3 0 1 2 6 - 4 0 1 2 (uassertNumQueriesu ConcreteModeluobjectsudelay_mptt_updatesucreateuassertTreeEqualuall(uself((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_insert_rootscCs|jdtjtjjT|jdt!|j|j_|jjWdQX|j tjj dWdQXWdQX|j tjj ddS(Ni iu 1 - 1 0 1 6 2 1 1 1 2 3 3 2 1 2 3 4 4 - 2 0 1 2 5 - 3 0 1 2 u 1 - 1 0 1 6 2 1 1 1 2 5 3 2 1 2 3 4 4 - 2 0 1 2 5 - 3 0 1 2 ( uassertNumQueriesuextra_queries_per_updateu ConcreteModeluobjectsudelay_mptt_updatesubucuparentusaveuassertTreeEqualuall(uself((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_move_node_same_treescCs|jdtjtjjT|jdt!|j|j_|jjWdQX|j tjj dWdQXWdQX|j tjj ddS(Ni iu 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 3 1 2 5 6 5 - 2 0 1 2 u 1 - 1 0 1 8 2 1 1 1 2 3 3 1 1 1 4 7 4 3 1 2 5 6 5 - 2 0 1 2 ( uassertNumQueriesuextra_queries_per_updateu ConcreteModeluobjectsudelay_mptt_updatesucuduparentusaveuassertTreeEqualuall(uself((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_move_node_different_tree scCs|jdtgtjjQ|jdtd|j_|jjWdQX|j tjj dWdQXWdQX|j tjj ddS(Niiu 1 - 1 0 1 6 2 1 1 1 2 3 4 - 2 0 1 2 5 - 3 0 1 2 3 - 4 0 1 2 u 1 - 1 0 1 6 2 1 1 1 2 3 4 - 2 0 1 2 5 - 3 0 1 2 3 - 4 0 1 2 ( uassertNumQueriesuextra_queries_per_updateu ConcreteModeluobjectsudelay_mptt_updatesuNoneucuparentusaveuassertTreeEqualuall(uself((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_move_node_to_root's cCs|jdtjtjjT|jdt!|j|j_|jjWdQX|j tjj dWdQXWdQX|j tjj ddS(Ni iu 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 3 1 2 5 6 5 - 2 0 1 2 u 1 - 1 0 1 8 2 1 1 1 2 3 3 1 1 1 4 7 4 3 1 2 5 6 5 - 2 0 1 2 ( uassertNumQueriesuextra_queries_per_updateu ConcreteModeluobjectsudelay_mptt_updatesucuduparentusaveuassertTreeEqualuall(uself((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_move_root_to_childBsN( u__name__u __module__usetUpu test_proxyutest_double_context_managerutest_insert_childutest_insert_rootutest_move_node_same_treeutest_move_node_different_treeutest_move_node_to_rootutest_move_root_to_child(u __locals__((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyuDelayedUpdatesTestCases         uDelayedUpdatesTestCasecBsM|EeZdZdZdZdZdZdZdZdS(cCstjjdd|_tjjddd|j|_tjjddd|j|_tjjdd|_tjjdd|_|jtjj ddS( Nunameucuduparentueufuzu 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 5 - 3 0 1 2 ( uOrderedInsertionuobjectsucreateucudueufuzuassertTreeEqualuall(uself((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyusetUp_s!!cCs|jdftjjP|jd!tjjddd|jWdQX|jtjjdWdQXWdQX|jtjjddS(Ni iunameudduparentu 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 6 1 1 1 6 7 4 - 2 0 1 2 5 - 3 0 1 2 u 1 - 1 0 1 8 2 1 1 1 2 3 6 1 1 1 4 5 3 1 1 1 6 7 4 - 2 0 1 2 5 - 3 0 1 2 (uassertNumQueriesuOrderedInsertionuobjectsudelay_mptt_updatesucreateucuassertTreeEqualuall(uself((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_insert_childns"cCs|jd]tjjG|jdtjjddWdQX|jtjjdWdQXWdQX|jtjjddS(Niiunameueeu 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 6 - 2 0 1 2 4 - 3 0 1 2 5 - 4 0 1 2 u 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 6 - 2 0 1 2 4 - 3 0 1 2 5 - 4 0 1 2 (uassertNumQueriesuOrderedInsertionuobjectsudelay_mptt_updatesucreateuassertTreeEqualuall(uself((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_insert_rootscCs|jdtgtjjQ|jdtd|j_|jjWdQX|jtjj dWdQXWdQX|jtjj ddS(Ni iubefore du 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 5 - 3 0 1 2 u 1 - 1 0 1 6 3 1 1 1 2 3 2 1 1 1 4 5 4 - 2 0 1 2 5 - 3 0 1 2 ( uassertNumQueriesuextra_queries_per_updateuOrderedInsertionuobjectsudelay_mptt_updatesueunameusaveuassertTreeEqualuall(uself((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_move_node_same_trees cCs|jdtvtjj`|jdt-|j|j_d|j_|jj WdQX|j tjj dWdQXWdQX|j tjj ddS(Ni iuddu 1 - 1 0 1 6 2 1 1 1 2 3 4 1 1 1 2 3 3 1 1 1 4 5 5 - 2 0 1 2 u 1 - 1 0 1 8 2 1 1 1 2 3 4 1 1 1 4 5 3 1 1 1 6 7 5 - 2 0 1 2 ( uassertNumQueriesuextra_queries_per_updateuOrderedInsertionuobjectsudelay_mptt_updatesucufuparentunameusaveuassertTreeEqualuall(uself((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_move_node_different_trees cCs|jdtgtjjQ|jdtd|j_|jjWdQX|j tjj dWdQXWdQX|j tjj ddS(Niiu 1 - 1 0 1 6 2 1 1 1 2 3 3 - 2 0 1 2 4 - 3 0 1 2 5 - 4 0 1 2 u 1 - 1 0 1 6 2 1 1 1 2 3 3 - 2 0 1 2 4 - 3 0 1 2 5 - 4 0 1 2 ( uassertNumQueriesuextra_queries_per_updateuOrderedInsertionuobjectsudelay_mptt_updatesuNoneueuparentusaveuassertTreeEqualuall(uself((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_move_node_to_roots cCs|jdtjtjjT|jdt!|j|j_|jjWdQX|j tjj dWdQXWdQX|j tjj ddS(Ni iu 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 3 1 2 5 6 5 - 2 0 1 2 u 1 - 1 0 1 8 2 1 1 1 2 3 3 1 1 1 4 7 4 3 1 2 5 6 5 - 2 0 1 2 ( uassertNumQueriesuextra_queries_per_updateuOrderedInsertionuobjectsudelay_mptt_updatesueufuparentusaveuassertTreeEqualuall(uself((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_move_root_to_childsN( u__name__u __module__usetUputest_insert_childutest_insert_rootutest_move_node_same_treeutest_move_node_different_treeutest_move_node_to_rootutest_move_root_to_child(u __locals__((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyu&OrderedInsertionDelayedUpdatesTestCase^s       u&OrderedInsertionDelayedUpdatesTestCasecBs;|EeZdgZdZdZdZdZdS(ucategories.jsoncCsvi}xitD]^}t|ts+qn|j}||krd|jd|j||jfn|||tdD]}|j}|dkr;Pq;q;W|jd|q WdS(Niu=Detected infinite recursion in %s._tree_manager._base_manager(u get_modelsu issubclassu MPTTModelu _tree_managerurangeu _base_manageruNoneufail(uselfumodelumanagerui((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyu$test_base_manager_infinite_recursion&s   cCsfdd}tjjdd}|j||ddg|j||dddddgdS( NcSs:tjj|d|}t|jdddjdS(Nu include_selfunameuflatT(uCategoryuobjectsuget_queryset_descendantsulistu values_listuTrueuorder_by(uqsu include_selfudesc((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyuget_desc_names4sunameu Nintendo WiiuGamesuHardware & Accessoriesu include_selfFT(uFalseuCategoryuobjectsufilteru assertEqualuTrue(uselfuget_desc_namesuqs((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_get_queryset_descendants3s   N(u__name__u __module__ufixturesutest_all_managers_are_differentu$test_all_managers_have_correct_modelu$test_base_manager_infinite_recursionutest_get_queryset_descendants(u __locals__((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyu ManagerTestss    u ManagerTestscBs&|EeZdZdgZdZdS(u@ Tests for the ``cache_tree_children`` template filter. ucategories.jsonc Cs|jdmttjj}|d}|jd}|jd}|j||j|j||jjWdQXdS(uW Ensures that each node's parent is cached by ``cache_tree_children``. iiN(uassertNumQueriesucache_tree_childrenuCategoryuobjectsuallu get_childrenu assertEqualuparent(uselfurootsugamesuwiiu wii_games((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyu'test_cache_tree_children_caches_parentsIs N(u__name__u __module__u__doc__ufixturesu'test_cache_tree_children_caches_parents(u __locals__((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyuCacheTreeChildrenTestCaseCs  uCacheTreeChildrenTestCasecBs5|EeZdZdgZdZdZdZdS(u8 Tests for the ``recursetree`` template filter. ucategories.jsonu{% load mptt_tags %}
    {% recursetree nodes %}
  • {{ node.name }}{% if not node.is_leaf_node %}
      {{ children }}
    {% endif %}
  • {% endrecursetree %}
cCsHt|jjtitjjddd6}|j|ddS(Nupki unodesu,
  • Hardware & Accessories
(uTemplateutemplateurenderuContextuCategoryuobjectsufilteru assertEqual(uselfuhtml((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_leaf_htmlnscCsZtjjddjdd}t|jjti|d6}|j |ddS(Nupkiu include_selfunodesuj
  • PlayStation 3
    • Games
    • Hardware & Accessories
T( uCategoryuobjectsugetuget_descendantsuTrueuTemplateutemplateurenderuContextu assertEqual(uselfuqsuhtml((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_nonleaf_htmlts ! N(u__name__u __module__u__doc__ufixturesutemplateutest_leaf_htmlutest_nonleaf_html(u __locals__((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyuRecurseTreeTestCaseYs   uRecurseTreeTestCasecBs|EeZdZdS(cCst}|jdS(N(uAutoNowDateFieldModelusave(uselfua((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyu#test_save_auto_now_date_field_models N(u__name__u __module__u#test_save_auto_now_date_field_model(u __locals__((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyuTestAutoNowDateFieldModels uTestAutoNowDateFieldModelcBs|EeZdZdS(cCs#tjjdd}|jdS(Nunameugroup 1(uGroupuobjectsucreateusave(uselfug1((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_save_registered_modelsN(u__name__u __module__utest_save_registered_model(u __locals__((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyuRegisteredRemoteModels uRegisteredRemoteModelcBs |EeZdgZdZdS(ucategories.jsoncCs>tjjdd}ttdtdd}|d|dS(Nunameu Nintendo Wiiuformufieldsuparentuinstance(unameuparent(uCategoryuobjectsugetumodelform_factoryu MPTTAdminForm(uselfucu CategoryForm((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_adminform_instantiations  N(u__name__u __module__ufixturesutest_adminform_instantiation(u __locals__((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyu TestFormss  u TestFormsF(ii(Fu __future__uunicode_literalsuosureusysutempfileudjangoudjango.contribuadminudjango.contrib.auth.modelsuGroupudjango.db.modelsu get_modelsudjango.forms.modelsumodelform_factoryudjango.templateuTemplateuContextu django.testuTestCaseudjango.utils.sixu string_typesuPY3ubufeincmsu ImportErroruFalseumptt.exceptionsuCantDisableUpdatesu InvalidMoveu mptt.formsu MPTTAdminFormu mptt.modelsu MPTTModelumptt.templatetags.mptt_tagsucache_tree_childrenu myapp.modelsuCategoryuGenreu CustomPKNameuSingleProxyModeluDoubleProxyModelu ConcreteModeluOrderedInsertionuAutoNowDateFieldModeluextra_queries_per_updateuVERSIONuget_tree_detailsucompileu MULTILINEuleading_whitespace_reu tree_detailsu TreeTestCaseuDocTestTestCaseuReparentingTestCaseuDeletionTestCaseuIntraTreeMovementTestCaseuInterTreeMovementTestCaseuPositionedInsertionTestCaseuFeinCMSModelAdminTestCaseuCustomPKNameTestCaseuDisabledUpdatesTestCaseuDelayedUpdatesTestCaseu&OrderedInsertionDelayedUpdatesTestCaseu ManagerTestsuCacheTreeChildrenTestCaseuRecurseTreeTestCaseuTestAutoNowDateFieldModeluRegisteredRemoteModelu TestForms(((u:/home/cdestigter/checkout/django-mptt/tests/myapp/tests.pyus\       :    9d2&django-mptt-0.8.0/tests/myapp/__pycache__/tests.cpython-33.pyc0000644000076500000240000014546412465236477025546 0ustar cdestigterstaff00000000000000 Gd,d-d-e4Z?Gd.d/d/e4Z@Gd0d1d1e4ZAGd2d3d3e4ZBGd4d5d5e4ZCGd6d7d7e4ZDGd8d9d9e4ZEdS(<i(uunicode_literalsN(uadmin(uGroup(u get_models(umodelform_factory(uTemplateuContext(uTestCase(u string_typesuPY3ub(uCantDisableUpdatesu InvalidMove(u MPTTAdminForm(u MPTTModel(ucache_tree_children(uCategoryuGenreu CustomPKNameuSingleProxyModeluDoubleProxyModelu ConcreteModeluOrderedInsertionuAutoNowDateFieldModeliicsft|dr-t|jddd}nt|}|djdjfdd|DS( u Creates pertinent tree details for the given list of nodes. The fields are: id parent_id tree_id level left right uorder_byutree_idulftupkiu c sug|]k}d|jt|djp.dt|jt|jt|jt|jfqS(u%s %s %s %s %s %su%s_idu-(upkugetattru parent_attru tree_id_attru level_attru left_attru right_attr(u.0un(uopts(u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyu -s u$get_tree_details..(uhasattrulistuorder_byu _mptt_metaujoin(unodes((uoptsu;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyuget_tree_details#s   uget_tree_detailsu^\s+cCstjd|jS(u Trims leading whitespace from the given text specifying tree details so triple-quoted strings can be used to provide tree details in a readable format (says who?), to be compared with the result of using the ``get_tree_details`` function. u(uleading_whitespace_reusuburstrip(utext((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyu tree_details6su tree_detailscBs |EeZdZddZdS(u TreeTestCasecCsqt|tst|}nt|}t|tsHt|}nt|}|j||d||fS(Nu %r != %r(u isinstanceu string_typesuget_tree_detailsu tree_detailsu assertEqual(uselfutree1utree2((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyuassertTreeEqualAs  uTreeTestCase.assertTreeEqualN(u__name__u __module__u __qualname__uassertTreeEqual(u __locals__((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyu TreeTestCase@su TreeTestCasecBs |EeZdZddZdS(uDocTestTestCasec Cs%Gddd}|}tj}ttjjtjjtd}tj }|j }t rt j dd|}|jt|n|jddl}|j|jdd d|jd d d d|t_|j}|rtjj|d |jnWdQXWdQXdS(NcBs8|EeZdZdZdZddZddZdS(u5DocTestTestCase.test_run_doctest..DummyStreamuuutf8cSs|j|7_dS(N(ucontent(uselfutext((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyuwriteQsu;DocTestTestCase.test_run_doctest..DummyStream.writecSsdS(N((uself((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyuflushTsu;DocTestTestCase.test_run_doctest..DummyStream.flushN(u__name__u __module__u __qualname__ucontentuencodinguwriteuflush(u __locals__((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyu DummyStreamMs u DummyStreamu doctests.txtu \bu(["\'])u\1iumodule_relativeu optionflagsuencodinguutf-8uraise_on_erroru FT(usysustdoutuopenuosupathujoinudirnameu__file__utempfileuNamedTemporaryFileureaduPY3ureusubuwriteubuflushudoctestutestfileunameuFalseuIGNORE_EXCEPTION_DETAILuTrueucontentustderrufail( uselfu DummyStreamu dummy_streamubeforeufutemputextudoctestucontent((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_run_doctestLs,  *       u DocTestTestCase.test_run_doctestN(u__name__u __module__u __qualname__utest_run_doctest(u __locals__((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyuDocTestTestCaseKsuDocTestTestCasecBs|EeZdZdZdgZddZddZddZd d Zd d Z d dZ ddZ ddZ ddZ dS(uReparentingTestCaseu Test that trees are in the appropriate state after reparenting and that reparented items have the correct tree attributes defined, should they be required for use after a save. u genres.jsoncCsXtjjdd}d|_|j|j|gd|jtjjddS(Nuidiu 6 - 3 0 1 6u 1 - 1 0 1 10 2 1 1 1 2 9 3 2 1 2 3 4 4 2 1 2 5 6 5 2 1 2 7 8 9 - 2 0 1 6 10 9 2 1 2 3 11 9 2 1 4 5 6 - 3 0 1 6 7 6 3 1 2 3 8 6 3 1 4 5 (uGenreuobjectsugetuNoneuparentusaveuassertTreeEqualuall(uselfushmup((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_new_root_from_subtrees    u.ReparentingTestCase.test_new_root_from_subtreecCsXtjjdd}d|_|j|j|gd|jtjjddS(Nuidiu 3 - 3 0 1 2u 1 - 1 0 1 14 2 1 1 1 2 7 4 2 1 2 3 4 5 2 1 2 5 6 6 1 1 1 8 13 7 6 1 2 9 10 8 6 1 2 11 12 9 - 2 0 1 6 10 9 2 1 2 3 11 9 2 1 4 5 3 - 3 0 1 2 (uGenreuobjectsugetuNoneuparentusaveuassertTreeEqualuall(uselfu platformer_2d((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyu%test_new_root_from_leaf_with_siblingss    u9ReparentingTestCase.test_new_root_from_leaf_with_siblingscCstjjdd}tjjdd}||_|j|j|gd|j|gd|jtjjddS(Nuidii u 1 9 2 1 6 21u 9 - 2 0 1 22u  9 - 2 0 1 22 10 9 2 1 2 3 11 9 2 1 4 5 1 9 2 1 6 21 2 1 2 2 7 14 3 2 2 3 8 9 4 2 2 3 10 11 5 2 2 3 12 13 6 1 2 2 15 20 7 6 2 3 16 17 8 6 2 3 18 19 (uGenreuobjectsugetuparentusaveuassertTreeEqualuall(uselfuactionurpg((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_new_child_from_roots   u,ReparentingTestCase.test_new_child_from_rootcCstjjdd}tjjdd}||_|j|j|gd|j|gd|jtjjddS(Nuidii u 8 9 2 1 6 7u 9 - 2 0 1 8u 1 - 1 0 1 14 2 1 1 1 2 9 3 2 1 2 3 4 4 2 1 2 5 6 5 2 1 2 7 8 6 1 1 1 10 13 7 6 1 2 11 12 9 - 2 0 1 8 10 9 2 1 2 3 11 9 2 1 4 5 8 9 2 1 6 7 (uGenreuobjectsugetuparentusaveuassertTreeEqualuall(uselfushmup_horizontalurpg((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_move_leaf_to_other_trees   u0ReparentingTestCase.test_move_leaf_to_other_treecCstjjdd}tjjdd}||_|j|j|gd|j|gd|jtjjddS(Nuidii u 6 11 2 2 5 10u 11 9 2 1 4 11u 1 - 1 0 1 10 2 1 1 1 2 9 3 2 1 2 3 4 4 2 1 2 5 6 5 2 1 2 7 8 9 - 2 0 1 12 10 9 2 1 2 3 11 9 2 1 4 11 6 11 2 2 5 10 7 6 2 3 6 7 8 6 2 3 8 9 (uGenreuobjectsugetuparentusaveuassertTreeEqualuall(uselfushmuputrpg((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_move_subtree_to_other_trees   u3ReparentingTestCase.test_move_subtree_to_other_treecCstjjdd}tjjdd}||_|j|j|gd|j|gd|jtjjddS(Nuidiiu 8 1 1 1 14 15u 1 - 1 0 1 16u 1 - 1 0 1 16 2 1 1 1 2 9 3 2 1 2 3 4 4 2 1 2 5 6 5 2 1 2 7 8 6 1 1 1 10 13 7 6 1 2 11 12 8 1 1 1 14 15 9 - 2 0 1 6 10 9 2 1 2 3 11 9 2 1 4 5 (uGenreuobjectsugetuparentusaveuassertTreeEqualuall(uselfushmup_horizontaluaction((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_move_child_up_levels   u,ReparentingTestCase.test_move_child_up_levelcCstjjdd}tjjdd}||_|j|j|gd|j|gd|jtjjddS(Nuidiiu 6 2 1 2 9 14u 2 1 1 1 2 15u 1 - 1 0 1 16 2 1 1 1 2 15 3 2 1 2 3 4 4 2 1 2 5 6 5 2 1 2 7 8 6 2 1 2 9 14 7 6 1 3 10 11 8 6 1 3 12 13 9 - 2 0 1 6 10 9 2 1 2 3 11 9 2 1 4 5 (uGenreuobjectsugetuparentusaveuassertTreeEqualuall(uselfushmupu platformer((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_move_subtree_down_levels   u0ReparentingTestCase.test_move_subtree_down_levelcCsXtjjdd}tjjdd}|j||j|j|j|dS(Nupki i(uGenreuobjectsugetumove_tousaveu assertEqualuparent(uselfurpguaction((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyu test_move_tos   u ReparentingTestCase.test_move_tocCstjjdd}||_tjjdd}||_|jt|j|jt|jtjjdd}||_||_|jt|j|jt|j|j|j||j|j|dS(Nuidiii(uGenreuobjectsugetuparentu assertRaisesu InvalidMoveusaveu assertEqual(uselfuactionu platformeru platformer_4d((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_invalid_moves#s    u&ReparentingTestCase.test_invalid_movesN(u__name__u __module__u __qualname__u__doc__ufixturesutest_new_root_from_subtreeu%test_new_root_from_leaf_with_siblingsutest_new_child_from_rootutest_move_leaf_to_other_treeutest_move_subtree_to_other_treeutest_move_child_up_levelutest_move_subtree_down_levelu test_move_toutest_invalid_moves(u __locals__((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyuReparentingTestCases         uReparentingTestCasecBs_|EeZdZdZdgZddZddZddZd d Zd d Z d S(uDeletionTestCaseuf Tests that the tree structure is maintained appropriately in various deletion scenarios. ucategories.jsoncCstddjtjjddddd tddjtjjddddd |jtjjd tjjddj|jtjjd dS( NunameuPreceding rootuidiuleftusaveuFollowing rooturightu7 11 - 1 0 1 2 1 - 2 0 1 20 2 1 2 1 2 7 3 2 2 2 3 4 4 2 2 2 5 6 5 1 2 1 8 13 6 5 2 2 9 10 7 5 2 2 11 12 8 1 2 1 14 19 9 8 2 2 15 16 10 8 2 2 17 18 12 - 3 0 1 2 u; 11 - 1 0 1 2 12 - 3 0 1 2 T(uCategoryu insert_atuobjectsugetuTrueuassertTreeEqualualludelete(uself((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_delete_root_nodeLs! !   u&DeletionTestCase.test_delete_root_nodecCs6tjjddj|jtjjddS(Nuidi u 1 - 1 0 1 18 2 1 1 1 2 7 3 2 1 2 3 4 4 2 1 2 5 6 5 1 1 1 8 13 6 5 1 2 9 10 7 5 1 2 11 12 8 1 1 1 14 17 10 8 1 2 15 16 (uCategoryuobjectsugetudeleteuassertTreeEqualuall(uself((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyu#test_delete_last_node_with_siblingshs u4DeletionTestCase.test_delete_last_node_with_siblingscCs6tjjddj|jtjjddS(Nuidiu 1 - 1 0 1 14 2 1 1 1 2 7 3 2 1 2 3 4 4 2 1 2 5 6 5 1 1 1 8 13 6 5 1 2 9 10 7 5 1 2 11 12 (uCategoryuobjectsugetudeleteuassertTreeEqualuall(uself((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyu&test_delete_last_node_with_descendantsvsu7DeletionTestCase.test_delete_last_node_with_descendantscCstjjdd}|j}|j|jd|j|jtjjd|j|jdtjjd|j }|j|jddS(Nuidiiu 1 - 1 0 1 18 2 1 1 1 2 7 3 2 1 2 3 4 4 2 1 2 5 6 5 1 1 1 8 11 7 5 1 2 9 10 8 1 1 1 12 17 9 8 1 2 13 14 10 8 1 2 15 16 iupk( uCategoryuobjectsugetuparentu assertEqualuget_descendant_countudeleteuassertTreeEqualuallupk(uselfuchilduparent((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_delete_node_with_siblingss   u/DeletionTestCase.test_delete_node_with_siblingscCs6tjjddj|jtjjddS(u Regression test for Issue 23 - we used to use pre_delete, which resulted in tree cleanup being performed for every node being deleted, rather than just the node on which ``delete()`` was called. uidiu 1 - 1 0 1 14 2 1 1 1 2 7 3 2 1 2 3 4 4 2 1 2 5 6 8 1 1 1 8 13 9 8 1 2 9 10 10 8 1 2 11 12 N(uCategoryuobjectsugetudeleteuassertTreeEqualuall(uself((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyu.test_delete_node_with_descendants_and_siblingssu?DeletionTestCase.test_delete_node_with_descendants_and_siblingsN( u__name__u __module__u __qualname__u__doc__ufixturesutest_delete_root_nodeu#test_delete_last_node_with_siblingsu&test_delete_last_node_with_descendantsutest_delete_node_with_siblingsu.test_delete_node_with_descendants_and_siblings(u __locals__((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyuDeletionTestCaseEs    uDeletionTestCasecBs|EeZdZdS(uIntraTreeMovementTestCaseN(u__name__u __module__u __qualname__(u __locals__((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyuIntraTreeMovementTestCasesuIntraTreeMovementTestCasecBs|EeZdZdS(uInterTreeMovementTestCaseN(u__name__u __module__u __qualname__(u __locals__((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyuInterTreeMovementTestCasesuInterTreeMovementTestCasecBs|EeZdZdS(uPositionedInsertionTestCaseN(u__name__u __module__u __qualname__(u __locals__((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyuPositionedInsertionTestCasesuPositionedInsertionTestCasecBs/|EeZdZdZdgZddZdS(uFeinCMSModelAdminTestCaseu. Tests for FeinCMSModelAdmin. ucategories.jsoncCs[ddlm}|ttj}tjjdd}|jd|j|dkdS(uQ The action column should have an "add" button inserted. i(uFeinCMSModelAdminuidiu*
N( u mptt.adminuFeinCMSModelAdminuCategoryuadminusiteuobjectsugetu assertTrueu_actions_column(uselfuFeinCMSModelAdminu model_adminucategory((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_actions_columns u-FeinCMSModelAdminTestCase.test_actions_columnN(u__name__u __module__u __qualname__u__doc__ufixturesutest_actions_column(u __locals__((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyuFeinCMSModelAdminTestCases uFeinCMSModelAdminTestCasecBs,|EeZdZddZddZdS(uCustomPKNameTestCasecCstj}|jdd}|jddd||jddd||jdd}|jddd||jddd||jdd dS( Nunameuc1uc11uparentuc12uc2uc21uc22uc3(u CustomPKNameuobjectsucreate(uselfumanageruc1uc2((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyusetUps uCustomPKNameTestCase.setUpcCs8tjjdd}|j}|j|dkdS(Nunameuc12(u CustomPKNameuobjectsugetuget_next_siblingu assertTrueuNone(uselfurootusib((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_get_next_siblings u*CustomPKNameTestCase.test_get_next_siblingN(u__name__u __module__u __qualname__usetUputest_get_next_sibling(u __locals__((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyuCustomPKNameTestCases uCustomPKNameTestCasecBs|EeZdZddZddZddZddZd d Zd d Zd dZ ddZ ddZ dS(uDisabledUpdatesTestCasecCstjjdd|_tjjddd|j|_tjjddd|j|_tjjdd|_|jtjjddS(Nunameuaubuparentucudui 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 ( u ConcreteModeluobjectsucreateuaubucuduassertTreeEqualuall(uself((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyusetUps !!uDisabledUpdatesTestCase.setUpc Cs|jtj|jtj|jttjjj|jtj|jtjtjj%|j tj|j tjWdQX|jtj|jtjdS(N( u assertTrueu ConcreteModelu_mptt_updates_enableduSingleProxyModelu assertRaisesuCantDisableUpdatesuobjectsudisable_mptt_updatesu __enter__u assertFalse(uself((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_single_proxysu)DisabledUpdatesTestCase.test_single_proxyc Cs|jtj|jtj|jttjjj|jtj|jtjtjj%|j tj|j tjWdQX|jtj|jtjdS(N( u assertTrueu ConcreteModelu_mptt_updates_enableduDoubleProxyModelu assertRaisesuCantDisableUpdatesuobjectsudisable_mptt_updatesu __enter__u assertFalse(uself((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_double_proxysu)DisabledUpdatesTestCase.test_double_proxycCs|jdftjjP|jd!tjjddd|jWdQX|jtjjdWdQXWdQX|jtjjddS(Niiunameueuparentu 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 5 4 2 1 2 3 u 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 5 4 2 1 2 3 (uassertNumQueriesu ConcreteModeluobjectsudisable_mptt_updatesucreateuduassertTreeEqualuall(uself((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_insert_child s"u)DisabledUpdatesTestCase.test_insert_childcCs|jd]tjjG|jdtjjddWdQX|jtjjdWdQXWdQX|jtjjddS(Niiunameueu 5 - 0 0 1 2 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 u 5 - 0 0 1 2 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 (uassertNumQueriesu ConcreteModeluobjectsudisable_mptt_updatesucreateuassertTreeEqualuall(uself((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_insert_root#su(DisabledUpdatesTestCase.test_insert_rootcCs|jdtjtjjT|jdt!|j|j_|jjWdQX|j tjj dWdQXWdQX|j tjj ddS(Niiu 1 - 1 0 1 6 2 1 1 1 2 3 3 2 1 1 4 5 4 - 2 0 1 2 ui 1 - 1 0 1 6 2 1 1 1 2 3 3 2 1 1 4 5 4 - 2 0 1 2 ( uassertNumQueriesuextra_queries_per_updateu ConcreteModeluobjectsudisable_mptt_updatesubucuparentusaveuassertTreeEqualuall(uself((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_move_node_same_tree9su0DisabledUpdatesTestCase.test_move_node_same_treecCs|jdtjtjjT|jdt!|j|j_|jjWdQX|j tjj dWdQXWdQX|j tjj ddS(Niiu 1 - 1 0 1 6 2 1 1 1 2 3 3 4 1 1 4 5 4 - 2 0 1 2 ui 1 - 1 0 1 6 2 1 1 1 2 3 3 4 1 1 4 5 4 - 2 0 1 2 ( uassertNumQueriesuextra_queries_per_updateu ConcreteModeluobjectsudisable_mptt_updatesuducuparentusaveuassertTreeEqualuall(uself((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_move_node_different_treeQsu5DisabledUpdatesTestCase.test_move_node_different_treecCs|jdtgtjjQ|jdtd|j_|jjWdQX|j tjj dWdQXWdQX|j tjj ddS(Niiu 1 - 1 0 1 6 2 1 1 1 2 3 3 - 1 1 4 5 4 - 2 0 1 2 ui 1 - 1 0 1 6 2 1 1 1 2 3 3 - 1 1 4 5 4 - 2 0 1 2 ( uassertNumQueriesuextra_queries_per_updateu ConcreteModeluobjectsudisable_mptt_updatesuNoneucuparentusaveuassertTreeEqualuall(uself((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_move_node_to_rooths u.DisabledUpdatesTestCase.test_move_node_to_rootcCs|jdtjtjjT|jdt!|j|j_|jjWdQX|j tjj dWdQXWdQX|j tjj ddS(Niiu 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 3 2 0 1 2 ui 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 3 2 0 1 2 ( uassertNumQueriesuextra_queries_per_updateu ConcreteModeluobjectsudisable_mptt_updatesucuduparentusaveuassertTreeEqualuall(uself((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_move_root_to_childsu/DisabledUpdatesTestCase.test_move_root_to_childN( u__name__u __module__u __qualname__usetUputest_single_proxyutest_double_proxyutest_insert_childutest_insert_rootutest_move_node_same_treeutest_move_node_different_treeutest_move_node_to_rootutest_move_root_to_child(u __locals__((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyuDisabledUpdatesTestCases       uDisabledUpdatesTestCasecBs|EeZdZddZddZddZddZd d Zd d Zd dZ ddZ ddZ dS(uDelayedUpdatesTestCasecCstjjdd|_tjjddd|j|_tjjddd|j|_tjjdd|_tjjdd|_|jtjj ddS( Nunameuaubuparentucuduzu 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 5 - 3 0 1 2 ( u ConcreteModeluobjectsucreateuaubucuduzuassertTreeEqualuall(uself((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyusetUps!!uDelayedUpdatesTestCase.setUpc Cs|jtj|jtj|jttjjj|jtj|jtjtjj%|j tj|j tjWdQX|jtj|jtjdS(N( u assertFalseu ConcreteModelu_mptt_is_trackinguSingleProxyModelu assertRaisesuCantDisableUpdatesuobjectsudelay_mptt_updatesu __enter__u assertTrue(uself((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyu test_proxysu!DelayedUpdatesTestCase.test_proxycCsptjjK|jtjtjj|jtjWdQX|jtjWdQX|jtjdS(N(u ConcreteModeluobjectsudelay_mptt_updatesu assertTrueu_mptt_is_trackingu assertFalse(uself((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_double_context_managers u2DelayedUpdatesTestCase.test_double_context_managercCs|jdftjjP|jd!tjjddd|jWdQX|jtjjdWdQXWdQX|jtjjddS(Niiunameueuparentu 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 6 4 2 1 2 3 5 - 3 0 1 2 u 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 4 6 4 2 1 2 3 5 - 3 0 1 2 (uassertNumQueriesu ConcreteModeluobjectsudelay_mptt_updatesucreateuduassertTreeEqualuall(uself((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_insert_childs"u(DelayedUpdatesTestCase.test_insert_childcCs|jd]tjjG|jdtjjddWdQX|jtjjdWdQXWdQX|jtjjddS(Niiunameueu 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 5 - 3 0 1 2 6 - 4 0 1 2 u 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 5 - 3 0 1 2 6 - 4 0 1 2 (uassertNumQueriesu ConcreteModeluobjectsudelay_mptt_updatesucreateuassertTreeEqualuall(uself((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_insert_rootsu'DelayedUpdatesTestCase.test_insert_rootcCs|jdtjtjjT|jdt!|j|j_|jjWdQX|j tjj dWdQXWdQX|j tjj ddS(Ni iu 1 - 1 0 1 6 2 1 1 1 2 3 3 2 1 2 3 4 4 - 2 0 1 2 5 - 3 0 1 2 u 1 - 1 0 1 6 2 1 1 1 2 5 3 2 1 2 3 4 4 - 2 0 1 2 5 - 3 0 1 2 ( uassertNumQueriesuextra_queries_per_updateu ConcreteModeluobjectsudelay_mptt_updatesubucuparentusaveuassertTreeEqualuall(uself((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_move_node_same_treesu/DelayedUpdatesTestCase.test_move_node_same_treecCs|jdtjtjjT|jdt!|j|j_|jjWdQX|j tjj dWdQXWdQX|j tjj ddS(Ni iu 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 3 1 2 5 6 5 - 2 0 1 2 u 1 - 1 0 1 8 2 1 1 1 2 3 3 1 1 1 4 7 4 3 1 2 5 6 5 - 2 0 1 2 ( uassertNumQueriesuextra_queries_per_updateu ConcreteModeluobjectsudelay_mptt_updatesucuduparentusaveuassertTreeEqualuall(uself((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_move_node_different_tree su4DelayedUpdatesTestCase.test_move_node_different_treecCs|jdtgtjjQ|jdtd|j_|jjWdQX|j tjj dWdQXWdQX|j tjj ddS(Niiu 1 - 1 0 1 6 2 1 1 1 2 3 4 - 2 0 1 2 5 - 3 0 1 2 3 - 4 0 1 2 u 1 - 1 0 1 6 2 1 1 1 2 3 4 - 2 0 1 2 5 - 3 0 1 2 3 - 4 0 1 2 ( uassertNumQueriesuextra_queries_per_updateu ConcreteModeluobjectsudelay_mptt_updatesuNoneucuparentusaveuassertTreeEqualuall(uself((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_move_node_to_root(s u-DelayedUpdatesTestCase.test_move_node_to_rootcCs|jdtjtjjT|jdt!|j|j_|jjWdQX|j tjj dWdQXWdQX|j tjj ddS(Ni iu 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 3 1 2 5 6 5 - 2 0 1 2 u 1 - 1 0 1 8 2 1 1 1 2 3 3 1 1 1 4 7 4 3 1 2 5 6 5 - 2 0 1 2 ( uassertNumQueriesuextra_queries_per_updateu ConcreteModeluobjectsudelay_mptt_updatesucuduparentusaveuassertTreeEqualuall(uself((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_move_root_to_childCsu.DelayedUpdatesTestCase.test_move_root_to_childN( u__name__u __module__u __qualname__usetUpu test_proxyutest_double_context_managerutest_insert_childutest_insert_rootutest_move_node_same_treeutest_move_node_different_treeutest_move_node_to_rootutest_move_root_to_child(u __locals__((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyuDelayedUpdatesTestCases        uDelayedUpdatesTestCasecBsh|EeZdZddZddZddZddZd d Zd d Zd dZ dS(u&OrderedInsertionDelayedUpdatesTestCasecCstjjdd|_tjjddd|j|_tjjddd|j|_tjjdd|_tjjdd|_|jtjj ddS( Nunameucuduparentueufuzu 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 5 - 3 0 1 2 ( uOrderedInsertionuobjectsucreateucudueufuzuassertTreeEqualuall(uself((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyusetUp`s!!u,OrderedInsertionDelayedUpdatesTestCase.setUpcCs|jdftjjP|jd!tjjddd|jWdQX|jtjjdWdQXWdQX|jtjjddS(Ni iunameudduparentu 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 6 1 1 1 6 7 4 - 2 0 1 2 5 - 3 0 1 2 u 1 - 1 0 1 8 2 1 1 1 2 3 6 1 1 1 4 5 3 1 1 1 6 7 4 - 2 0 1 2 5 - 3 0 1 2 (uassertNumQueriesuOrderedInsertionuobjectsudelay_mptt_updatesucreateucuassertTreeEqualuall(uself((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_insert_childos"u8OrderedInsertionDelayedUpdatesTestCase.test_insert_childcCs|jd]tjjG|jdtjjddWdQX|jtjjdWdQXWdQX|jtjjddS(Niiunameueeu 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 6 - 2 0 1 2 4 - 3 0 1 2 5 - 4 0 1 2 u 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 6 - 2 0 1 2 4 - 3 0 1 2 5 - 4 0 1 2 (uassertNumQueriesuOrderedInsertionuobjectsudelay_mptt_updatesucreateuassertTreeEqualuall(uself((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_insert_rootsu7OrderedInsertionDelayedUpdatesTestCase.test_insert_rootcCs|jdtgtjjQ|jdtd|j_|jjWdQX|jtjj dWdQXWdQX|jtjj ddS(Ni iubefore du 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 5 - 3 0 1 2 u 1 - 1 0 1 6 3 1 1 1 2 3 2 1 1 1 4 5 4 - 2 0 1 2 5 - 3 0 1 2 ( uassertNumQueriesuextra_queries_per_updateuOrderedInsertionuobjectsudelay_mptt_updatesueunameusaveuassertTreeEqualuall(uself((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_move_node_same_trees u?OrderedInsertionDelayedUpdatesTestCase.test_move_node_same_treecCs|jdtvtjj`|jdt-|j|j_d|j_|jj WdQX|j tjj dWdQXWdQX|j tjj ddS(Ni iuddu 1 - 1 0 1 6 2 1 1 1 2 3 4 1 1 1 2 3 3 1 1 1 4 5 5 - 2 0 1 2 u 1 - 1 0 1 8 2 1 1 1 2 3 4 1 1 1 4 5 3 1 1 1 6 7 5 - 2 0 1 2 ( uassertNumQueriesuextra_queries_per_updateuOrderedInsertionuobjectsudelay_mptt_updatesucufuparentunameusaveuassertTreeEqualuall(uself((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_move_node_different_trees uDOrderedInsertionDelayedUpdatesTestCase.test_move_node_different_treecCs|jdtgtjjQ|jdtd|j_|jjWdQX|j tjj dWdQXWdQX|j tjj ddS(Niiu 1 - 1 0 1 6 2 1 1 1 2 3 3 - 2 0 1 2 4 - 3 0 1 2 5 - 4 0 1 2 u 1 - 1 0 1 6 2 1 1 1 2 3 3 - 2 0 1 2 4 - 3 0 1 2 5 - 4 0 1 2 ( uassertNumQueriesuextra_queries_per_updateuOrderedInsertionuobjectsudelay_mptt_updatesuNoneueuparentusaveuassertTreeEqualuall(uself((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_move_node_to_roots u=OrderedInsertionDelayedUpdatesTestCase.test_move_node_to_rootcCs|jdtjtjjT|jdt!|j|j_|jjWdQX|j tjj dWdQXWdQX|j tjj ddS(Ni iu 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 3 1 2 5 6 5 - 2 0 1 2 u 1 - 1 0 1 8 2 1 1 1 2 3 3 1 1 1 4 7 4 3 1 2 5 6 5 - 2 0 1 2 ( uassertNumQueriesuextra_queries_per_updateuOrderedInsertionuobjectsudelay_mptt_updatesueufuparentusaveuassertTreeEqualuall(uself((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_move_root_to_childsu>OrderedInsertionDelayedUpdatesTestCase.test_move_root_to_childN( u__name__u __module__u __qualname__usetUputest_insert_childutest_insert_rootutest_move_node_same_treeutest_move_node_different_treeutest_move_node_to_rootutest_move_root_to_child(u __locals__((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyu&OrderedInsertionDelayedUpdatesTestCase_s      u&OrderedInsertionDelayedUpdatesTestCasecBsM|EeZdZdgZddZddZddZdd Zd S( u ManagerTestsucategories.jsoncCsvi}xitD]^}t|ts+qn|j}||krd|jd|j||jfn|||tdD]}|j}|dkr;Pq;q;W|jd|q WdS(Niu=Detected infinite recursion in %s._tree_manager._base_manager(u get_modelsu issubclassu MPTTModelu _tree_managerurangeu _base_manageruNoneufail(uselfumodelumanagerui((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyu$test_base_manager_infinite_recursion's   u1ManagerTests.test_base_manager_infinite_recursioncCsiddd}tjjdd}|j||ddg|j||dd dddgdS( NcSs:tjj|d|}t|jdddjdS(Nu include_selfunameuflatT(uCategoryuobjectsuget_queryset_descendantsulistu values_listuTrueuorder_by(uqsu include_selfudesc((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyuget_desc_names5suBManagerTests.test_get_queryset_descendants..get_desc_namesunameu Nintendo WiiuGamesuHardware & Accessoriesu include_selfFT(uFalseuCategoryuobjectsufilteru assertEqualuTrue(uselfuget_desc_namesuqs((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_get_queryset_descendants4s  u*ManagerTests.test_get_queryset_descendantsN(u__name__u __module__u __qualname__ufixturesutest_all_managers_are_differentu$test_all_managers_have_correct_modelu$test_base_manager_infinite_recursionutest_get_queryset_descendants(u __locals__((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyu ManagerTestss    u ManagerTestscBs/|EeZdZdZdgZddZdS(uCacheTreeChildrenTestCaseu@ Tests for the ``cache_tree_children`` template filter. ucategories.jsonc Cs|jdmttjj}|d}|jd}|jd}|j||j|j||jjWdQXdS(uW Ensures that each node's parent is cached by ``cache_tree_children``. iiN(uassertNumQueriesucache_tree_childrenuCategoryuobjectsuallu get_childrenu assertEqualuparent(uselfurootsugamesuwiiu wii_games((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyu'test_cache_tree_children_caches_parentsJs uACacheTreeChildrenTestCase.test_cache_tree_children_caches_parentsN(u__name__u __module__u __qualname__u__doc__ufixturesu'test_cache_tree_children_caches_parents(u __locals__((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyuCacheTreeChildrenTestCaseDs uCacheTreeChildrenTestCasecBsA|EeZdZdZdgZdZddZddZdS( uRecurseTreeTestCaseu8 Tests for the ``recursetree`` template filter. ucategories.jsonu{% load mptt_tags %}
    {% recursetree nodes %}
  • {{ node.name }}{% if not node.is_leaf_node %}
      {{ children }}
    {% endif %}
  • {% endrecursetree %}
cCsHt|jjtitjjddd6}|j|ddS(Nupki unodesu,
  • Hardware & Accessories
(uTemplateutemplateurenderuContextuCategoryuobjectsufilteru assertEqual(uselfuhtml((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_leaf_htmlosu"RecurseTreeTestCase.test_leaf_htmlcCsZtjjddjdd}t|jjti|d6}|j |ddS(Nupkiu include_selfunodesuj
  • PlayStation 3
    • Games
    • Hardware & Accessories
T( uCategoryuobjectsugetuget_descendantsuTrueuTemplateutemplateurenderuContextu assertEqual(uselfuqsuhtml((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_nonleaf_htmlus ! u%RecurseTreeTestCase.test_nonleaf_htmlN(u__name__u __module__u __qualname__u__doc__ufixturesutemplateutest_leaf_htmlutest_nonleaf_html(u __locals__((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyuRecurseTreeTestCaseZs   uRecurseTreeTestCasecBs |EeZdZddZdS(uTestAutoNowDateFieldModelcCst}|jdS(N(uAutoNowDateFieldModelusave(uselfua((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyu#test_save_auto_now_date_field_models u=TestAutoNowDateFieldModel.test_save_auto_now_date_field_modelN(u__name__u __module__u __qualname__u#test_save_auto_now_date_field_model(u __locals__((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyuTestAutoNowDateFieldModelsuTestAutoNowDateFieldModelcBs |EeZdZddZdS(uRegisteredRemoteModelcCs#tjjdd}|jdS(Nunameugroup 1(uGroupuobjectsucreateusave(uselfug1((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_save_registered_modelsu0RegisteredRemoteModel.test_save_registered_modelN(u__name__u __module__u __qualname__utest_save_registered_model(u __locals__((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyuRegisteredRemoteModelsuRegisteredRemoteModelcBs)|EeZdZdgZddZdS(u TestFormsucategories.jsoncCs>tjjdd}ttdtdf}|d|dS(Nunameu Nintendo Wiiuformuexcludeuinstance(uCategoryuobjectsugetumodelform_factoryu MPTTAdminForm(uselfucu CategoryForm((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyutest_adminform_instantiationsu&TestForms.test_adminform_instantiationN(u__name__u __module__u __qualname__ufixturesutest_adminform_instantiation(u __locals__((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyu TestFormss u TestFormsF(ii(Fu __future__uunicode_literalsuosureusysutempfileudjangoudjango.contribuadminudjango.contrib.auth.modelsuGroupudjango.db.modelsu get_modelsudjango.forms.modelsumodelform_factoryudjango.templateuTemplateuContextu django.testuTestCaseudjango.utils.sixu string_typesuPY3ubufeincmsu ImportErroruFalseumptt.exceptionsuCantDisableUpdatesu InvalidMoveu mptt.formsu MPTTAdminFormu mptt.modelsu MPTTModelumptt.templatetags.mptt_tagsucache_tree_childrenu myapp.modelsuCategoryuGenreu CustomPKNameuSingleProxyModeluDoubleProxyModelu ConcreteModeluOrderedInsertionuAutoNowDateFieldModeluextra_queries_per_updateuVERSIONuget_tree_detailsucompileu MULTILINEuleading_whitespace_reu tree_detailsu TreeTestCaseuDocTestTestCaseuReparentingTestCaseuDeletionTestCaseuIntraTreeMovementTestCaseuInterTreeMovementTestCaseuPositionedInsertionTestCaseuFeinCMSModelAdminTestCaseuCustomPKNameTestCaseuDisabledUpdatesTestCaseuDelayedUpdatesTestCaseu&OrderedInsertionDelayedUpdatesTestCaseu ManagerTestsuCacheTreeChildrenTestCaseuRecurseTreeTestCaseuTestAutoNowDateFieldModeluRegisteredRemoteModelu TestForms(((u;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyus\       :    :d2&django-mptt-0.8.0/tests/myapp/__pycache__/tests.cpython-34.pyc0000644000076500000240000017334012635626147025535 0ustar cdestigterstaff00000000000000 +wV @sddlmZddlZddlZddlZddlZddlZddlZddlm Z m Z ddl m Z ddl mZddlmZddlmZddlmZmZmZdd lmZdd lmZmZmZmZydd lmZWne k r*dZYnXdd l!m"Z"m#Z#dd l$m%Z%m&Z&m'Z'm(Z(ddl)m*Z*ddl+m,Z,ddl-m.Z.ddl/m0Z0ddl1m2Z2ddl3m4Z4m5Z5m6Z6m7Z7m8Z8m9Z9m:Z:m;Z;m<Z<m=Z=m>Z>m?Z?m@Z@mAZAddZBejCdejDZEddZFGdddeZGGdddeGZHGdddeGZIGdd d eGZJGd!d"d"eGZKGd#d$d$eGZLGd%d&d&eGZMGd'd(d(eGZNGd)d*d*eGZOGd+d,d,eGZPGd-d.d.eGZQGd/d0d0eGZRGd1d2d2eGZSGd3d4d4eGZTGd5d6d6eGZUGd7d8d8eGZVGd9d:d:eGZWGd;d<d<eGZXGd=d>d>eGZYGd?d@d@eGZZGdAdBdBeGZ[GdCdDdDeGZ\GdEdFdFeGZ]GdGdHdHeGZ^GdIdJdJeGZ_GdKdLdLeGZ`GdMdNdNeGZaGdOdPdPeGZbGdQdRdReGZcejdedSGdTdUdUeZeGdVdWdWeGZfdS)X)unicode_literalsN)GroupUser)Q)DeferredAttribute)apps)modelform_factory)TemplateTemplateSyntaxErrorContext)TestCase) string_typesPY3bassertRaisesRegex)mock_signal_receiver)CantDisableUpdates InvalidMove) MPTTAdminFormTreeNodeChoiceFieldTreeNodeMultipleChoiceField MoveNodeForm) MPTTModel) TreeManager) node_moved)cache_tree_children)print_debug_info)CategoryItemGenre CustomPKNameSingleProxyModelDoubleProxyModel ConcreteModelOrderedInsertionAutoNowDateFieldModelPersonCustomTreeQuerysetNodeReferencingModelCustomTreeManagercsft|dr-t|jddd}nt|}|djdjfdd|DS) z Creates pertinent tree details for the given list of nodes. The fields are: id parent_id tree_id level left right order_bytree_idlftpkr c sug|]k}d|jt|djp.dt|jt|jt|jt|jfqS)z%s %s %s %s %s %sz%s_id-)r.getattr parent_attr tree_id_attr level_attr left_attr right_attr).0n)opts;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.py 3s z$get_tree_details..)hasattrlistr+ _mptt_metajoin)nodesr:)r9r;get_tree_details)s   rBz^\s+cCstjd|jS)a Trims leading whitespace from the given text specifying tree details so triple-quoted strings can be used to provide tree details in a readable format (says who?), to be compared with the result of using the ``get_tree_details`` function. )leading_whitespace_resubrstrip)textr:r:r; tree_details<srHc@seZdZddZdS) TreeTestCasecCsqt|tst|}nt|}t|tsHt|}nt|}|j||d||fS)Nz %r != %r) isinstancer rBrH assertEqual)selfZtree1Ztree2r:r:r;assertTreeEqualGs  zTreeTestCase.assertTreeEqualN)__name__ __module__ __qualname__rMr:r:r:r;rIFs rIc@seZdZddZdS)DocTestTestCasec Cs2Gddd}|}tj}|t_ttjjtjjtd}tj }|j }t rt j dd|}|jt|n |j||jddl}|j|jddd |jd d |t_|j}|r"|j|d |jnWdQXWdQXdS) Nc@s4eZdZdZdZddZddZdS)z5DocTestTestCase.test_run_doctest..DummyStreamrCutf8cSs|j|7_dS)N)content)rLrGr:r:r;writeWsz;DocTestTestCase.test_run_doctest..DummyStream.writecSsdS)Nr:)rLr:r:r;flushZsz;DocTestTestCase.test_run_doctest..DummyStream.flushN)rNrOrPrSencodingrTrUr:r:r:r; DummyStreamSs  rWz doctests.txtz \bu(["\'])z\1rZmodule_relativeFZ optionflagsrVzutf-8r/)sysstdoutopenospathr@dirname__file__tempfileNamedTemporaryFilereadrrerErTrrUdoctestZtestfilenameZIGNORE_EXCEPTION_DETAILrSfail) rLrWZ dummy_streambeforeftemprGrcrSr:r:r;test_run_doctestRs.   *        z DocTestTestCase.test_run_doctestN)rNrOrPrir:r:r:r;rQQs rQc@seZdZdZdgZddZddZddZd d Zd d Z d dZ ddZ ddZ ddZ dS)ReparentingTestCasez Test that trees are in the appropriate state after reparenting and that reparented items have the correct tree attributes defined, should they be required for use after a save. z genres.jsoncCsXtjjdd}d|_|j|j|gd|jtjjddS)Nidz 6 - 3 0 1 6a 1 - 1 0 1 10 2 1 1 1 2 9 3 2 1 2 3 4 4 2 1 2 5 6 5 2 1 2 7 8 9 - 2 0 1 6 10 9 2 1 2 3 11 9 2 1 4 5 6 - 3 0 1 6 7 6 3 1 2 3 8 6 3 1 4 5 )robjectsgetparentsaverMall)rLshmupr:r:r;test_new_root_from_subtrees    z.ReparentingTestCase.test_new_root_from_subtreecCsXtjjdd}d|_|j|j|gd|jtjjddS)Nrkz 3 - 3 0 1 2a 1 - 1 0 1 14 2 1 1 1 2 7 4 2 1 2 3 4 5 2 1 2 5 6 6 1 1 1 8 13 7 6 1 2 9 10 8 6 1 2 11 12 9 - 2 0 1 6 10 9 2 1 2 3 11 9 2 1 4 5 3 - 3 0 1 2 )rrmrnrorprMrq)rLZ platformer_2dr:r:r;%test_new_root_from_leaf_with_siblingss    z9ReparentingTestCase.test_new_root_from_leaf_with_siblingscCstjjdd}tjjdd}||_|j|j|gd|j|gd|jtjjddS)Nrk z 1 9 2 1 6 21z 9 - 2 0 1 22a  9 - 2 0 1 22 10 9 2 1 2 3 11 9 2 1 4 5 1 9 2 1 6 21 2 1 2 2 7 14 3 2 2 3 8 9 4 2 2 3 10 11 5 2 2 3 12 13 6 1 2 2 15 20 7 6 2 3 16 17 8 6 2 3 18 19 )rrmrnrorprMrq)rLactionrpgr:r:r;test_new_child_from_roots   z,ReparentingTestCase.test_new_child_from_rootcCstjjdd}tjjdd}||_|j|j|gd|j|gd|jtjjddS)Nrkrwz 8 9 2 1 6 7z 9 - 2 0 1 8a 1 - 1 0 1 14 2 1 1 1 2 9 3 2 1 2 3 4 4 2 1 2 5 6 5 2 1 2 7 8 6 1 1 1 10 13 7 6 1 2 11 12 9 - 2 0 1 8 10 9 2 1 2 3 11 9 2 1 4 5 8 9 2 1 6 7 )rrmrnrorprMrq)rLshmup_horizontalryr:r:r;test_move_leaf_to_other_trees   z0ReparentingTestCase.test_move_leaf_to_other_treecCstjjdd}tjjdd}||_|j|j|gd|j|gd|jtjjddS)Nrkrl z 6 11 2 2 5 10z 11 9 2 1 4 11a 1 - 1 0 1 10 2 1 1 1 2 9 3 2 1 2 3 4 4 2 1 2 5 6 5 2 1 2 7 8 9 - 2 0 1 12 10 9 2 1 2 3 11 9 2 1 4 11 6 11 2 2 5 10 7 6 2 3 6 7 8 6 2 3 8 9 )rrmrnrorprMrq)rLrrZtrpgr:r:r;test_move_subtree_to_other_trees   z3ReparentingTestCase.test_move_subtree_to_other_treecCstjjdd}tjjdd}||_|j|j|gd|j|gd|jtjjddS)Nrkr{rvz 8 1 1 1 14 15z 1 - 1 0 1 16a 1 - 1 0 1 16 2 1 1 1 2 9 3 2 1 2 3 4 4 2 1 2 5 6 5 2 1 2 7 8 6 1 1 1 10 13 7 6 1 2 11 12 8 1 1 1 14 15 9 - 2 0 1 6 10 9 2 1 2 3 11 9 2 1 4 5 )rrmrnrorprMrq)rLr|rxr:r:r;test_move_child_up_levels   z,ReparentingTestCase.test_move_child_up_levelcCstjjdd}tjjdd}||_|j|j|gd|j|gd|jtjjddS)Nrkrlz 6 2 1 2 9 14z 2 1 1 1 2 15a 1 - 1 0 1 16 2 1 1 1 2 15 3 2 1 2 3 4 4 2 1 2 5 6 5 2 1 2 7 8 6 2 1 2 9 14 7 6 1 3 10 11 8 6 1 3 12 13 9 - 2 0 1 6 10 9 2 1 2 3 11 9 2 1 4 5 )rrmrnrorprMrq)rLrr platformerr:r:r;test_move_subtree_down_levels   z0ReparentingTestCase.test_move_subtree_down_levelcCsXtjjdd}tjjdd}|j||j|j|j|dS)Nr.rwrv)rrmrnmove_torprKro)rLryrxr:r:r; test_move_to#s   z ReparentingTestCase.test_move_tocCstjjdd}||_tjjdd}||_|jt|j|jt|jtjjdd}||_||_|jt|j|jt|j|j|j||j|j|dS)Nrkrvr)rrmrnro assertRaisesrrprK)rLrxrZ platformer_4dr:r:r;test_invalid_moves*s    z&ReparentingTestCase.test_invalid_movesN)rNrOrP__doc__fixturesrsrurzr}rrrrrr:r:r:r;rjs          rjc@s^eZdZdZddZddZddZdd Zd d Zd d Z dS)ConcurrencyTestCasez Test that tree structure remains intact when saving nodes (without setting new parent) after tree structure has been changed. cCstjjdd}tjjdd}tjjddd|tjjddd|tjjddd|tjjddd||jtjjd dS) NrdFruitVegieZAppleroZPearZTomatoZCarrotz 1 - 1 0 1 6 3 1 1 1 2 3 4 1 1 1 4 5 2 - 2 0 1 6 5 2 2 1 2 3 6 2 2 1 4 5 )r#rmcreaterMrq)rLfruitvegier:r:r;setUpDszConcurrencyTestCase.setUpcCs;tjjdd}tjjdd}|j|dS)Nrdrr)r#rmrnr)rLrrr:r:r; _modify_treeVsz ConcurrencyTestCase._modify_treecCsEtjjdd}|j|gd|jtjjddS)Nrkrlz 6 2 1 2 5 6z 1 - 1 0 1 12 2 1 1 1 2 7 5 2 1 2 3 4 6 2 1 2 5 6 3 1 1 1 8 9 4 1 1 1 10 11 )r#rmrnrMrq)rLcarrotr:r:r;_assert_modified_tree_state[sz/ConcurrencyTestCase._assert_modified_tree_statecCs@tjjdd}|jd|_|j|jdS)Nrkrlz Purple carrot)r#rmrnrrdrpr)rLrr:r:r;'test_node_save_after_tree_restructuringgs    z;ConcurrencyTestCase.test_node_save_after_tree_restructuringcCstjjdd}|jd|_d|_|jddg|jtjjdd}|j|j|j|j |j|jd|_d|_|jd d d d gtjjdd}|j |j|j|j|j|jd S) zN Test that model is saved properly when passing update_fields rkrlz Won't changezWill get updated update_fieldsghostsz Will changezWill not be updatedFNrd) r#rmrnrrdrrprrKassertNotEqual)rLrZupdated_carrotr:r:r;:test_node_save_after_tree_restructuring_with_update_fieldsqs      zNConcurrencyTestCase.test_node_save_after_tree_restructuring_with_update_fieldscCs/tjjdd}|jdddddS)z Test that update_fields works as a positional argument Test for https://github.com/django-mptt/django-mptt/issues/384 rkrlFN)r#rmrnrp)rLrr:r:r;test_update_fields_positionalsz1ConcurrencyTestCase.test_update_fields_positionalN) rNrOrPrrrrrrrr:r:r:r;r?s    rc@s[eZdZdZdgZddZddZddZd d Zd d Z d S)DeletionTestCasezf Tests that the tree structure is maintained appropriately in various deletion scenarios. zcategories.jsoncCstddjtjjdddddtddjtjjddd dd|jtjjd tjjddj|jtjjd dS) NrdzPreceding rootrkrvleftrpTzFollowing rootrighta7 11 - 1 0 1 2 1 - 2 0 1 20 2 1 2 1 2 7 3 2 2 2 3 4 4 2 2 2 5 6 5 1 2 1 8 13 6 5 2 2 9 10 7 5 2 2 11 12 8 1 2 1 14 19 9 8 2 2 15 16 10 8 2 2 17 18 12 - 3 0 1 2 z; 11 - 1 0 1 2 12 - 3 0 1 2 )r insert_atrmrnrMrqdelete)rLr:r:r;test_delete_root_nodes! !   z&DeletionTestCase.test_delete_root_nodecCs6tjjddj|jtjjddS)Nrkrwz 1 - 1 0 1 18 2 1 1 1 2 7 3 2 1 2 3 4 4 2 1 2 5 6 5 1 1 1 8 13 6 5 1 2 9 10 7 5 1 2 11 12 8 1 1 1 14 17 10 8 1 2 15 16 )rrmrnrrMrq)rLr:r:r;#test_delete_last_node_with_siblingss z4DeletionTestCase.test_delete_last_node_with_siblingscCs6tjjddj|jtjjddS)Nrkr{z 1 - 1 0 1 14 2 1 1 1 2 7 3 2 1 2 3 4 4 2 1 2 5 6 5 1 1 1 8 13 6 5 1 2 9 10 7 5 1 2 11 12 )rrmrnrrMrq)rLr:r:r;&test_delete_last_node_with_descendantssz7DeletionTestCase.test_delete_last_node_with_descendantscCstjjdd}|j}|j|jd|j|jtjjd|j|jdtjjd|j }|j|jddS)Nrkrlrz 1 - 1 0 1 18 2 1 1 1 2 7 3 2 1 2 3 4 4 2 1 2 5 6 5 1 1 1 8 11 7 5 1 2 9 10 8 1 1 1 12 17 9 8 1 2 13 14 10 8 1 2 15 16 rvr.) rrmrnrorKget_descendant_countrrMrqr.)rLchildror:r:r;test_delete_node_with_siblingss   z/DeletionTestCase.test_delete_node_with_siblingscCs6tjjddj|jtjjddS)z Regression test for Issue 23 - we used to use pre_delete, which resulted in tree cleanup being performed for every node being deleted, rather than just the node on which ``delete()`` was called. rkrz 1 - 1 0 1 14 2 1 1 1 2 7 3 2 1 2 3 4 4 2 1 2 5 6 8 1 1 1 8 13 9 8 1 2 9 10 10 8 1 2 11 12 N)rrmrnrrMrq)rLr:r:r;.test_delete_node_with_descendants_and_siblingssz?DeletionTestCase.test_delete_node_with_descendants_and_siblingsN) rNrOrPrrrrrrrr:r:r:r;rs     rc@seZdZdS)IntraTreeMovementTestCaseN)rNrOrPr:r:r:r;r s rc@seZdZdS)InterTreeMovementTestCaseN)rNrOrPr:r:r:r;rs rc@seZdZdS)PositionedInsertionTestCaseN)rNrOrPr:r:r:r;rs rc@s(eZdZddZddZdS)CustomPKNameTestCasecCstj}|jdd}|jddd||jddd||jdd}|jddd||jddd||jdd dS) Nrdc1Zc11roc12c2Zc21Zc22Zc3)r rmr)rLmanagerrrr:r:r;rs zCustomPKNameTestCase.setUpcCs8tjjdd}|j}|j|dkdS)Nrdr)r rmrnget_next_sibling assertTrue)rLrootZsibr:r:r;test_get_next_sibling&s z*CustomPKNameTestCase.test_get_next_siblingN)rNrOrPrrr:r:r:r;rs  rc@s|eZdZddZddZddZddZd d Zd d Zd dZ ddZ ddZ dS)DisabledUpdatesTestCasecCstjjdd|_tjjddd|j|_tjjddd|j|_tjjdd|_|jtjjddS)Nrdarrocdzi 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 ) r#rmrrrrrrMrq)rLr:r:r;r-s !!zDisabledUpdatesTestCase.setUpc Cs|jtj|jtj|jttjjj|jtj|jtjtjj%|j tj|j tjWdQX|jtj|jtjdS)N) rr#_mptt_updates_enabledr!rrrmdisable_mptt_updates __enter__ assertFalse)rLr:r:r;test_single_proxy:sz)DisabledUpdatesTestCase.test_single_proxyc Cs|jtj|jtj|jttjjj|jtj|jtjtjj%|j tj|j tjWdQX|jtj|jtjdS)N) rr#rr"rrrmrrr)rLr:r:r;test_double_proxyLsz)DisabledUpdatesTestCase.test_double_proxycCs|jdftjjP|jd!tjjddd|jWdQX|jtjjdWdQXWdQX|jtjjddS)Nrrvrderoz 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 5 4 2 1 2 3 z 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 5 4 2 1 2 3 )assertNumQueriesr#rmrrrrMrq)rLr:r:r;test_insert_child^s"z)DisabledUpdatesTestCase.test_insert_childcCs|jd]tjjG|jdtjjddWdQX|jtjjdWdQXWdQX|jtjjddS)Nrrvrdrz 5 - 0 0 1 2 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 z 5 - 0 0 1 2 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 )rr#rmrrrMrq)rLr:r:r;test_insert_rootvsz(DisabledUpdatesTestCase.test_insert_rootcCs|jdftjjP|jd!|j|j_|jjWdQX|jtjj dWdQXWdQX|jtjj ddS)Nrrvz 1 - 1 0 1 6 2 1 1 1 2 3 3 2 1 1 4 5 4 - 2 0 1 2 zi 1 - 1 0 1 6 2 1 1 1 2 3 3 2 1 1 4 5 4 - 2 0 1 2 ) rr#rmrrrrorprMrq)rLr:r:r;test_move_node_same_treesz0DisabledUpdatesTestCase.test_move_node_same_treecCs|jdftjjP|jd!|j|j_|jjWdQX|jtjj dWdQXWdQX|jtjj ddS)Nrrvz 1 - 1 0 1 6 2 1 1 1 2 3 3 4 1 1 4 5 4 - 2 0 1 2 zi 1 - 1 0 1 6 2 1 1 1 2 3 3 4 1 1 4 5 4 - 2 0 1 2 ) rr#rmrrrrorprMrq)rLr:r:r;test_move_node_different_treesz5DisabledUpdatesTestCase.test_move_node_different_treecCs|jdctjjM|jdd|j_|jjWdQX|jtjjdWdQXWdQX|jtjjddS)Nrrvz 1 - 1 0 1 6 2 1 1 1 2 3 3 - 1 1 4 5 4 - 2 0 1 2 zi 1 - 1 0 1 6 2 1 1 1 2 3 3 - 1 1 4 5 4 - 2 0 1 2 ) rr#rmrrrorprMrq)rLr:r:r;test_move_node_to_roots z.DisabledUpdatesTestCase.test_move_node_to_rootcCs|jdftjjP|jd!|j|j_|jjWdQX|jtjj dWdQXWdQX|jtjj ddS)Nrrvz 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 3 2 0 1 2 zi 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 3 2 0 1 2 ) rr#rmrrrrorprMrq)rLr:r:r;test_move_root_to_childsz/DisabledUpdatesTestCase.test_move_root_to_childN) rNrOrPrrrrrrrrrr:r:r:r;r,s        rc@s|eZdZddZddZddZddZd d Zd d Zd dZ ddZ ddZ dS)DelayedUpdatesTestCasecCstjjdd|_tjjddd|j|_tjjddd|j|_tjjdd|_tjjdd|_|jtjj ddS) Nrdrrrorrzz 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 5 - 3 0 1 2 ) r#rmrrrrrrrMrq)rLr:r:r;rs!!zDelayedUpdatesTestCase.setUpc Cs|jtj|jtj|jttjjj|jtj|jtjtjj%|j tj|j tjWdQX|jtj|jtjdS)N) rr#_mptt_is_trackingr!rrrmdelay_mptt_updatesrr)rLr:r:r; test_proxysz!DelayedUpdatesTestCase.test_proxycCsptjjK|jtjtjj|jtjWdQX|jtjWdQX|jtjdS)N)r#rmrrrr)rLr:r:r;test_double_context_manager s z2DelayedUpdatesTestCase.test_double_context_managercCs|jdftjjP|jd!tjjddd|jWdQX|jtjjdWdQXWdQX|jtjjddS)Nr{rrdrroz 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 6 4 2 1 2 3 5 - 3 0 1 2 z 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 4 6 4 2 1 2 3 5 - 3 0 1 2 )rr#rmrrrrMrq)rLr:r:r;rs"z(DelayedUpdatesTestCase.test_insert_childcCs|jd]tjjG|jdtjjddWdQX|jtjjdWdQXWdQX|jtjjddS)Nrtrrdrz 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 5 - 3 0 1 2 6 - 4 0 1 2 z 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 5 - 3 0 1 2 6 - 4 0 1 2 )rr#rmrrrMrq)rLr:r:r;r/sz'DelayedUpdatesTestCase.test_insert_rootcCs|jdftjjP|jd!|j|j_|jjWdQX|jtjj dWdQXWdQX|jtjj ddS)N rz 1 - 1 0 1 6 2 1 1 1 2 3 3 2 1 2 3 4 4 - 2 0 1 2 5 - 3 0 1 2 z 1 - 1 0 1 6 2 1 1 1 2 5 3 2 1 2 3 4 4 - 2 0 1 2 5 - 3 0 1 2 ) rr#rmrrrrorprMrq)rLr:r:r;rJsz/DelayedUpdatesTestCase.test_move_node_same_treecCs|jdftjjP|jd!|j|j_|jjWdQX|jtjj dWdQXWdQX|jtjj ddS)N rz 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 3 1 2 5 6 5 - 2 0 1 2 z 1 - 1 0 1 8 2 1 1 1 2 3 3 1 1 1 4 7 4 3 1 2 5 6 5 - 2 0 1 2 ) rr#rmrrrrorprMrq)rLr:r:r;rdsz4DelayedUpdatesTestCase.test_move_node_different_treecCs|jdctjjM|jdd|j_|jjWdQX|jtjjdWdQXWdQX|jtjjddS)Nrtz 1 - 1 0 1 6 2 1 1 1 2 3 4 - 2 0 1 2 5 - 3 0 1 2 3 - 4 0 1 2 z 1 - 1 0 1 6 2 1 1 1 2 3 4 - 2 0 1 2 5 - 3 0 1 2 3 - 4 0 1 2 ) rr#rmrrrorprMrq)rLr:r:r;rs z-DelayedUpdatesTestCase.test_move_node_to_rootcCs|jdftjjP|jd!|j|j_|jjWdQX|jtjj dWdQXWdQX|jtjj ddS)Nrrz 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 3 1 2 5 6 5 - 2 0 1 2 z 1 - 1 0 1 8 2 1 1 1 2 3 3 1 1 1 4 7 4 3 1 2 5 6 5 - 2 0 1 2 ) rr#rmrrrrorprMrq)rLr:r:r;rsz.DelayedUpdatesTestCase.test_move_root_to_childN) rNrOrPrrrrrrrrrr:r:r:r;rs         rc@sdeZdZddZddZddZddZd d Zd d Zd dZ dS)&OrderedInsertionDelayedUpdatesTestCasecCstjjdd|_tjjddd|j|_tjjddd|j|_tjjdd|_tjjdd|_|jtjj ddS) Nrdrrrorrgrz 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 5 - 3 0 1 2 ) r$rmrrrrrgrrMrq)rLr:r:r;rs!!z,OrderedInsertionDelayedUpdatesTestCase.setUpcCs|jdftjjP|jd!tjjddd|jWdQX|jtjjdWdQXWdQX|jtjjddS)Nrrrdddroz 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 6 1 1 1 6 7 4 - 2 0 1 2 5 - 3 0 1 2 z 1 - 1 0 1 8 2 1 1 1 2 3 6 1 1 1 4 5 3 1 1 1 6 7 4 - 2 0 1 2 5 - 3 0 1 2 )rr$rmrrrrMrq)rLr:r:r;rs"z8OrderedInsertionDelayedUpdatesTestCase.test_insert_childcCs|jd]tjjG|jdtjjddWdQX|jtjjdWdQXWdQX|jtjjddS)Nrrtrdeez 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 6 - 2 0 1 2 4 - 3 0 1 2 5 - 4 0 1 2 z 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 6 - 2 0 1 2 4 - 3 0 1 2 5 - 4 0 1 2 )rr$rmrrrMrq)rLr:r:r;rsz7OrderedInsertionDelayedUpdatesTestCase.test_insert_rootcCs|jdctjjM|jdd|j_|jjWdQX|jtjjdWdQXWdQX|jtjjddS)Nrwrvzbefore dz 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 5 - 3 0 1 2 z 1 - 1 0 1 6 3 1 1 1 2 3 2 1 1 1 4 5 4 - 2 0 1 2 5 - 3 0 1 2 ) rr$rmrrrdrprMrq)rLr:r:r;rs z?OrderedInsertionDelayedUpdatesTestCase.test_move_node_same_treecCs|jdrtjj\|jd-|j|j_d|j_|jjWdQX|j tjj dWdQXWdQX|j tjj ddS)Nrrrz 1 - 1 0 1 6 2 1 1 1 2 3 4 1 1 1 2 3 3 1 1 1 4 5 5 - 2 0 1 2 z 1 - 1 0 1 8 2 1 1 1 2 3 4 1 1 1 4 5 3 1 1 1 6 7 5 - 2 0 1 2 ) rr$rmrrrgrordrprMrq)rLr:r:r;rs zDOrderedInsertionDelayedUpdatesTestCase.test_move_node_different_treecCs|jdctjjM|jdd|j_|jjWdQX|jtjjdWdQXWdQX|jtjjddS)Nrrtz 1 - 1 0 1 6 2 1 1 1 2 3 3 - 2 0 1 2 4 - 3 0 1 2 5 - 4 0 1 2 z 1 - 1 0 1 6 2 1 1 1 2 3 3 - 2 0 1 2 4 - 3 0 1 2 5 - 4 0 1 2 ) rr$rmrrrorprMrq)rLr:r:r;r2s z=OrderedInsertionDelayedUpdatesTestCase.test_move_node_to_rootcCs|jdftjjP|jd!|j|j_|jjWdQX|jtjj dWdQXWdQX|jtjj ddS)Nrrz 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 3 1 2 5 6 5 - 2 0 1 2 z 1 - 1 0 1 8 2 1 1 1 2 3 3 1 1 1 4 7 4 3 1 2 5 6 5 - 2 0 1 2 ) rr$rmrrrgrorprMrq)rLr:r:r;rMsz>OrderedInsertionDelayedUpdatesTestCase.test_move_root_to_childN) rNrOrPrrrrrrrr:r:r:r;rs       rc@seZdZdddgZddZddZdd Zd d Zd d ZdddZ ddZ ddZ ddZ ddZ ddZdS) ManagerTestszcategories.jsonz genres.jsonz persons.jsoncCsi}x~tjD]p}t|ts.qn|j}t||krs|jd|j|t|jfn||t|tdD]}|j}|dkr>Pq>q>W|jd|q WdS)Nz=Detected infinite recursion in %s._tree_manager._base_manager)rrrrrrange _base_managerre)rLrrir:r:r;$test_base_manager_infinite_recursions   z1ManagerTests.test_base_manager_infinite_recursioncCsV|jtjt|jtjjt|jtjt|jtjjtdS)N)assertIsInstancer!rr*rrrm)rLr:r:r;test_proxy_custom_managersz&ManagerTests.test_proxy_custom_managerc Csddd}tjjtddtddB}|j||ddddg|j||d d ddddddgtjjd d}|j||d d dddddddg |j||d d d d dddddddddg dS)NFcSs=|jjj|d|}t|jdddjdS)N include_selfrdflatT)rrmget_queryset_descendantsr> values_listr+)qsrdescr:r:r;get_desc_namess zBManagerTests.test_get_queryset_descendants..get_desc_namesrdz Nintendo Wiiz PlayStation 3ZGameszHardware & AccessoriesrTroz 2D Platformerz 3D Platformerz 4D Platformerz Action RPGzHorizontal Scrolling ShootemupZ PlatformerZ Shootemupz Tactical RPGzVertical Scrolling ShootemupActionzRole-playing Game)rrmfilterrrKr)rLrrr:r:r;test_get_queryset_descendantss,(         z*ManagerTests.test_get_queryset_descendantsFcCs=|jjj|d|}t|jdddjdS)NrrdrT)rrmget_queryset_ancestorsr>rr+)rLrrZancr:r:r;_get_anc_namess zManagerTests._get_anc_namescCstjjtddtddB}|j|j|dg|j|j|dddddgtjjdd}|j|j|g|j|j|dddd gdS) Nrdz Nintendo Wiiz PlayStation 3zPC & Video GamesrTrorzRole-playing Game)rrmrrrKrr)rLrr:r:r;test_get_queryset_ancestorss(  z(ManagerTests.test_get_queryset_ancestorscCsStjj}|j|j|ddttjjdddjddS)NrTrdr)rrmrqrKrr>rr+)rLrr:r:r;*test_get_queryset_ancestors_regression_379sz7ManagerTests.test_get_queryset_ancestors_regression_379cCs|jttjjt|jttjjdjt|jttjjd|jttjjdjjd|j t tjjt tjj dS)zL Test that a custom manager also provides custom querysets. r custom_methodN) rrJr&rmrqr' get_childrenr=nonerKtype root_nodes)rLr:r:r;test_custom_querysetss)/z"ManagerTests.test_custom_querysetscCs9tjtjtd|jtjjtdS)zm Test that a manager created from a custom queryset works. Regression test for #378. my_managerN)r from_querysetr'contribute_to_classrrr get_queryset)rLr:r:r;!test_manager_from_custom_querysetsz.ManagerTests.test_manager_from_custom_querysetc CsQ|jd<tjjtjjdd}|jt|dWdQXdS)z Test the number of queries to access descendants is not O(n). At the moment it is O(1)+1. Ideally we should aim for O(1). rrTrN)rrrmrrqrKlen)rLrr:r:r;,test_num_queries_on_get_queryset_descendantss z9ManagerTests.test_num_queries_on_get_queryset_descendantsN)rNrOrPrrrrrrrrrrrrr:r:r:r;ris     %    rc@s7eZdZdZdgZddZddZdS)CacheTreeChildrenTestCasez@ Tests for the ``cache_tree_children`` template filter. zcategories.jsonc Cs|jdmttjj}|d}|jd}|jd}|j||j|j||jjWdQXdS)zW Ensures that each node's parent is cached by ``cache_tree_children``. rvrN)rrrrmrqrrKro)rLrootsZgameswiiZ wii_gamesr:r:r;'test_cache_tree_children_caches_parentss zACacheTreeChildrenTestCase.test_cache_tree_children_caches_parentscCs|jd7|jt!tttjjdWdQXWdQX|jdtttjjWdQXttjjdddttjj ddjddS)z Ensures that ``cache_tree_children`` fails with a ``ValueError`` when passed a list which is not in tree order. rvz-idNr,r-rd) rr ValueErrorrr>rrmr+rqr)rLr:r:r;.test_cache_tree_children_with_invalid_orderings(zHCacheTreeChildrenTestCase.test_cache_tree_children_with_invalid_orderingN)rNrOrPrrrrr:r:r:r;rs   rc@sdeZdZdZdgZejdddZddZdd Z d d Z d d Z dS)RecurseTreeTestCasez8 Tests for the ``recursetree`` template filter. zcategories.jsonz (?m)^[\s]+rCa {% load mptt_tags %}
    {% recursetree nodes %}
  • {{ node.name }} {% if not node.is_leaf_node %}
      {{ children }}
    {% endif %}
  • {% endrecursetree %}
cCsTt|jjtitjjddd6jdd}|j|ddS)Nr.rrAr/rCz,
  • Hardware & Accessories
) r templaterenderr rrmrreplacerK)rLhtmlr:r:r;test_leaf_html>s z"RecurseTreeTestCase.test_leaf_htmlcCsftjjddjdd}t|jjti|d6jdd}|j |ddS) Nr.r{rTrAr/rCzj
  • PlayStation 3
    • Games
    • Hardware & Accessories
) rrmrnget_descendantsr rrr rrK)rLrrr:r:r;test_nonleaf_htmlDs !  z%RecurseTreeTestCase.test_nonleaf_htmlcCs|jttddS)Nz9{% load mptt_tags %}{% recursetree %}{% endrecursetree %})rr r )rLr:r:r;test_parsing_failNsz%RecurseTreeTestCase.test_parsing_failc CsOtd}|jd.tjj}|jti|d6WdQXdS)Na! {% load mptt_tags %} {% recursetree nodes %} {{ node.get_ancestors|join:" > " }} {{ node.name }} {% if not node.is_leaf_node %} {{ children }} {% endif %} {% endrecursetree %} rvrA)r rrrmrqrr )rLrrr:r:r;test_cached_ancestorsTs  z)RecurseTreeTestCase.test_cached_ancestorsN) rNrOrPrrrbrErrr r r r:r:r:r;r)s      rc@sOeZdZdgZejdddZejdddZddZdS) TreeInfoTestCasez genres.jsonz (?m)^[\s]+rCa {% load mptt_tags %} {% for node, structure in nodes|tree_info %} {% if structure.new_level %}
  • {% else %}
  • {% endif %} {{ node.pk }} {% for level in structure.closed_levels %}
{% endfor %} {% endfor %}a {% load mptt_tags %} {% for node, structure in nodes|tree_info:"ancestors" %} {% if structure.new_level %}
  • {% else %}
  • {% endif %} {{ node.pk }} {% for ancestor in structure.ancestors %} {% if forloop.first %}A:{% endif %} {{ ancestor }}{% if not forloop.last %},{% endif %} {% endfor %} {% for level in structure.closed_levels %}
{% endfor %} {% endfor %}cCs,t|jjtitjjd6jdd}|j|dt|jjtitjj iddtj j 6ddtj j 6d6jdd}|j|d t|j jtitjj iddtj j 6ddtj j 6d6jdd}|j|d dS) NrAr/rCz
  • 1
    • 2
      • 3
      • 4
      • 5
    • 6
      • 7
      • 8
  • 9
    • 10
    • 11
rvz%s__gterz%s__ltezw
  • 2
    • 3
    • 4
    • 5
  • 6
    • 7
    • 8
  • 10
  • 11
z
  • 2
    • 3A:Platformer
    • 4A:Platformer
    • 5A:Platformer
  • 6
    • 7A:Shootemup
    • 8A:Shootemup
  • 10
  • 11
) r rrr rrmrqrrKrr?r4template_with_ancestors)rLrr:r:r;test_tree_info_htmlys,  !  ! z$TreeInfoTestCase.test_tree_info_htmlN) rNrOrPrrbrErr rr:r:r:r;r cs     r c@s:eZdZdgZejdddZddZdS)FullTreeTestCasez genres.jsonz (?m)^[\s]+rCz {% load mptt_tags %} {% full_tree_for_model myapp.Genre as tree %} {% for node in tree %}{{ node.pk }},{% endfor %} cCs>t|jjtijdd}|j|ddS)Nr/rCz1,2,3,4,5,6,7,8,9,10,11,)r rrr rrK)rLrr:r:r;test_full_tree_htmls*z$FullTreeTestCase.test_full_tree_htmlN)rNrOrPrrbrErrr:r:r:r;rs    rc@sLeZdZdgZejdddZddddZdd Zd S) DrilldownTreeTestCasez genres.jsonz (?m)^[\s]+rCab {% load mptt_tags %} {% drilldown_tree_for_node node as tree count myapp.Game.genre in game_count %} {% for n in tree %} {% ifequal n node %}[{% endifequal %} {{ n.pk }}:{{ n.game_count }} {% ifequal n node %}]{% endifequal %}{% if not forloop.last %},{% endif %} {% endfor %} FcCsz|j}|r$|jdd}n|r?|jdd}nt|jtitjjd|d6jddS) Nz count z cumulative count z Game.genrezGame.genres_m2mr.noder/rC)rrr rr rrmrn)rLr. cumulativem2mrr:r:r;render_for_nodes z%DrilldownTreeTestCase.render_for_nodecCsbxcttjjD]L\}}x=t|D]/}|jjdd|}|jj|q/WqW|j |j dd|j |j dd|j |j dddd |j |j dddd|j |j dd dd|j |j dd dd|j |j dddd dd |j |j dddd dddS) NrdzGame %srvz [1:],2:1,6:5rz1:,[2:],3:2,4:3,5:4rTz[1:],2:10,6:18r) enumeraterrmrqrgame_setr games_m2maddrKr)rLidxgenrergamer:r:r;test_drilldown_htmls8"  z)DrilldownTreeTestCase.test_drilldown_htmlN) rNrOrPrrbrErrrr:r:r:r;rs     rc@seZdZddZdS)TestAutoNowDateFieldModelcCst}|jdS)N)r%rp)rLrr:r:r;#test_save_auto_now_date_field_models z=TestAutoNowDateFieldModel.test_save_auto_now_date_field_modelN)rNrOrPrr:r:r:r;rs rc@seZdZddZdS)RegisteredRemoteModelcCs#tjjdd}|jdS)Nrdzgroup 1)rrmrrp)rLZg1r:r:r;test_save_registered_modelsz0RegisteredRemoteModel.test_save_registered_modelN)rNrOrPr!r:r:r:r;r s r c@s=eZdZdgZddZddZddZdS) TestFormszcategories.jsoncCstjjdd}ttdtdd }|j|d||i|jd6|jjdj d6d|}|j |j |j dd |j |i|jd6|jjdj d6d|}tjj|jd_|j |j |j d d |j dS) Nrdz Nintendo WiiformfieldsroinstancerzSelect a valid choicez%szInvalid parent)rdzparent)rrmrnrrrrdchildrenrqr.ris_validassertInerrorsr$queryset)rLrZ CategoryFormr#r:r:r;test_adminform_instantiations.     z&TestForms.test_adminform_instantiationcCsvttdd}|}|jt|jdt|jt|jdt|jt|jdtdS)Nexcluderkfkoner)zid)rr)rrJr$rr)rLZReferencingModelFormr#r:r:r;test_field_typess          zTestForms.test_field_typescCsltjjdd}t|idd6dd6}|j|j|j|jtjjddS)Nr.r5targetz first-childpositiona 1 - 1 0 1 20 5 1 1 1 2 13 2 5 1 2 3 8 3 2 1 3 4 5 4 2 1 3 6 7 6 5 1 2 9 10 7 5 1 2 11 12 8 1 1 1 14 19 9 8 1 2 15 16 10 8 1 2 17 18 ) rrmrnrrr'rprMrq)rLrr#r:r:r;test_movenodeform*s    zTestForms.test_movenodeformN)rNrOrPrr+r/r3r:r:r:r;r"s   r"c@seZdZddZdS)TestAltersDatacCst}tdjti|d6}|j|d|j|jd|j|j|jdtdjti|d6}|j|tjj d|jdS)Nz{{ node.save }}rrCz{{ node.delete }}r.) r(r rr rKr.rprrmrn)rLroutputr:r:r;test_alters_dataBs  zTestAltersData.test_alters_dataN)rNrOrPr6r:r:r:r;r4As r4c@s%eZdZdgZddZdS) TestDebugInfozcategories.jsonc Csctrtjntj}|,}ttjjd||j}WdQX|j d|dS)Nfilez 1,0,,1,1,20) rioStringIOBytesIOrrrmrqgetvaluer()rLZ stream_typeoutr5r:r:r;test_debug_infoWs  zTestDebugInfo.test_debug_infoN)rNrOrPrr>r:r:r:r;r7Ts  r7c@s%eZdZdgZddZdS) AdminBatchzcategories.jsoncCsBtjjddd}|jjd|jdd|jjd}|j|ddtj }|j |j d j j jdd |j|jgid d 6d ddgd6}|jjd|}|j|ddd|d<|jjd|}|j|d|j tjjd|jtjjddS)Nadminztest@example.compusernamepasswordz/admin/myapp/category/zname="_selected_action"rclrdelete_selectedrxr089_selected_actionzvalue="Yes, I'm sure"rvyespostrzi 1 - 1 0 1 8 2 1 1 1 2 7 3 2 1 2 3 4 4 2 1 2 5 6 )rrmcreate_superuserclientloginrBrnassertContainsrr?rKcontext result_listqueryr+r3r5rJassertRedirectscountrMrq)rLuserresponse mptt_optsdatar:r:r;test_changelistcs0  zAdminBatch.test_changelistN)rNrOrPrrXr:r:r:r;r?`s  r?c@seZdZddZdS) TestUnsavedc CsSxLddddddddd g D])}t|td |tt|q"WdS) N get_ancestors get_familyrr get_leafnodesrget_previous_siblingget_root get_siblingsz)Cannot call %s on unsaved Genre instances)rrr1r)rLmethodr:r:r; test_unsaveds zTestUnsaved.test_unsavedN)rNrOrPrar:r:r:r;rYs rYc@s1eZdZdgZddZddZdS) QuerySetTestszcategories.jsoncCs|jddtjjddjddDddtjjddjddD|jddtjjddjdd Dd dtjjddjdd DdS) NcSsg|]}|jqSr:)r.)r7rr:r:r;r<s z4QuerySetTests.test_get_ancestors..rdz Nintendo WiirFcSsg|]}|jqSr:)r.)r7rr:r:r;r<s cSsg|]}|jqSr:)r.)r7rr:r:r;r<s TcSsg|]}|jqSr:)r.)r7rr:r:r;r<s )rKrrmrnrZr)rLr:r:r;test_get_ancestorss " & " z QuerySetTests.test_get_ancestorscCs|jddtjjddjddDddtjjddjddD|jddtjjddjdd Dd dtjjddjdd DdS) NcSsg|]}|jqSr:)r.)r7rr:r:r;r<s z6QuerySetTests.test_get_descendants..rdz Nintendo WiirFcSsg|]}|jqSr:)r.)r7rr:r:r;r<s cSsg|]}|jqSr:)r.)r7rr:r:r;r<s TcSsg|]}|jqSr:)r.)r7rr:r:r;r<s )rKrrmrnrr)rLr:r:r;test_get_descendantss " & " z"QuerySetTests.test_get_descendantsN)rNrOrPrrcrdr:r:r:r;rbs   rbc@s(eZdZddgZddZdS)TreeManagerTestCasezcategories.jsonz items.jsoncCstjjddjd}xEtjj|tddddD]"}|j|j|jj q@WxEtjj|tddddD]"}|j|j|jj qWdS) NrdzXbox 360rk category_fk item_countrF category_pk) rrmrr+add_related_countrrKrg items_by_pkrS)rLr*rr:r:r;-test_add_related_count_with_fk_to_natural_keys   zATreeManagerTestCase.test_add_related_count_with_fk_to_natural_keyN)rNrOrPrrkr:r:r:r;res  rec@s@eZdZddZddZddZddZd S) TestOrderedInsertionBFScCs^tjjdd}tjjddd|tjjdd|jtjjddS)NrdRockz Led Zeppelinro ClassicalzQ 3 - 1 0 1 2 1 - 2 0 1 4 2 1 2 1 2 3 )r$rmrrMrq)rLrockr:r:r;,test_insert_ordered_DFS_backwards_root_nodess zDTestOrderedInsertionBFS.test_insert_ordered_DFS_backwards_root_nodescCstjjdd}|jtjjdtjjdd|jtjjdtjjddd||jtjjddS) Nrdrmz! 1 - 1 0 1 2 rnz9 2 - 1 0 1 2 1 - 2 0 1 2 z Led ZeppelinrozQ 2 - 1 0 1 2 1 - 2 0 1 4 3 1 2 1 2 3 )r$rmrrMrq)rLror:r:r;,test_insert_ordered_BFS_backwards_root_nodesszDTestOrderedInsertionBFS.test_insert_ordered_BFS_backwards_root_nodescCstjjdd}tjjddd|}tjjddd|tjjddd||jtjjddS)Nrdmusicrmroz Led Zeppelinrnzi 1 - 1 0 1 8 4 1 1 1 2 3 2 1 1 1 4 7 3 2 1 2 5 6 )r$rmrrMrq)rLrrror:r:r;/test_insert_ordered_DFS_backwards_nonroot_nodess zGTestOrderedInsertionBFS.test_insert_ordered_DFS_backwards_nonroot_nodescCstjjdd}tjjddd|}tjjddd||jtjjdtjjddd||jtjjddS) NrdrrrmrornzQ 1 - 1 0 1 6 3 1 1 1 2 3 2 1 1 1 4 5 z Led Zeppelinzi 1 - 1 0 1 8 3 1 1 1 2 3 2 1 1 1 4 7 4 2 1 2 5 6 )r$rmrrMrq)rLrrror:r:r;/test_insert_ordered_BFS_backwards_nonroot_nodesszGTestOrderedInsertionBFS.test_insert_ordered_BFS_backwards_nonroot_nodesN)rNrOrPrprqrsrtr:r:r:r;rls   rlc@s+eZdZdZdgZddZdS)CacheChildrenTestCasezr Tests that the queryset function `get_cached_trees` results in a minimum number of database queries. z genres.jsoncCs|jdtjjj}WdQX|jt|d|jdtxl|D]d}|j|txK|jD]=}|j|tx$|jD]}|j|tqWq{WqXWWdQXdS)z3 Test a query with two root nodes. rvNrr) rrrmrqget_cached_treesrKrrr)rLrrrZchild2r:r:r;test_genre_iter-s z%CacheChildrenTestCase.test_genre_iterN)rNrOrPrrrwr:r:r:r;ru&s  ruz+Signals tests require mock_django installedc@sUeZdZdgZddZddZddZdd Zd d Zd S) Signalszcategories.jsoncCs=t|_tjjdd|_tjjdd|_dS)Nr.rr{)rsignalrrmrnrps3)rLr:r:r;rEs z Signals.setUpc CsNt|jdt3}d|j_|jj|j|jdWdQXdS)NsenderZWoor)rryrrrdrprK call_count)rLreceiverr:r:r;8test_signal_should_not_be_sent_when_parent_hasnt_changedJs  z@Signals.test_signal_should_not_be_sent_when_parent_hasnt_changedc CsHt|jdt-}tjjdd|j|jdWdQXdS)Nr{rdzDescriptive namer)rryrrmrrKr|)rLr}r:r:r;1test_signal_should_not_be_sent_when_model_createdQsz9Signals.test_signal_should_not_be_sent_when_model_createdcCsft|jdtK}|jj|j|jd|jd|jd|jdtddWdQXdS)Nr{r%ryr1r2z first-child)rryrrrrzassert_called_once_with)rLr}r:r:r;-test_move_by_using_move_to_should_send_signalWs    z5Signals.test_move_by_using_move_to_should_send_signalcCsit|jdtN}|j|j_|jj|jd|jd|jd|jdtWdQXdS)zposition is not set when sent from save(). I assume it would be the default(first-child) but didn't feel comfortable setting it. r{r%ryr1N)rryrrzrrorpr)rLr}r:r:r;/test_move_by_changing_parent_should_send_signalcs     z7Signals.test_move_by_changing_parent_should_send_signalN) rNrOrPrrr~rrrr:r:r:r;rxAs      rxc@sFeZdZdZddZddZddZdd Zd S) DeferredAttributeTestsz, Regression tests for #176 and #424 cCstjjdddS)Nrdr)r$rmr)rLr:r:r;rxszDeferredAttributeTests.setUpcCs^tjjd}|jdt|}WdQX|jd|j|dWdQXdS)Nrdrvrz) 1 - 1 0 1 2 )r$rmdeferrr>rM)rLrrAr:r:r; test_deferred_order_insertion_by{s  z7DeferredAttributeTests.test_deferred_order_insertion_bycCstjjdj}|j|jdt|jd |jWdQX|jd|j WdQX|j|jdddS)Nrdrvrtr) r$rmrrnrK_mptt_cached_fieldsrrrdrp)rLobjr:r:r;%test_deferred_cached_field_undeferreds zsz      "  "^   ;jd(:;:L -(J 2django-mptt-0.8.0/tests/myapp/__pycache__/urls.cpython-34.pyc0000644000076500000240000000053512635112365025343 0ustar cdestigterstaff00000000000000 8urV@sRddlmZmZddlmZejedeejjgZdS))includeurl)adminz^admin/N) django.conf.urlsrrZdjango.contribr autodiscoversiteurls urlpatternsr r :/Users/cdestigter/checkout/django-mptt/tests/myapp/urls.pys django-mptt-0.8.0/tests/myapp/admin.py0000644000076500000240000000031212515002575021153 0ustar cdestigterstaff00000000000000from django.contrib import admin from mptt.admin import MPTTModelAdmin from myapp.models import Category class CategoryAdmin(MPTTModelAdmin): pass admin.site.register(Category, CategoryAdmin) django-mptt-0.8.0/tests/myapp/admin.pyc0000644000076500000240000000117012635626177021336 0ustar cdestigterstaff00000000000000 }4Ucs]ddlmZddlmZddlmZdefdYZejeedS(i(sadmin(sMPTTModelAdmin(sCategorys CategoryAdmincseZRS((t__name__t __module__(((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/admin.pyt CategoryAdminsN( tdjango.contribtadmint mptt.admintMPTTModelAdmint myapp.modelstCategoryt CategoryAdmintsitetregister(((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/admin.pytsdjango-mptt-0.8.0/tests/myapp/doctests.txt0000644000076500000240000013106312635466021022115 0ustar cdestigterstaff00000000000000>>> from datetime import date >>> from django.db import connection >>> from mptt.exceptions import InvalidMove >>> from myapp.models import Genre, Insert, MultiOrder, Node, OrderedInsertion, Tree, Person, Student >>> def print_tree_details(nodes): ... opts = nodes[0]._mptt_meta ... print('\n'.join(['%s %s %s %s %s %s' % \ ... (n.pk, getattr(n, '%s_id' % opts.parent_attr) or '-', ... getattr(n, opts.tree_id_attr), getattr(n, opts.level_attr), ... getattr(n, opts.left_attr), getattr(n, opts.right_attr)) \ ... for n in nodes])) >>> def reset_sequence(model): ... # This fixes test failures. For a strange reason, this snippet is ... # only necessary with PY3. Also, do not fail if the table does not ... # exist at all for some reason. ... try: ... connection.cursor().execute( ... 'UPDATE sqlite_sequence SET seq=0 WHERE name=%s', ... [model._meta.db_table]) ... except: ... pass # Creation #################################################################### # creation with a given ID >>> action = Genre.objects.create(pk=1, name='Action') >>> platformer = Genre.objects.create(name='Platformer', parent=action) >>> platformer_2d = Genre.objects.create(name='2D Platformer', parent=platformer) >>> platformer = Genre.objects.get(pk=platformer.pk) >>> platformer_3d = Genre.objects.create(name='3D Platformer', parent=platformer) >>> platformer = Genre.objects.get(pk=platformer.pk) >>> platformer_4d = Genre.objects.create(name='4D Platformer', parent=platformer) >>> rpg = Genre.objects.create(name='Role-playing Game') >>> arpg = Genre.objects.create(name='Action RPG', parent=rpg) >>> rpg = Genre.objects.get(pk=rpg.pk) >>> trpg = Genre.objects.create(name='Tactical RPG', parent=rpg) >>> print_tree_details(Genre.objects.all()) 1 - 1 0 1 10 2 1 1 1 2 9 3 2 1 2 3 4 4 2 1 2 5 6 5 2 1 2 7 8 6 - 2 0 1 6 7 6 2 1 2 3 8 6 2 1 4 5 # Utilities ################################################################### >>> from mptt.utils import previous_current_next, tree_item_iterator, drilldown_tree_for_node >>> for p,c,n in previous_current_next(Genre.objects.all()): ... print((p,c,n)) (None, , ) (, , ) (, , ) (, , ) (, , ) (, , ) (, , ) (, , None) >>> for i,s in tree_item_iterator(Genre.objects.all()): ... print((i, s['new_level'], list(s['closed_levels']))) (, True, []) (, True, []) (, True, []) (, False, []) (, False, [2, 1]) (, False, []) (, True, []) (, False, [1, 0]) >>> for i,s in tree_item_iterator(rpg.get_descendants()): ... print((i, s['new_level'], list(s['closed_levels']))) (, True, []) (, False, [1]) >>> for i,s in tree_item_iterator(trpg.get_descendants()): ... print((i, s['new_level'], list(s['closed_levels']))) >>> for i,s in tree_item_iterator(Genre.objects.all(), ancestors=True): ... print((i, s['new_level'], s['ancestors'], list(s['closed_levels']))) (, True, [], []) (, True, [u'Action'], []) (, True, [u'Action', u'Platformer'], []) (, False, [u'Action', u'Platformer'], []) (, False, [u'Action', u'Platformer'], [2, 1]) (, False, [], []) (, True, [u'Role-playing Game'], []) (, False, [u'Role-playing Game'], [1, 0]) >>> action = Genre.objects.get(pk=action.pk) >>> [item.name for item in drilldown_tree_for_node(action)] [u'Action', u'Platformer'] >>> platformer = Genre.objects.get(pk=platformer.pk) >>> [item.name for item in drilldown_tree_for_node(platformer)] [u'Action', u'Platformer', u'2D Platformer', u'3D Platformer', u'4D Platformer'] >>> platformer_3d = Genre.objects.get(pk=platformer_3d.pk) >>> [item.name for item in drilldown_tree_for_node(platformer_3d)] [u'Action', u'Platformer', u'3D Platformer'] # Forms ####################################################################### >>> from mptt.forms import TreeNodeChoiceField, MoveNodeForm >>> f = TreeNodeChoiceField(queryset=Genre.objects.all()) >>> print(f.widget.render("test", None)) >>> f = TreeNodeChoiceField(queryset=Genre.objects.all(), required=False) >>> print(f.widget.render("test", None)) >>> f = TreeNodeChoiceField(queryset=Genre.objects.all(), empty_label=u'None of the below') >>> print(f.widget.render("test", None)) >>> f = TreeNodeChoiceField(queryset=Genre.objects.all(), required=False, empty_label=u'None of the below') >>> print(f.widget.render("test", None)) >>> f = TreeNodeChoiceField(queryset=Genre.objects.all(), level_indicator=u'+--') >>> print(f.widget.render("test", None)) >>> form = MoveNodeForm(Genre.objects.get(pk=7)) >>> print(form['target']) >>> form = MoveNodeForm(Genre.objects.get(pk=7), level_indicator=u'+--', target_select_size=5) >>> print(form['target']) >>> form = MoveNodeForm(Genre.objects.get(pk=7), position_choices=(('left', 'left'),)) >>> print(form['position']) # TreeManager Methods ######################################################### # check that tree manager is the explicitly defined tree manager for Person >>> Person._tree_manager == Person.objects True # managers of non-abstract bases don't get inherited, so: >>> Student._tree_manager == Student.objects False >>> Student._tree_manager == Person._tree_manager False >>> Student._tree_manager.model >>> Student._tree_manager.tree_model >>> Person._tree_manager.model >>> Person._tree_manager.tree_model >>> Genre.objects.root_node(action.tree_id) >>> Genre.objects.root_node(rpg.tree_id) >>> Genre.objects.root_node(3) Traceback (most recent call last): ... DoesNotExist: Genre matching query does not exist. >>> [g.name for g in Genre.objects.root_nodes()] [u'Action', u'Role-playing Game'] >>> [g.parent for g in Genre.objects.only('parent')] [None, , , , , None, , ] # Model Instance Methods ###################################################### >>> action = Genre.objects.get(pk=action.pk) >>> [g.name for g in action.get_ancestors()] [] >>> [g.name for g in action.get_ancestors(ascending=True)] [] >>> [g.name for g in action.get_children()] [u'Platformer'] >>> [g.name for g in action.get_descendants()] [u'Platformer', u'2D Platformer', u'3D Platformer', u'4D Platformer'] >>> [g.name for g in action.get_descendants(include_self=True)] [u'Action', u'Platformer', u'2D Platformer', u'3D Platformer', u'4D Platformer'] >>> [g.name for g in action.get_leafnodes()] [u'2D Platformer', u'3D Platformer', u'4D Platformer'] >>> [g.name for g in action.get_leafnodes(include_self=False)] [u'2D Platformer', u'3D Platformer', u'4D Platformer'] >>> [g.name for g in action.get_leafnodes(include_self=True)] [u'2D Platformer', u'3D Platformer', u'4D Platformer'] >>> [g.name for g in platformer.get_family()] [u'Action', u'Platformer', u'2D Platformer', u'3D Platformer', u'4D Platformer'] >>> action.get_descendant_count() 4 >>> action.get_previous_sibling() >>> action.get_next_sibling() >>> action.get_root() >>> [g.name for g in action.get_siblings()] [u'Role-playing Game'] >>> [g.name for g in action.get_siblings(include_self=True)] [u'Action', u'Role-playing Game'] >>> action.is_root_node() True >>> action.is_child_node() False >>> action.is_leaf_node() False >>> action.is_descendant_of(action) False >>> action.is_descendant_of(action, include_self=True) True >>> action.is_ancestor_of(action) False >>> action.is_ancestor_of(action, include_self=True) True >>> platformer = Genre.objects.get(pk=platformer.pk) >>> [g.name for g in platformer.get_ancestors()] [u'Action'] >>> [g.name for g in platformer.get_ancestors(ascending=True)] [u'Action'] >>> [g.name for g in platformer.get_children()] [u'2D Platformer', u'3D Platformer', u'4D Platformer'] >>> [g.name for g in platformer.get_descendants()] [u'2D Platformer', u'3D Platformer', u'4D Platformer'] >>> [g.name for g in platformer.get_descendants(include_self=True)] [u'Platformer', u'2D Platformer', u'3D Platformer', u'4D Platformer'] >>> [g.name for g in platformer.get_leafnodes()] [u'2D Platformer', u'3D Platformer', u'4D Platformer'] >>> [g.name for g in platformer.get_leafnodes(include_self=True)] [u'2D Platformer', u'3D Platformer', u'4D Platformer'] >>> platformer.get_descendant_count() 3 >>> platformer.get_previous_sibling() >>> platformer.get_next_sibling() >>> platformer.get_root() >>> [g.name for g in platformer.get_siblings()] [] >>> [g.name for g in platformer.get_siblings(include_self=True)] [u'Platformer'] >>> platformer.is_root_node() False >>> platformer.is_child_node() True >>> platformer.is_leaf_node() False >>> action.is_descendant_of(platformer) False >>> action.is_descendant_of(platformer, include_self=True) False >>> action.is_ancestor_of(platformer) True >>> action.is_ancestor_of(platformer, include_self=True) True >>> platformer_3d = Genre.objects.get(pk=platformer_3d.pk) >>> [g.name for g in platformer_3d.get_ancestors()] [u'Action', u'Platformer'] >>> [g.name for g in platformer_3d.get_ancestors(ascending=True)] [u'Platformer', u'Action'] >>> [g.name for g in platformer_3d.get_children()] [] >>> [g.name for g in platformer_3d.get_descendants()] [] >>> [g.name for g in platformer_3d.get_descendants(include_self=True)] [u'3D Platformer'] >>> [g.name for g in platformer_3d.get_leafnodes()] [] >>> [g.name for g in platformer_3d.get_leafnodes(include_self=True)] [u'3D Platformer'] >>> platformer_3d.get_descendant_count() 0 >>> platformer_3d.get_previous_sibling() >>> platformer_3d.get_next_sibling() >>> platformer_3d.get_root() >>> [g.name for g in platformer_3d.get_siblings()] [u'2D Platformer', u'4D Platformer'] >>> [g.name for g in platformer_3d.get_siblings(include_self=True)] [u'2D Platformer', u'3D Platformer', u'4D Platformer'] >>> platformer_3d.is_root_node() False >>> platformer_3d.is_child_node() True >>> platformer_3d.is_leaf_node() True >>> action.is_descendant_of(platformer_3d) False >>> action.is_descendant_of(platformer_3d, include_self=True) False >>> action.is_ancestor_of(platformer_3d) True >>> action.is_ancestor_of(platformer_3d, include_self=True) True >>> platformer_3d.is_descendant_of(platformer_3d) False >>> platformer_3d.is_descendant_of(platformer_3d, include_self=True) True >>> platformer_3d.is_ancestor_of(platformer_3d) False >>> platformer_3d.is_ancestor_of(platformer_3d, include_self=True) True # The move_to method will be used in other tests to verify that it calls the # TreeManager correctly. ####################### # Intra-Tree Movement # ####################### >>> root = Node.objects.create() >>> c_1 = Node.objects.create(parent=root) >>> c_1_1 = Node.objects.create(parent=c_1) >>> c_1 = Node.objects.get(pk=c_1.pk) >>> c_1_2 = Node.objects.create(parent=c_1) >>> root = Node.objects.get(pk=root.pk) >>> c_2 = Node.objects.create(parent=root) >>> c_2_1 = Node.objects.create(parent=c_2) >>> c_2 = Node.objects.get(pk=c_2.pk) >>> c_2_2 = Node.objects.create(parent=c_2) >>> print_tree_details(Node.objects.all()) 1 - 1 0 1 14 2 1 1 1 2 7 3 2 1 2 3 4 4 2 1 2 5 6 5 1 1 1 8 13 6 5 1 2 9 10 7 5 1 2 11 12 # Validate exceptions are raised appropriately >>> root = Node.objects.get(pk=root.pk) >>> Node.objects.move_node(root, root, position='first-child') Traceback (most recent call last): ... InvalidMove: A node may not be made a child of itself. >>> c_1 = Node.objects.get(pk=c_1.pk) >>> c_1_1 = Node.objects.get(pk=c_1_1.pk) >>> Node.objects.move_node(c_1, c_1_1, position='last-child') Traceback (most recent call last): ... InvalidMove: A node may not be made a child of any of its descendants. >>> Node.objects.move_node(root, root, position='right') Traceback (most recent call last): ... InvalidMove: A node may not be made a sibling of itself. >>> c_2 = Node.objects.get(pk=c_2.pk) >>> Node.objects.move_node(c_1, c_1_1, position='left') Traceback (most recent call last): ... InvalidMove: A node may not be made a sibling of any of its descendants. >>> Node.objects.move_node(c_1, c_2, position='cheese') Traceback (most recent call last): ... ValueError: An invalid position was given: cheese. # Move up the tree using first-child >>> c_2_2 = Node.objects.get(pk=c_2_2.pk) >>> c_1 = Node.objects.get(pk=c_1.pk) >>> Node.objects.move_node(c_2_2, c_1, 'first-child') >>> print_tree_details([c_2_2]) 7 2 1 2 3 4 >>> print_tree_details(Node.objects.all()) 1 - 1 0 1 14 2 1 1 1 2 9 7 2 1 2 3 4 3 2 1 2 5 6 4 2 1 2 7 8 5 1 1 1 10 13 6 5 1 2 11 12 # Undo the move using right >>> c_2_1 = Node.objects.get(pk=c_2_1.pk) >>> c_2_2.move_to(c_2_1, 'right') >>> print_tree_details([c_2_2]) 7 5 1 2 11 12 >>> print_tree_details(Node.objects.all()) 1 - 1 0 1 14 2 1 1 1 2 7 3 2 1 2 3 4 4 2 1 2 5 6 5 1 1 1 8 13 6 5 1 2 9 10 7 5 1 2 11 12 # Move up the tree with descendants using first-child >>> c_2 = Node.objects.get(pk=c_2.pk) >>> c_1 = Node.objects.get(pk=c_1.pk) >>> Node.objects.move_node(c_2, c_1, 'first-child') >>> print_tree_details([c_2]) 5 2 1 2 3 8 >>> print_tree_details(Node.objects.all()) 1 - 1 0 1 14 2 1 1 1 2 13 5 2 1 2 3 8 6 5 1 3 4 5 7 5 1 3 6 7 3 2 1 2 9 10 4 2 1 2 11 12 # Undo the move using right >>> c_1 = Node.objects.get(pk=c_1.pk) >>> Node.objects.move_node(c_2, c_1, 'right') >>> print_tree_details([c_2]) 5 1 1 1 8 13 >>> print_tree_details(Node.objects.all()) 1 - 1 0 1 14 2 1 1 1 2 7 3 2 1 2 3 4 4 2 1 2 5 6 5 1 1 1 8 13 6 5 1 2 9 10 7 5 1 2 11 12 COVERAGE | U1 | U> | D1 | D> ------------+----+----+----+---- first-child | Y | Y | | last-child | | | | left | | | | right | | | Y | Y # Move down the tree using first-child >>> c_1_2 = Node.objects.get(pk=c_1_2.pk) >>> c_2 = Node.objects.get(pk=c_2.pk) >>> Node.objects.move_node(c_1_2, c_2, 'first-child') >>> print_tree_details([c_1_2]) 4 5 1 2 7 8 >>> print_tree_details(Node.objects.all()) 1 - 1 0 1 14 2 1 1 1 2 5 3 2 1 2 3 4 5 1 1 1 6 13 4 5 1 2 7 8 6 5 1 2 9 10 7 5 1 2 11 12 # Undo the move using last-child >>> c_1 = Node.objects.get(pk=c_1.pk) >>> Node.objects.move_node(c_1_2, c_1, 'last-child') >>> print_tree_details([c_1_2]) 4 2 1 2 5 6 >>> print_tree_details(Node.objects.all()) 1 - 1 0 1 14 2 1 1 1 2 7 3 2 1 2 3 4 4 2 1 2 5 6 5 1 1 1 8 13 6 5 1 2 9 10 7 5 1 2 11 12 # Move down the tree with descendants using first-child >>> c_1 = Node.objects.get(pk=c_1.pk) >>> c_2 = Node.objects.get(pk=c_2.pk) >>> Node.objects.move_node(c_1, c_2, 'first-child') >>> print_tree_details([c_1]) 2 5 1 2 3 8 >>> print_tree_details(Node.objects.all()) 1 - 1 0 1 14 5 1 1 1 2 13 2 5 1 2 3 8 3 2 1 3 4 5 4 2 1 3 6 7 6 5 1 2 9 10 7 5 1 2 11 12 # Undo the move using left >>> c_2 = Node.objects.get(pk=c_2.pk) >>> Node.objects.move_node(c_1, c_2, 'left') >>> print_tree_details([c_1]) 2 1 1 1 2 7 >>> print_tree_details(Node.objects.all()) 1 - 1 0 1 14 2 1 1 1 2 7 3 2 1 2 3 4 4 2 1 2 5 6 5 1 1 1 8 13 6 5 1 2 9 10 7 5 1 2 11 12 COVERAGE | U1 | U> | D1 | D> ------------+----+----+----+---- first-child | Y | Y | Y | Y last-child | Y | | | left | | Y | | right | | | Y | Y # Move up the tree using right >>> c_2_2 = Node.objects.get(pk=c_2_2.pk) >>> c_1_1 = Node.objects.get(pk=c_1_1.pk) >>> Node.objects.move_node(c_2_2, c_1_1, 'right') >>> print_tree_details([c_2_2]) 7 2 1 2 5 6 >>> print_tree_details(Node.objects.all()) 1 - 1 0 1 14 2 1 1 1 2 9 3 2 1 2 3 4 7 2 1 2 5 6 4 2 1 2 7 8 5 1 1 1 10 13 6 5 1 2 11 12 # Undo the move using last-child >>> c_2 = Node.objects.get(pk=c_2.pk) >>> Node.objects.move_node(c_2_2, c_2, 'last-child') >>> print_tree_details([c_2_2]) 7 5 1 2 11 12 >>> print_tree_details(Node.objects.all()) 1 - 1 0 1 14 2 1 1 1 2 7 3 2 1 2 3 4 4 2 1 2 5 6 5 1 1 1 8 13 6 5 1 2 9 10 7 5 1 2 11 12 # Move up the tree with descendants using right >>> c_2 = Node.objects.get(pk=c_2.pk) >>> c_1_1 = Node.objects.get(pk=c_1_1.pk) >>> Node.objects.move_node(c_2, c_1_1, 'right') >>> print_tree_details([c_2]) 5 2 1 2 5 10 >>> print_tree_details(Node.objects.all()) 1 - 1 0 1 14 2 1 1 1 2 13 3 2 1 2 3 4 5 2 1 2 5 10 6 5 1 3 6 7 7 5 1 3 8 9 4 2 1 2 11 12 # Undo the move using last-child >>> root = Node.objects.get(pk=root.pk) >>> Node.objects.move_node(c_2, root, 'last-child') >>> print_tree_details([c_2]) 5 1 1 1 8 13 >>> print_tree_details(Node.objects.all()) 1 - 1 0 1 14 2 1 1 1 2 7 3 2 1 2 3 4 4 2 1 2 5 6 5 1 1 1 8 13 6 5 1 2 9 10 7 5 1 2 11 12 COVERAGE | U1 | U> | D1 | D> ------------+----+----+----+---- first-child | Y | Y | Y | Y last-child | Y | | Y | Y left | | Y | | right | Y | Y | Y | Y # Move down the tree with descendants using left >>> c_1 = Node.objects.get(pk=c_1.pk) >>> c_2_2 = Node.objects.get(pk=c_2_2.pk) >>> Node.objects.move_node(c_1, c_2_2, 'left') >>> print_tree_details([c_1]) 2 5 1 2 5 10 >>> print_tree_details(Node.objects.all()) 1 - 1 0 1 14 5 1 1 1 2 13 6 5 1 2 3 4 2 5 1 2 5 10 3 2 1 3 6 7 4 2 1 3 8 9 7 5 1 2 11 12 # Undo the move using first-child >>> root = Node.objects.get(pk=root.pk) >>> Node.objects.move_node(c_1, root, 'first-child') >>> print_tree_details([c_1]) 2 1 1 1 2 7 >>> print_tree_details(Node.objects.all()) 1 - 1 0 1 14 2 1 1 1 2 7 3 2 1 2 3 4 4 2 1 2 5 6 5 1 1 1 8 13 6 5 1 2 9 10 7 5 1 2 11 12 # Move down the tree using left >>> c_1_1 = Node.objects.get(pk=c_1_1.pk) >>> c_2_2 = Node.objects.get(pk=c_2_2.pk) >>> Node.objects.move_node(c_1_1, c_2_2, 'left') >>> print_tree_details([c_1_1]) 3 5 1 2 9 10 >>> print_tree_details(Node.objects.all()) 1 - 1 0 1 14 2 1 1 1 2 5 4 2 1 2 3 4 5 1 1 1 6 13 6 5 1 2 7 8 3 5 1 2 9 10 7 5 1 2 11 12 # Undo the move using left >>> c_1_2 = Node.objects.get(pk=c_1_2.pk) >>> Node.objects.move_node(c_1_1, c_1_2, 'left') >>> print_tree_details([c_1_1]) 3 2 1 2 3 4 >>> print_tree_details(Node.objects.all()) 1 - 1 0 1 14 2 1 1 1 2 7 3 2 1 2 3 4 4 2 1 2 5 6 5 1 1 1 8 13 6 5 1 2 9 10 7 5 1 2 11 12 COVERAGE | U1 | U> | D1 | D> ------------+----+----+----+---- first-child | Y | Y | Y | Y last-child | Y | Y | Y | Y left | Y | Y | Y | Y right | Y | Y | Y | Y I guess we're covered :) ####################### # Inter-Tree Movement # ####################### >>> new_root = Node.objects.create() >>> print_tree_details(Node.objects.all()) 1 - 1 0 1 14 2 1 1 1 2 7 3 2 1 2 3 4 4 2 1 2 5 6 5 1 1 1 8 13 6 5 1 2 9 10 7 5 1 2 11 12 8 - 2 0 1 2 # Moving child nodes between trees ############################################ # Move using default (last-child) >>> c_2 = Node.objects.get(pk=c_2.pk) >>> c_2.move_to(new_root) >>> print_tree_details([c_2]) 5 8 2 1 2 7 >>> print_tree_details(Node.objects.all()) 1 - 1 0 1 8 2 1 1 1 2 7 3 2 1 2 3 4 4 2 1 2 5 6 8 - 2 0 1 8 5 8 2 1 2 7 6 5 2 2 3 4 7 5 2 2 5 6 # Move using left >>> c_1_1 = Node.objects.get(pk=c_1_1.pk) >>> c_2 = Node.objects.get(pk=c_2.pk) >>> Node.objects.move_node(c_1_1, c_2, position='left') >>> print_tree_details([c_1_1]) 3 8 2 1 2 3 >>> print_tree_details(Node.objects.all()) 1 - 1 0 1 6 2 1 1 1 2 5 4 2 1 2 3 4 8 - 2 0 1 10 3 8 2 1 2 3 5 8 2 1 4 9 6 5 2 2 5 6 7 5 2 2 7 8 # Move using first-child >>> c_1_2 = Node.objects.get(pk=c_1_2.pk) >>> c_2 = Node.objects.get(pk=c_2.pk) >>> Node.objects.move_node(c_1_2, c_2, position='first-child') >>> print_tree_details([c_1_2]) 4 5 2 2 5 6 >>> print_tree_details(Node.objects.all()) 1 - 1 0 1 4 2 1 1 1 2 3 8 - 2 0 1 12 3 8 2 1 2 3 5 8 2 1 4 11 4 5 2 2 5 6 6 5 2 2 7 8 7 5 2 2 9 10 # Move using right >>> c_2 = Node.objects.get(pk=c_2.pk) >>> c_1 = Node.objects.get(pk=c_1.pk) >>> Node.objects.move_node(c_2, c_1, position='right') >>> print_tree_details([c_2]) 5 1 1 1 4 11 >>> print_tree_details(Node.objects.all()) 1 - 1 0 1 12 2 1 1 1 2 3 5 1 1 1 4 11 4 5 1 2 5 6 6 5 1 2 7 8 7 5 1 2 9 10 8 - 2 0 1 4 3 8 2 1 2 3 # Move using last-child >>> c_1_1 = Node.objects.get(pk=c_1_1.pk) >>> Node.objects.move_node(c_1_1, c_2, position='last-child') >>> print_tree_details([c_1_1]) 3 5 1 2 11 12 >>> print_tree_details(Node.objects.all()) 1 - 1 0 1 14 2 1 1 1 2 3 5 1 1 1 4 13 4 5 1 2 5 6 6 5 1 2 7 8 7 5 1 2 9 10 3 5 1 2 11 12 8 - 2 0 1 2 # Moving a root node into another tree as a child node ######################## # Validate exceptions are raised appropriately >>> Node.objects.move_node(root, c_1, position='first-child') Traceback (most recent call last): ... InvalidMove: A node may not be made a child of any of its descendants. >>> Node.objects.move_node(new_root, c_1, position='cheese') Traceback (most recent call last): ... ValueError: An invalid position was given: cheese. >>> new_root = Node.objects.get(pk=new_root.pk) >>> c_2 = Node.objects.get(pk=c_2.pk) >>> new_root.move_to(c_2, position='first-child') >>> print_tree_details([new_root]) 8 5 1 2 5 6 >>> print_tree_details(Node.objects.all()) 1 - 1 0 1 16 2 1 1 1 2 3 5 1 1 1 4 15 8 5 1 2 5 6 4 5 1 2 7 8 6 5 1 2 9 10 7 5 1 2 11 12 3 5 1 2 13 14 >>> new_root = Node.objects.create() >>> root = Node.objects.get(pk=root.pk) >>> Node.objects.move_node(new_root, root, position='last-child') >>> print_tree_details([new_root]) 9 1 1 1 16 17 >>> print_tree_details(Node.objects.all()) 1 - 1 0 1 18 2 1 1 1 2 3 5 1 1 1 4 15 8 5 1 2 5 6 4 5 1 2 7 8 6 5 1 2 9 10 7 5 1 2 11 12 3 5 1 2 13 14 9 1 1 1 16 17 >>> new_root = Node.objects.create() >>> c_2_1 = Node.objects.get(pk=c_2_1.pk) >>> Node.objects.move_node(new_root, c_2_1, position='left') >>> print_tree_details([new_root]) 10 5 1 2 9 10 >>> print_tree_details(Node.objects.all()) 1 - 1 0 1 20 2 1 1 1 2 3 5 1 1 1 4 17 8 5 1 2 5 6 4 5 1 2 7 8 10 5 1 2 9 10 6 5 1 2 11 12 7 5 1 2 13 14 3 5 1 2 15 16 9 1 1 1 18 19 >>> new_root = Node.objects.create() >>> c_1 = Node.objects.get(pk=c_1.pk) >>> Node.objects.move_node(new_root, c_1, position='right') >>> print_tree_details([new_root]) 11 1 1 1 4 5 >>> print_tree_details(Node.objects.all()) 1 - 1 0 1 22 2 1 1 1 2 3 11 1 1 1 4 5 5 1 1 1 6 19 8 5 1 2 7 8 4 5 1 2 9 10 10 5 1 2 11 12 6 5 1 2 13 14 7 5 1 2 15 16 3 5 1 2 17 18 9 1 1 1 20 21 # Making nodes siblings of root nodes ######################################### # Validate exceptions are raised appropriately >>> root = Node.objects.get(pk=root.pk) >>> Node.objects.move_node(root, root, position='left') Traceback (most recent call last): ... InvalidMove: A node may not be made a sibling of itself. >>> Node.objects.move_node(root, root, position='right') Traceback (most recent call last): ... InvalidMove: A node may not be made a sibling of itself. >>> r1 = Tree.objects.create() >>> c1_1 = Tree.objects.create(parent=r1) >>> c1_1_1 = Tree.objects.create(parent=c1_1) >>> r2 = Tree.objects.create() >>> c2_1 = Tree.objects.create(parent=r2) >>> c2_1_1 = Tree.objects.create(parent=c2_1) >>> r3 = Tree.objects.create() >>> c3_1 = Tree.objects.create(parent=r3) >>> c3_1_1 = Tree.objects.create(parent=c3_1) >>> print_tree_details(Tree.objects.all()) 1 - 1 0 1 6 2 1 1 1 2 5 3 2 1 2 3 4 4 - 2 0 1 6 5 4 2 1 2 5 6 5 2 2 3 4 7 - 3 0 1 6 8 7 3 1 2 5 9 8 3 2 3 4 # Target < root node, left sibling >>> r1 = Tree.objects.get(pk=r1.pk) >>> r2 = Tree.objects.get(pk=r2.pk) >>> r2.move_to(r1, 'left') >>> print_tree_details([r2]) 4 - 1 0 1 6 >>> print_tree_details(Tree.objects.all()) 4 - 1 0 1 6 5 4 1 1 2 5 6 5 1 2 3 4 1 - 2 0 1 6 2 1 2 1 2 5 3 2 2 2 3 4 7 - 3 0 1 6 8 7 3 1 2 5 9 8 3 2 3 4 # Target > root node, left sibling >>> r3 = Tree.objects.get(pk=r3.pk) >>> r2.move_to(r3, 'left') >>> print_tree_details([r2]) 4 - 2 0 1 6 >>> print_tree_details(Tree.objects.all()) 1 - 1 0 1 6 2 1 1 1 2 5 3 2 1 2 3 4 4 - 2 0 1 6 5 4 2 1 2 5 6 5 2 2 3 4 7 - 3 0 1 6 8 7 3 1 2 5 9 8 3 2 3 4 # Target < root node, right sibling >>> r1 = Tree.objects.get(pk=r1.pk) >>> r3 = Tree.objects.get(pk=r3.pk) >>> r3.move_to(r1, 'right') >>> print_tree_details([r3]) 7 - 2 0 1 6 >>> print_tree_details(Tree.objects.all()) 1 - 1 0 1 6 2 1 1 1 2 5 3 2 1 2 3 4 7 - 2 0 1 6 8 7 2 1 2 5 9 8 2 2 3 4 4 - 3 0 1 6 5 4 3 1 2 5 6 5 3 2 3 4 # Target > root node, right sibling >>> r1 = Tree.objects.get(pk=r1.pk) >>> r2 = Tree.objects.get(pk=r2.pk) >>> r1.move_to(r2, 'right') >>> print_tree_details([r1]) 1 - 3 0 1 6 >>> print_tree_details(Tree.objects.all()) 7 - 1 0 1 6 8 7 1 1 2 5 9 8 1 2 3 4 4 - 2 0 1 6 5 4 2 1 2 5 6 5 2 2 3 4 1 - 3 0 1 6 2 1 3 1 2 5 3 2 3 2 3 4 # No-op, root left sibling >>> r2 = Tree.objects.get(pk=r2.pk) >>> r2.move_to(r1, 'left') >>> print_tree_details([r2]) 4 - 2 0 1 6 >>> print_tree_details(Tree.objects.all()) 7 - 1 0 1 6 8 7 1 1 2 5 9 8 1 2 3 4 4 - 2 0 1 6 5 4 2 1 2 5 6 5 2 2 3 4 1 - 3 0 1 6 2 1 3 1 2 5 3 2 3 2 3 4 # No-op, root right sibling >>> r1.move_to(r2, 'right') >>> print_tree_details([r1]) 1 - 3 0 1 6 >>> print_tree_details(Tree.objects.all()) 7 - 1 0 1 6 8 7 1 1 2 5 9 8 1 2 3 4 4 - 2 0 1 6 5 4 2 1 2 5 6 5 2 2 3 4 1 - 3 0 1 6 2 1 3 1 2 5 3 2 3 2 3 4 # Child node, left sibling >>> c3_1 = Tree.objects.get(pk=c3_1.pk) >>> c3_1.move_to(r1, 'left') >>> print_tree_details([c3_1]) 8 - 3 0 1 4 >>> print_tree_details(Tree.objects.all()) 7 - 1 0 1 2 4 - 2 0 1 6 5 4 2 1 2 5 6 5 2 2 3 4 8 - 3 0 1 4 9 8 3 1 2 3 1 - 4 0 1 6 2 1 4 1 2 5 3 2 4 2 3 4 # Child node, right sibling >>> r3 = Tree.objects.get(pk=r3.pk) >>> c1_1 = Tree.objects.get(pk=c1_1.pk) >>> c1_1.move_to(r3, 'right') >>> print_tree_details([c1_1]) 2 - 2 0 1 4 >>> print_tree_details(Tree.objects.all()) 7 - 1 0 1 2 2 - 2 0 1 4 3 2 2 1 2 3 4 - 3 0 1 6 5 4 3 1 2 5 6 5 3 2 3 4 8 - 4 0 1 4 9 8 4 1 2 3 1 - 5 0 1 2 # Insertion of positioned nodes ############################################### >>> r1 = Insert.objects.create() >>> r2 = Insert.objects.create() >>> r3 = Insert.objects.create() >>> print_tree_details(Insert.objects.all()) 1 - 1 0 1 2 2 - 2 0 1 2 3 - 3 0 1 2 >>> r2 = Insert.objects.get(pk=r2.pk) >>> c1 = Insert() >>> c1 = Insert.objects.insert_node(c1, r2, save=True) >>> print_tree_details([c1]) 4 2 2 1 2 3 >>> print_tree_details(Insert.objects.all()) 1 - 1 0 1 2 2 - 2 0 1 4 4 2 2 1 2 3 3 - 3 0 1 2 >>> c1.insert_at(r2) Traceback (most recent call last): ... ValueError: Cannot insert a node which has already been saved. # First child >>> r2 = Insert.objects.get(pk=r2.pk) >>> c2 = Insert() >>> c2 = Insert.objects.insert_node(c2, r2, position='first-child', save=True) >>> print_tree_details([c2]) 5 2 2 1 2 3 >>> print_tree_details(Insert.objects.all()) 1 - 1 0 1 2 2 - 2 0 1 6 5 2 2 1 2 3 4 2 2 1 4 5 3 - 3 0 1 2 # Left >>> c1 = Insert.objects.get(pk=c1.pk) >>> c3 = Insert() >>> c3 = Insert.objects.insert_node(c3, c1, position='left', save=True) >>> print_tree_details([c3]) 6 2 2 1 4 5 >>> print_tree_details(Insert.objects.all()) 1 - 1 0 1 2 2 - 2 0 1 8 5 2 2 1 2 3 6 2 2 1 4 5 4 2 2 1 6 7 3 - 3 0 1 2 # Right >>> c4 = Insert() >>> c4 = Insert.objects.insert_node(c4, c3, position='right', save=True) >>> print_tree_details([c4]) 7 2 2 1 6 7 >>> print_tree_details(Insert.objects.all()) 1 - 1 0 1 2 2 - 2 0 1 10 5 2 2 1 2 3 6 2 2 1 4 5 7 2 2 1 6 7 4 2 2 1 8 9 3 - 3 0 1 2 # Last child >>> r2 = Insert.objects.get(pk=r2.pk) >>> c5 = Insert() >>> c5 = Insert.objects.insert_node(c5, r2, position='last-child', save=True) >>> print_tree_details([c5]) 8 2 2 1 10 11 >>> print_tree_details(Insert.objects.all()) 1 - 1 0 1 2 2 - 2 0 1 12 5 2 2 1 2 3 6 2 2 1 4 5 7 2 2 1 6 7 4 2 2 1 8 9 8 2 2 1 10 11 3 - 3 0 1 2 # Left sibling of root >>> r2 = Insert.objects.get(pk=r2.pk) >>> r4 = Insert() >>> r4 = Insert.objects.insert_node(r4, r2, position='left', save=True) >>> print_tree_details([r4]) 9 - 2 0 1 2 >>> print_tree_details(Insert.objects.all()) 1 - 1 0 1 2 9 - 2 0 1 2 2 - 3 0 1 12 5 2 3 1 2 3 6 2 3 1 4 5 7 2 3 1 6 7 4 2 3 1 8 9 8 2 3 1 10 11 3 - 4 0 1 2 # Right sibling of root >>> r2 = Insert.objects.get(pk=r2.pk) >>> r5 = Insert() >>> r5 = Insert.objects.insert_node(r5, r2, position='right', save=True) >>> print_tree_details([r5]) 10 - 4 0 1 2 >>> print_tree_details(Insert.objects.all()) 1 - 1 0 1 2 9 - 2 0 1 2 2 - 3 0 1 12 5 2 3 1 2 3 6 2 3 1 4 5 7 2 3 1 6 7 4 2 3 1 8 9 8 2 3 1 10 11 10 - 4 0 1 2 3 - 5 0 1 2 # Last root >>> r6 = Insert() >>> r6 = Insert.objects.insert_node(r6, None, save=True) >>> print_tree_details([r6]) 11 - 6 0 1 2 >>> print_tree_details(Insert.objects.all()) 1 - 1 0 1 2 9 - 2 0 1 2 2 - 3 0 1 12 5 2 3 1 2 3 6 2 3 1 4 5 7 2 3 1 6 7 4 2 3 1 8 9 8 2 3 1 10 11 10 - 4 0 1 2 3 - 5 0 1 2 11 - 6 0 1 2 # order_insertion_by with single criterion #################################### >>> r1 = OrderedInsertion.objects.create(name='games') # Root ordering >>> r2 = OrderedInsertion.objects.create(name='food') >>> print_tree_details(OrderedInsertion.objects.all()) 2 - 1 0 1 2 1 - 2 0 1 2 # Same name - insert after >>> r3 = OrderedInsertion.objects.create(name='food') >>> print_tree_details(OrderedInsertion.objects.all()) 2 - 1 0 1 2 3 - 2 0 1 2 1 - 3 0 1 2 >>> c1 = OrderedInsertion.objects.create(name='zoo', parent=r3) >>> print_tree_details(OrderedInsertion.objects.all()) 2 - 1 0 1 2 3 - 2 0 1 4 4 3 2 1 2 3 1 - 3 0 1 2 >>> r3 = OrderedInsertion.objects.get(pk=r3.pk) >>> c2 = OrderedInsertion.objects.create(name='monkey', parent=r3) >>> print_tree_details(OrderedInsertion.objects.all()) 2 - 1 0 1 2 3 - 2 0 1 6 5 3 2 1 2 3 4 3 2 1 4 5 1 - 3 0 1 2 >>> r3 = OrderedInsertion.objects.get(pk=r3.pk) >>> c3 = OrderedInsertion.objects.create(name='animal', parent=r3) >>> print_tree_details(OrderedInsertion.objects.all()) 2 - 1 0 1 2 3 - 2 0 1 8 6 3 2 1 2 3 5 3 2 1 4 5 4 3 2 1 6 7 1 - 3 0 1 2 # order_insertion_by reparenting with single criterion ######################## # Root -> child >>> r1 = OrderedInsertion.objects.get(pk=r1.pk) >>> r3 = OrderedInsertion.objects.get(pk=r3.pk) >>> r1.parent = r3 >>> r1.save() >>> print_tree_details(OrderedInsertion.objects.all()) 2 - 1 0 1 2 3 - 2 0 1 10 6 3 2 1 2 3 1 3 2 1 4 5 5 3 2 1 6 7 4 3 2 1 8 9 # Child -> root >>> c3 = OrderedInsertion.objects.get(pk=c3.pk) >>> c3.parent = None >>> c3.save() >>> print_tree_details(OrderedInsertion.objects.all()) 6 - 1 0 1 2 2 - 2 0 1 2 3 - 3 0 1 8 1 3 3 1 2 3 5 3 3 1 4 5 4 3 3 1 6 7 # Child -> child >>> c1 = OrderedInsertion.objects.get(pk=c1.pk) >>> c1.parent = c3 >>> c1.save() >>> print_tree_details(OrderedInsertion.objects.all()) 6 - 1 0 1 4 4 6 1 1 2 3 2 - 2 0 1 2 3 - 3 0 1 6 1 3 3 1 2 3 5 3 3 1 4 5 >>> c3 = OrderedInsertion.objects.get(pk=c3.pk) >>> c2 = OrderedInsertion.objects.get(pk=c2.pk) >>> c2.parent = c3 >>> c2.save() >>> print_tree_details(OrderedInsertion.objects.all()) 6 - 1 0 1 6 5 6 1 1 2 3 4 6 1 1 4 5 2 - 2 0 1 2 3 - 3 0 1 4 1 3 3 1 2 3 # reordering after save, original insertion in order ########################## # sibling root nodes, inserted B,C, moved C-->A >>> _ = OrderedInsertion.objects.all().delete() >>> reset_sequence(OrderedInsertion) >>> b = OrderedInsertion.objects.create(name='b') >>> c = OrderedInsertion.objects.create(name='c') >>> c.name = 'a' >>> c.save() >>> print_tree_details(OrderedInsertion.objects.all()) 2 - 1 0 1 2 1 - 2 0 1 2 # sibling non-root nodes, inserted B,C, moved C-->A >>> _ = OrderedInsertion.objects.all().delete() >>> reset_sequence(OrderedInsertion) >>> root = OrderedInsertion.objects.create(name='root') >>> b = OrderedInsertion.objects.create(name='b', parent=root) >>> c = OrderedInsertion.objects.create(name='c', parent=root) >>> c.name = 'a' >>> c.save() >>> print_tree_details(OrderedInsertion.objects.all()) 1 - 1 0 1 6 3 1 1 1 2 3 2 1 1 1 4 5 # sibling root nodes, inserted A,B, moved A-->C >>> _ = OrderedInsertion.objects.all().delete() >>> reset_sequence(OrderedInsertion) >>> a = OrderedInsertion.objects.create(name='a') >>> b = OrderedInsertion.objects.create(name='b') >>> a = OrderedInsertion.objects.get(name='a') >>> a.name = 'c' >>> a.save() >>> print_tree_details(OrderedInsertion.objects.all()) 2 - 1 0 1 2 1 - 2 0 1 2 # sibling non-root nodes, inserted A,B, moved A-->C >>> _ = OrderedInsertion.objects.all().delete() >>> reset_sequence(OrderedInsertion) >>> root = OrderedInsertion.objects.create(name='root') >>> a = OrderedInsertion.objects.create(name='a', parent=root) >>> b = OrderedInsertion.objects.create(name='b', parent=root) >>> a = OrderedInsertion.objects.get(name='a') >>> a.name = 'c' >>> a.save() >>> print_tree_details(OrderedInsertion.objects.all()) 1 - 1 0 1 6 3 1 1 1 2 3 2 1 1 1 4 5 # reordering after save, original insertion in reverse ######################## # sibling root nodes, inserted C,B, moved C-->A >>> _ = OrderedInsertion.objects.all().delete() >>> reset_sequence(OrderedInsertion) >>> c = OrderedInsertion.objects.create(name='c') >>> b = OrderedInsertion.objects.create(name='b') >>> c = OrderedInsertion.objects.get(name='c') >>> c.name = 'a' >>> c.save() >>> print_tree_details(OrderedInsertion.objects.all()) 1 - 1 0 1 2 2 - 2 0 1 2 # sibling non-root nodes, inserted C,B, moved C-->A >>> _ = OrderedInsertion.objects.all().delete() >>> reset_sequence(OrderedInsertion) >>> root = OrderedInsertion.objects.create(name='root') >>> c = OrderedInsertion.objects.create(name='c', parent=root) >>> b = OrderedInsertion.objects.create(name='b', parent=root) >>> c = OrderedInsertion.objects.get(name='c') >>> c.name = 'a' >>> c.save() >>> print_tree_details(OrderedInsertion.objects.all()) 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 # sibling root nodes, inserted B,A, moved A-->C >>> _ = OrderedInsertion.objects.all().delete() >>> reset_sequence(OrderedInsertion) >>> b = OrderedInsertion.objects.create(name='b') >>> a = OrderedInsertion.objects.create(name='a') >>> a.name = 'c' >>> a.save() >>> print_tree_details(OrderedInsertion.objects.all()) 1 - 1 0 1 2 2 - 2 0 1 2 # sibling non-root nodes, inserted B,A, moved A-->C >>> _ = OrderedInsertion.objects.all().delete() >>> reset_sequence(OrderedInsertion) >>> root = OrderedInsertion.objects.create(name='root') >>> b = OrderedInsertion.objects.create(name='b', parent=root) >>> a = OrderedInsertion.objects.create(name='a', parent=root) >>> a.name = 'c' >>> a.save() >>> print_tree_details(OrderedInsertion.objects.all()) 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 # reordering after save, when only a single instance at level exists ########## # root node with no siblings no children, inserted B, moved B-->A (forward) >>> _ = OrderedInsertion.objects.all().delete() >>> reset_sequence(OrderedInsertion) >>> b = OrderedInsertion.objects.create(name='b') >>> b = OrderedInsertion.objects.get(name='b') >>> b.name = 'a' >>> b.save() >>> print_tree_details(OrderedInsertion.objects.all()) 1 - 1 0 1 2 # root node with no siblings with children, inserted B,child, moved B-->A (forward) >>> _ = OrderedInsertion.objects.all().delete() >>> reset_sequence(OrderedInsertion) >>> b = OrderedInsertion.objects.create(name='b') >>> child = OrderedInsertion.objects.create(name='child', parent=b) >>> b = OrderedInsertion.objects.get(name='b') >>> b.name = 'a' >>> b.save() >>> print_tree_details(OrderedInsertion.objects.all()) 1 - 1 0 1 4 2 1 1 1 2 3 # parented node with no siblings no children, inserted root,B, moved B-->A (forward) >>> _ = OrderedInsertion.objects.all().delete() >>> reset_sequence(OrderedInsertion) >>> root = OrderedInsertion.objects.create(name='root') >>> b = OrderedInsertion.objects.create(name='b', parent=root) >>> b = OrderedInsertion.objects.get(name='b') >>> b.name = 'a' >>> b.save() >>> print_tree_details(OrderedInsertion.objects.all()) 1 - 1 0 1 4 2 1 1 1 2 3 # parented node with no siblings with children, inserted root,B,C, moved B-->A (forward) >>> _ = OrderedInsertion.objects.all().delete() >>> reset_sequence(OrderedInsertion) >>> root = OrderedInsertion.objects.create(name='root') >>> b = OrderedInsertion.objects.create(name='b', parent=root) >>> c = OrderedInsertion.objects.create(name='c', parent=b) >>> b = OrderedInsertion.objects.get(name='b') >>> b.name = 'a' >>> b.save() >>> print_tree_details(OrderedInsertion.objects.all()) 1 - 1 0 1 6 2 1 1 1 2 5 3 2 1 2 3 4 # root node with no siblings no children, inserted B, moved B-->C (backward) >>> _ = OrderedInsertion.objects.all().delete() >>> reset_sequence(OrderedInsertion) >>> b = OrderedInsertion.objects.create(name='b') >>> b = OrderedInsertion.objects.get(name='b') >>> b.name = 'c' >>> b.save() >>> print_tree_details(OrderedInsertion.objects.all()) 1 - 1 0 1 2 # root node with no siblings with children, inserted B,child, moved B-->C (backward) >>> _ = OrderedInsertion.objects.all().delete() >>> reset_sequence(OrderedInsertion) >>> b = OrderedInsertion.objects.create(name='b') >>> child = OrderedInsertion.objects.create(name='child', parent=b) >>> b = OrderedInsertion.objects.get(name='b') >>> b.name = 'c' >>> b.save() >>> print_tree_details(OrderedInsertion.objects.all()) 1 - 1 0 1 4 2 1 1 1 2 3 # parented node with no siblings no children, inserted root,B, moved B-->C (backward) >>> _ = OrderedInsertion.objects.all().delete() >>> reset_sequence(OrderedInsertion) >>> root = OrderedInsertion.objects.create(name='root') >>> b = OrderedInsertion.objects.create(name='b', parent=root) >>> b = OrderedInsertion.objects.get(name='b') >>> b.name = 'c' >>> b.save() >>> print_tree_details(OrderedInsertion.objects.all()) 1 - 1 0 1 4 2 1 1 1 2 3 # parented node with no siblings with children, inserted root,B,D, moved B-->C (backward) >>> _ = OrderedInsertion.objects.all().delete() >>> reset_sequence(OrderedInsertion) >>> root = OrderedInsertion.objects.create(name='root') >>> b = OrderedInsertion.objects.create(name='b', parent=root) >>> d = OrderedInsertion.objects.create(name='d', parent=b) >>> b = OrderedInsertion.objects.get(name='b') >>> b.name = 'c' >>> b.save() >>> print_tree_details(OrderedInsertion.objects.all()) 1 - 1 0 1 6 2 1 1 1 2 5 3 2 1 2 3 4 >>> _ = OrderedInsertion.objects.all().delete() >>> reset_sequence(OrderedInsertion) >>> root = OrderedInsertion.objects.create(name='root') >>> r1 = OrderedInsertion.objects.create(name='r1', parent=root) >>> r2 = OrderedInsertion.objects.create(name='r2', parent=root) >>> print_tree_details(OrderedInsertion.objects.all()) 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 # Insertion of positioned nodes, multiple ordering criteria ################### >>> r1 = MultiOrder.objects.create(name='fff', size=20, date=date(2008, 1, 1)) # Root nodes - ordering by subsequent fields >>> r2 = MultiOrder.objects.create(name='fff', size=10, date=date(2009, 1, 1)) >>> print_tree_details(MultiOrder.objects.all()) 2 - 1 0 1 2 1 - 2 0 1 2 >>> r3 = MultiOrder.objects.create(name='fff', size=20, date=date(2007, 1, 1)) >>> print_tree_details(MultiOrder.objects.all()) 2 - 1 0 1 2 1 - 2 0 1 2 3 - 3 0 1 2 >>> r4 = MultiOrder.objects.create(name='fff', size=20, date=date(2008, 1, 1)) >>> print_tree_details(MultiOrder.objects.all()) 2 - 1 0 1 2 1 - 2 0 1 2 4 - 3 0 1 2 3 - 4 0 1 2 >>> r5 = MultiOrder.objects.create(name='fff', size=20, date=date(2007, 1, 1)) >>> print_tree_details(MultiOrder.objects.all()) 2 - 1 0 1 2 1 - 2 0 1 2 4 - 3 0 1 2 3 - 4 0 1 2 5 - 5 0 1 2 >>> r6 = MultiOrder.objects.create(name='aaa', size=999, date=date(2010, 1, 1)) >>> print_tree_details(MultiOrder.objects.all()) 6 - 1 0 1 2 2 - 2 0 1 2 1 - 3 0 1 2 4 - 4 0 1 2 3 - 5 0 1 2 5 - 6 0 1 2 # Child nodes >>> r1 = MultiOrder.objects.get(pk=r1.pk) >>> c1 = MultiOrder.objects.create(parent=r1, name='hhh', size=10, date=date(2009, 1, 1)) >>> print_tree_details(MultiOrder.objects.filter(tree_id=r1.tree_id)) 1 - 3 0 1 4 7 1 3 1 2 3 >>> r1 = MultiOrder.objects.get(pk=r1.pk) >>> c2 = MultiOrder.objects.create(parent=r1, name='hhh', size=20, date=date(2008, 1, 1)) >>> print_tree_details(MultiOrder.objects.filter(tree_id=r1.tree_id)) 1 - 3 0 1 6 7 1 3 1 2 3 8 1 3 1 4 5 >>> r1 = MultiOrder.objects.get(pk=r1.pk) >>> c3 = MultiOrder.objects.create(parent=r1, name='hhh', size=15, date=date(2008, 1, 1)) >>> print_tree_details(MultiOrder.objects.filter(tree_id=r1.tree_id)) 1 - 3 0 1 8 7 1 3 1 2 3 9 1 3 1 4 5 8 1 3 1 6 7 >>> r1 = MultiOrder.objects.get(pk=r1.pk) >>> c4 = MultiOrder.objects.create(parent=r1, name='hhh', size=15, date=date(2008, 1, 1)) >>> print_tree_details(MultiOrder.objects.filter(tree_id=r1.tree_id)) 1 - 3 0 1 10 7 1 3 1 2 3 9 1 3 1 4 5 10 1 3 1 6 7 8 1 3 1 8 9 # Multi-table Inheritance ##################################################### (pk, parent_id or '-', tree_id, level, left, right_attr) # Create test model instances >>> jack = Person.objects.create(name='Jack') >>> jill = Student.objects.create(name='Jill', parent=jack) >>> jim = Person.objects.create(name='Jim', parent=jack) >>> jess = Student.objects.create(name='Jess') >>> jeff = Person.objects.create(name='Jeff', parent=jess) >>> jane = Student.objects.create(name='Jane', parent=jeff) >>> joe = Person.objects.create(name='Joe', parent=jane) >>> julie = Student.objects.create(name='Julie', parent=jess) >>> print_tree_details(Person.objects.all()) 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 10 5 4 2 1 2 7 6 5 2 2 3 6 7 6 2 3 4 5 8 4 2 1 8 9 >>> print_tree_details(Student.objects.all()) 2 1 1 1 2 3 4 - 2 0 1 10 6 5 2 2 3 6 8 4 2 1 8 9 >>> jack = Person.objects.get(pk=1) >>> list(jack.get_descendants()) [, ] >>> jack.get_root() >>> jill = Person.objects.get(pk=3) >>> jill.get_root() >>> jess = Student.objects.get(pk=4) >>> list(jess.get_descendants()) [, , , ] >>> jess.get_root() >>> jeff = Person.objects.get(pk=5) >>> list(jeff.get_descendants()) [, ] >>> jeff.get_root() >>> jane = Student.objects.get(pk=6) >>> jane.get_root() >>> list(jane.get_ancestors()) [, ] django-mptt-0.8.0/tests/myapp/fixtures/0000755000076500000240000000000012635633365021400 5ustar cdestigterstaff00000000000000django-mptt-0.8.0/tests/myapp/fixtures/categories.json0000644000076500000240000000525212515002575024412 0ustar cdestigterstaff00000000000000[ { "pk": 1, "model": "myapp.category", "fields": { "rght": 20, "name": "PC & Video Games", "category_uuid": "6263ac21-f08b-4b44-9462-0489c56e0d3d", "parent": null, "level": 0, "lft": 1, "tree_id": 1 } }, { "pk": 2, "model": "myapp.category", "fields": { "rght": 7, "name": "Nintendo Wii", "category_uuid": "c6125cec-ef08-4095-9a07-fd5c6db50cc1", "parent": 1, "level": 1, "lft": 2, "tree_id": 1 } }, { "pk": 3, "model": "myapp.category", "fields": { "rght": 4, "name": "Games", "category_uuid": "85da870c-837a-48a5-a3e8-ab651cfdd799", "parent": 2, "level": 2, "lft": 3, "tree_id": 1 } }, { "pk": 4, "model": "myapp.category", "fields": { "rght": 6, "name": "Hardware & Accessories", "category_uuid": "840d1ccb-9ecc-48b0-bf7c-5b0f08334d3d", "parent": 2, "level": 2, "lft": 5, "tree_id": 1 } }, { "pk": 5, "model": "myapp.category", "fields": { "rght": 13, "name": "Xbox 360", "category_uuid": "b6299e26-d5e9-4dc2-9e2a-3f8033c8dfe5", "parent": 1, "level": 1, "lft": 8, "tree_id": 1 } }, { "pk": 6, "model": "myapp.category", "fields": { "rght": 10, "name": "Games", "category_uuid": "8171b0ec-f6e6-46dc-aabc-46a6399d9290", "parent": 5, "level": 2, "lft": 9, "tree_id": 1 } }, { "pk": 7, "model": "myapp.category", "fields": { "rght": 12, "name": "Hardware & Accessories", "category_uuid": "a64720cb-af69-440a-9abc-5a8f095974e4", "parent": 5, "level": 2, "lft": 11, "tree_id": 1 } }, { "pk": 8, "model": "myapp.category", "fields": { "rght": 19, "name": "PlayStation 3", "category_uuid": "01cac717-454e-48bd-8180-8a0089613bbd", "parent": 1, "level": 1, "lft": 14, "tree_id": 1 } }, { "pk": 9, "model": "myapp.category", "fields": { "rght": 16, "name": "Games", "category_uuid": "74f9c250-6e75-4152-bbc9-f7a89969c82b", "parent": 8, "level": 2, "lft": 15, "tree_id": 1 } }, { "pk": 10, "model": "myapp.category", "fields": { "rght": 18, "name": "Hardware & Accessories", "category_uuid": "a4918038-4d03-48ca-8cc3-1c28e177bed5", "parent": 8, "level": 2, "lft": 17, "tree_id": 1 } } ] django-mptt-0.8.0/tests/myapp/fixtures/genres.json0000644000076500000240000000450112455555636023562 0ustar cdestigterstaff00000000000000[ { "pk": 1, "model": "myapp.genre", "fields": { "rght": 16, "name": "Action", "parent": null, "level": 0, "lft": 1, "tree_id": 1 } }, { "pk": 2, "model": "myapp.genre", "fields": { "rght": 9, "name": "Platformer", "parent": 1, "level": 1, "lft": 2, "tree_id": 1 } }, { "pk": 3, "model": "myapp.genre", "fields": { "rght": 4, "name": "2D Platformer", "parent": 2, "level": 2, "lft": 3, "tree_id": 1 } }, { "pk": 4, "model": "myapp.genre", "fields": { "rght": 6, "name": "3D Platformer", "parent": 2, "level": 2, "lft": 5, "tree_id": 1 } }, { "pk": 5, "model": "myapp.genre", "fields": { "rght": 8, "name": "4D Platformer", "parent": 2, "level": 2, "lft": 7, "tree_id": 1 } }, { "pk": 6, "model": "myapp.genre", "fields": { "rght": 15, "name": "Shootemup", "parent": 1, "level": 1, "lft": 10, "tree_id": 1 } }, { "pk": 7, "model": "myapp.genre", "fields": { "rght": 12, "name": "Vertical Scrolling Shootemup", "parent": 6, "level": 2, "lft": 11, "tree_id": 1 } }, { "pk": 8, "model": "myapp.genre", "fields": { "rght": 14, "name": "Horizontal Scrolling Shootemup", "parent": 6, "level": 2, "lft": 13, "tree_id": 1 } }, { "pk": 9, "model": "myapp.genre", "fields": { "rght": 6, "name": "Role-playing Game", "parent": null, "level": 0, "lft": 1, "tree_id": 2 } }, { "pk": 10, "model": "myapp.genre", "fields": { "rght": 3, "name": "Action RPG", "parent": 9, "level": 1, "lft": 2, "tree_id": 2 } }, { "pk": 11, "model": "myapp.genre", "fields": { "rght": 5, "name": "Tactical RPG", "parent": 9, "level": 1, "lft": 4, "tree_id": 2 } } ] django-mptt-0.8.0/tests/myapp/fixtures/items.json0000644000076500000240000000067312515002575023410 0ustar cdestigterstaff00000000000000[ { "pk": 1, "model": "myapp.item", "fields": { "name": "FIFA 15", "category_fk": "b6299e26-d5e9-4dc2-9e2a-3f8033c8dfe5", "category_pk": 5 } }, { "pk": 2, "model": "myapp.item", "fields": { "name": "Halo: Reach", "category_fk": "b6299e26-d5e9-4dc2-9e2a-3f8033c8dfe5", "category_pk": 5 } } ] django-mptt-0.8.0/tests/myapp/fixtures/persons.json0000644000076500000240000000031112601601672023744 0ustar cdestigterstaff00000000000000[ { "pk": 1, "model": "myapp.person", "fields": { "rght": 16, "name": "John Doe", "parent": null, "level": 0, "lft": 1, "tree_id": 1 } } ]django-mptt-0.8.0/tests/myapp/models.py0000644000076500000240000001712512634472470021367 0ustar cdestigterstaff00000000000000from __future__ import unicode_literals from django.db import models from django.utils.encoding import python_2_unicode_compatible import mptt from mptt.fields import TreeForeignKey, TreeOneToOneField, TreeManyToManyField from mptt.models import MPTTModel from mptt.managers import TreeManager from django.db.models.query import QuerySet class CustomTreeQueryset(QuerySet): def custom_method(self): pass class CustomTreeManager(TreeManager): def get_query_set(self): return CustomTreeQueryset(model=self.model, using=self._db) def get_queryset(self): # Django 1.8 removed the fallbacks here. return CustomTreeQueryset(model=self.model, using=self._db) def get_empty_query_set(self): return self.get_queryset().none() @python_2_unicode_compatible class Category(MPTTModel): name = models.CharField(max_length=50) parent = TreeForeignKey( 'self', null=True, blank=True, related_name='children', on_delete=models.CASCADE) category_uuid = models.CharField(max_length=50, unique=True, null=True) def __str__(self): return self.name def delete(self): super(Category, self).delete() delete.alters_data = True @python_2_unicode_compatible class Item(models.Model): name = models.CharField(max_length=100) category_fk = models.ForeignKey( 'Category', to_field='category_uuid', null=True, related_name='items_by_fk', on_delete=models.CASCADE) category_pk = models.ForeignKey( 'Category', null=True, related_name='items_by_pk', on_delete=models.CASCADE) def __str__(self): return self.name @python_2_unicode_compatible class Genre(MPTTModel): name = models.CharField(max_length=50, unique=True) parent = TreeForeignKey( 'self', null=True, blank=True, related_name='children', on_delete=models.CASCADE) def __str__(self): return self.name class Game(models.Model): genre = TreeForeignKey(Genre, on_delete=models.CASCADE) genres_m2m = models.ManyToManyField(Genre, related_name='games_m2m') name = models.CharField(max_length=50) def __str__(self): return self.name class Insert(MPTTModel): parent = models.ForeignKey( 'self', null=True, blank=True, related_name='children', on_delete=models.CASCADE) @python_2_unicode_compatible class MultiOrder(MPTTModel): name = models.CharField(max_length=50) size = models.PositiveIntegerField() date = models.DateField() parent = TreeForeignKey( 'self', null=True, blank=True, related_name='children', on_delete=models.CASCADE) class MPTTMeta: order_insertion_by = ['name', 'size', '-date'] def __str__(self): return self.name class Node(MPTTModel): parent = TreeForeignKey( 'self', null=True, blank=True, related_name='children', on_delete=models.CASCADE) # To check that you can set level_attr etc to an existing field. level = models.IntegerField() class MPTTMeta: left_attr = 'does' right_attr = 'zis' level_attr = 'level' tree_id_attr = 'work' @python_2_unicode_compatible class OrderedInsertion(MPTTModel): name = models.CharField(max_length=50) parent = TreeForeignKey( 'self', null=True, blank=True, related_name='children', on_delete=models.CASCADE) class MPTTMeta: order_insertion_by = ['name'] def __str__(self): return self.name class Tree(MPTTModel): parent = TreeForeignKey( 'self', null=True, blank=True, related_name='children', on_delete=models.CASCADE) class NewStyleMPTTMeta(MPTTModel): parent = TreeForeignKey( 'self', null=True, blank=True, related_name='children', on_delete=models.CASCADE) class MPTTMeta(object): left_attr = 'testing' @python_2_unicode_compatible class Person(MPTTModel): name = models.CharField(max_length=50) parent = TreeForeignKey( 'self', null=True, blank=True, related_name='children', on_delete=models.CASCADE) # 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 def __str__(self): return self.name class Student(Person): type = models.CharField(max_length=50) @python_2_unicode_compatible class CustomPKName(MPTTModel): my_id = models.AutoField(db_column='my_custom_name', primary_key=True) name = models.CharField(max_length=50) parent = TreeForeignKey( 'self', null=True, blank=True, related_name='children', db_column="my_cusom_parent", on_delete=models.CASCADE) def __str__(self): return self.name class ReferencingModel(models.Model): fk = TreeForeignKey(Category, related_name='+', on_delete=models.CASCADE) one = TreeOneToOneField(Category, related_name='+', on_delete=models.CASCADE) m2m = TreeManyToManyField(Category, related_name='+') # for testing various types of inheritance: # 1. multi-table inheritance, with mptt fields on base class. class MultiTableInheritanceA1(MPTTModel): parent = TreeForeignKey( 'self', null=True, blank=True, related_name='children', on_delete=models.CASCADE) class MultiTableInheritanceA2(MultiTableInheritanceA1): name = models.CharField(max_length=50) # 2. multi-table inheritance, with mptt fields on child class. class MultiTableInheritanceB1(MPTTModel): name = models.CharField(max_length=50) class MultiTableInheritanceB2(MultiTableInheritanceB1): parent = TreeForeignKey( 'self', null=True, blank=True, related_name='children', on_delete=models.CASCADE) # 3. abstract models class AbstractModel(MPTTModel): parent = TreeForeignKey( 'self', null=True, blank=True, related_name='children', on_delete=models.CASCADE) ghosts = models.CharField(max_length=50) class Meta: abstract = True class ConcreteModel(AbstractModel): name = models.CharField(max_length=50) class AbstractConcreteAbstract(ConcreteModel): # abstract --> concrete --> abstract class Meta: abstract = True class ConcreteAbstractConcreteAbstract(ConcreteModel): # concrete --> abstract --> concrete --> abstract pass class ConcreteConcrete(ConcreteModel): # another subclass (concrete this time) of the root concrete model pass # 4. proxy models class SingleProxyModel(ConcreteModel): objects = CustomTreeManager() class Meta: proxy = True class DoubleProxyModel(SingleProxyModel): class Meta: proxy = True # 5. swappable models class SwappableModel(MPTTModel): parent = TreeForeignKey( 'self', null=True, blank=True, related_name='children', on_delete=models.CASCADE) class Meta: swappable = 'MPTT_SWAPPABLE_MODEL' class SwappedInModel(MPTTModel): parent = TreeForeignKey( 'self', null=True, blank=True, related_name='children', on_delete=models.CASCADE) name = models.CharField(max_length=50) class AutoNowDateFieldModel(MPTTModel): parent = TreeForeignKey( 'self', null=True, blank=True, related_name='children', on_delete=models.CASCADE) now = models.DateTimeField(auto_now_add=True) class MPTTMeta: order_insertion_by = ('now',) # test registering of remote model class Group(models.Model): name = models.CharField(max_length=100) TreeForeignKey( Group, blank=True, null=True, on_delete=models.CASCADE ).contribute_to_class(Group, 'parent') mptt.register(Group, order_insertion_by=('name',)) django-mptt-0.8.0/tests/myapp/models.pyc0000644000076500000240000004462712635626177021547 0ustar cdestigterstaff00000000000000 8urVcsddlmZddlmZddlmZddlZddlmZm Z m Z ddl m Z ddl mZddlmZd efd YZd efd YZed e fdYZedejfdYZede fdYZdejfdYZde fdYZede fdYZde fdYZede fdYZde fdYZde fd YZed!e fd"YZd#efd$YZed%e fd&YZ d'ejfd(YZ!d)e fd*YZ"d+e"fd,YZ#d-e fd.YZ$d/e$fd0YZ%d1e fd2YZ&d3e&fd4YZ'd5e'fd6YZ(d7e'fd8YZ)d9e'fd:YZ*d;e'fd<YZ+d=e+fd>YZ,d?e fd@YZ-dAe fdBYZ.dCe fdDYZ/dEejfdFYZ0ee0dGe1dHe1dIej23e0dJe4e0dKdLdS(Mi(sunicode_literals(smodels(spython_2_unicode_compatibleN(sTreeForeignKeysTreeOneToOneFieldsTreeManyToManyField(s MPTTModel(s TreeManager(sQuerySetsCustomTreeQuerysetcseZdZRS(cCsdS(N((tself((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pyt custom_method s(t__name__t __module__t custom_method(((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pytCustomTreeQueryset ssCustomTreeManagercs#eZdZdZdZRS(cCstd|jd|jS(Nsmodelsusing(tCustomTreeQuerysettmodelt_db(tself((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pyt get_query_setscCstd|jd|jS(Nsmodelsusing(tCustomTreeQuerysettmodelt_db(tself((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pyt get_querysetscCs|S(N(t get_querysettnone(tself((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pytget_empty_query_sets(t__name__t __module__t get_query_sett get_querysettget_empty_query_set(((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pytCustomTreeManagers  sCategoryc szeZeddZeddededddejZedddedeZ d Z d Z ee _ RS( s max_lengthi2uselfsnullsblanks related_nameuchildrens on_deletesuniquecCs|jS(N(tname(tself((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pyt__str__%scCstt|dS(N(tsupertCategorytdelete(tself((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pytdelete(s( t__name__t __module__tmodelst CharFieldtnametTreeForeignKeytTruetCASCADEtparentt category_uuidt__str__tdeletet alters_data(((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pytCategorys   sItemc sqeZeddZeddddedddejZeddedd dejZ d Z RS( s max_lengthiduCategorysto_fieldu category_uuidsnulls related_nameu items_by_fks on_deleteu items_by_pkcCs|jS(N(tname(tself((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pyt__str__8s( t__name__t __module__tmodelst CharFieldtnamet ForeignKeytTruetCASCADEt category_fkt category_pkt__str__(((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pytItem-s sGenrec sPeZedddeZeddededddejZd Z RS( s max_lengthi2suniqueuselfsnullsblanks related_nameuchildrens on_deletecCs|jS(N(tname(tself((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pyt__str__Cs( t__name__t __module__tmodelst CharFieldtTruetnametTreeForeignKeytCASCADEtparentt__str__(((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pytGenre<s  sGamecsMeZeedejZeeddZe ddZ dZ RS(s on_deletes related_nameu games_m2ms max_lengthi2cCs|jS(N(tname(tself((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pyt__str__Ls( t__name__t __module__tTreeForeignKeytGenretmodelstCASCADEtgenretManyToManyFieldt genres_m2mt CharFieldtnamet__str__(((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pytGameGssInsertc s2eZeddededddejZRS(uselfsnullsblanks related_nameuchildrens on_delete(t__name__t __module__tmodelst ForeignKeytTruetCASCADEtparent(((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pytInsertPss MultiOrderc sueZeddZeZeZe dde de dddej Z dfd YZ d ZRS( s max_lengthi2uselfsnullsblanks related_nameuchildrens on_deletesMPTTMetacseZdddgZRS(unameusizeu-date(t__name__t __module__torder_insertion_by(((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pytMPTTMeta_scCs|jS(N(tname(tself((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pyt__str__bs(t__name__t __module__tmodelst CharFieldtnametPositiveIntegerFieldtsizet DateFieldtdatetTreeForeignKeytTruetCASCADEtparenttMPTTMetat__str__(((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pyt MultiOrderVs   sNodec sNeZeddededddejZeZdfdYZ RS(uselfsnullsblanks related_nameuchildrens on_deletesMPTTMetacs eZdZdZdZdZRS(udoesuzisuleveluwork(t__name__t __module__t left_attrt right_attrt level_attrt tree_id_attr(((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pytMPTTMetams( t__name__t __module__tTreeForeignKeytTruetmodelstCASCADEtparentt IntegerFieldtleveltMPTTMeta(((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pytNodefs   sOrderedInsertionc s]eZeddZeddededddejZdfd YZ d Z RS( s max_lengthi2uselfsnullsblanks related_nameuchildrens on_deletesMPTTMetacseZdgZRS(uname(t__name__t __module__torder_insertion_by(((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pytMPTTMeta{scCs|jS(N(tname(tself((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pyt__str__~s( t__name__t __module__tmodelst CharFieldtnametTreeForeignKeytTruetCASCADEtparenttMPTTMetat__str__(((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pytOrderedInsertionts  sTreec s/eZeddededddejZRS(uselfsnullsblanks related_nameuchildrens on_delete(t__name__t __module__tTreeForeignKeytTruetmodelstCASCADEtparent(((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pytTreessNewStyleMPTTMetac sEeZeddededddejZdefdYZRS(uselfsnullsblanks related_nameuchildrens on_deletesMPTTMetacseZdZRS(utesting(t__name__t __module__t left_attr(((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pytMPTTMetas( t__name__t __module__tTreeForeignKeytTruetmodelstCASCADEtparenttobjecttMPTTMeta(((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pytNewStyleMPTTMetas sPersonc sYeZeddZeddededddejZe Z e Z dZ RS( s max_lengthi2uselfsnullsblanks related_nameuchildrens on_deletecCs|jS(N(tname(tself((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pyt__str__s( t__name__t __module__tmodelst CharFieldtnametTreeForeignKeytTruetCASCADEtparenttCustomTreeManagertobjectst_default_managert__str__(((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pytPersons  sStudentcseZeddZRS(s max_lengthi2(t__name__t __module__tmodelst CharFieldttype(((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pytStudentss CustomPKNamec sheZedddeZeddZeddededd dd d ej Z d Z RS( s db_columnumy_custom_names primary_keys max_lengthi2uselfsnullsblanks related_nameuchildrenumy_cusom_parents on_deletecCs|jS(N(tname(tself((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pyt__str__s( t__name__t __module__tmodelst AutoFieldtTruetmy_idt CharFieldtnametTreeForeignKeytCASCADEtparentt__str__(((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pyt CustomPKNames  sReferencingModelcsPeZeedddejZeedddejZe eddZ RS(s related_nameu+s on_delete( t__name__t __module__tTreeForeignKeytCategorytmodelstCASCADEtfktTreeOneToOneFieldtonetTreeManyToManyFieldtm2m(((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pytReferencingModelssMultiTableInheritanceA1c s/eZeddededddejZRS(uselfsnullsblanks related_nameuchildrens on_delete(t__name__t __module__tTreeForeignKeytTruetmodelstCASCADEtparent(((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pytMultiTableInheritanceA1ssMultiTableInheritanceA2cseZeddZRS(s max_lengthi2(t__name__t __module__tmodelst CharFieldtname(((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pytMultiTableInheritanceA2ssMultiTableInheritanceB1cseZeddZRS(s max_lengthi2(t__name__t __module__tmodelst CharFieldtname(((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pytMultiTableInheritanceB1ssMultiTableInheritanceB2c s/eZeddededddejZRS(uselfsnullsblanks related_nameuchildrens on_delete(t__name__t __module__tTreeForeignKeytTruetmodelstCASCADEtparent(((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pytMultiTableInheritanceB2ss AbstractModelc sTeZeddededddejZeddZdfd YZ RS( uselfsnullsblanks related_nameuchildrens on_deletes max_lengthi2sMetacseZeZRS((t__name__t __module__tTruetabstract(((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pytMetas( t__name__t __module__tTreeForeignKeytTruetmodelstCASCADEtparentt CharFieldtghoststMeta(((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pyt AbstractModels  s ConcreteModelcseZeddZRS(s max_lengthi2(t__name__t __module__tmodelst CharFieldtname(((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pyt ConcreteModelssAbstractConcreteAbstractcseZdfdYZRS(sMetacseZeZRS((t__name__t __module__tTruetabstract(((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pytMetas(t__name__t __module__tMeta(((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pytAbstractConcreteAbstractss ConcreteAbstractConcreteAbstractcseZRS((t__name__t __module__(((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pyt ConcreteAbstractConcreteAbstractssConcreteConcretecseZRS((t__name__t __module__(((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pytConcreteConcretessSingleProxyModelcs$eZeZdfdYZRS(sMetacseZeZRS((t__name__t __module__tTruetproxy(((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pytMetas(t__name__t __module__tCustomTreeManagertobjectstMeta(((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pytSingleProxyModels sDoubleProxyModelcseZdfdYZRS(sMetacseZeZRS((t__name__t __module__tTruetproxy(((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pytMetas(t__name__t __module__tMeta(((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pytDoubleProxyModelssSwappableModelc sBeZeddededddejZdfdYZRS(uselfsnullsblanks related_nameuchildrens on_deletesMetacseZdZRS(uMPTT_SWAPPABLE_MODEL(t__name__t __module__t swappable(((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pytMetas(t__name__t __module__tTreeForeignKeytTruetmodelstCASCADEtparenttMeta(((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pytSwappableModels sSwappedInModelc sAeZeddededddejZeddZRS(uselfsnullsblanks related_nameuchildrens on_deletes max_lengthi2( t__name__t __module__tTreeForeignKeytTruetmodelstCASCADEtparentt CharFieldtname(((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pytSwappedInModel s sAutoNowDateFieldModelc sTeZeddededddejZedeZdfdYZ RS( uselfsnullsblanks related_nameuchildrens on_deletes auto_now_addsMPTTMetacseZdZRS((unow(t__name__t __module__torder_insertion_by(((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pytMPTTMetas( t__name__t __module__tTreeForeignKeytTruetmodelstCASCADEtparentt DateTimeFieldtnowtMPTTMeta(((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pytAutoNowDateFieldModels  sGroupcseZeddZRS(s max_lengthid(t__name__t __module__tmodelst CharFieldtname(((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pytGroupssblanksnulls on_deleteuparentsorder_insertion_by(uname(5t __future__tunicode_literalst django.dbtmodelstdjango.utils.encodingtpython_2_unicode_compatibletmpttt mptt.fieldstTreeForeignKeytTreeOneToOneFieldtTreeManyToManyFieldt mptt.modelst MPTTModelt mptt.managerst TreeManagertdjango.db.models.querytQuerySettCustomTreeQuerysettCustomTreeManagertCategorytModeltItemtGenretGametInsertt MultiOrdertNodetOrderedInsertiontTreetNewStyleMPTTMetatPersontStudentt CustomPKNametReferencingModeltMultiTableInheritanceA1tMultiTableInheritanceA2tMultiTableInheritanceB1tMultiTableInheritanceB2t AbstractModelt ConcreteModeltAbstractConcreteAbstractt ConcreteAbstractConcreteAbstracttConcreteConcretetSingleProxyModeltDoubleProxyModeltSwappableModeltSwappedInModeltAutoNowDateFieldModeltGrouptTruetCASCADEtcontribute_to_classtregister(((t</Users/cdestigter/checkout/django-mptt/tests/myapp/models.pytsb            django-mptt-0.8.0/tests/myapp/tests.py0000644000076500000240000020243012635625646021247 0ustar cdestigterstaff00000000000000from __future__ import unicode_literals import io import os import re import sys import tempfile import unittest from django.contrib.auth.models import Group, User from django.db.models import Q from django.db.models.query_utils import DeferredAttribute from django.apps import apps from django.forms.models import modelform_factory from django.template import Template, TemplateSyntaxError, Context from django.test import TestCase from django.utils.six import string_types, PY3, b, assertRaisesRegex try: from mock_django import mock_signal_receiver except ImportError: mock_signal_receiver = None from mptt.exceptions import CantDisableUpdates, InvalidMove from mptt.forms import ( MPTTAdminForm, TreeNodeChoiceField, TreeNodeMultipleChoiceField, MoveNodeForm) from mptt.models import MPTTModel from mptt.managers import TreeManager from mptt.signals import node_moved from mptt.templatetags.mptt_tags import cache_tree_children from mptt.utils import print_debug_info from myapp.models import ( Category, Item, Genre, CustomPKName, SingleProxyModel, DoubleProxyModel, ConcreteModel, OrderedInsertion, AutoNowDateFieldModel, Person, CustomTreeQueryset, Node, ReferencingModel, CustomTreeManager) def get_tree_details(nodes): """ Creates pertinent tree details for the given list of nodes. The fields are: id parent_id tree_id level left right """ if hasattr(nodes, 'order_by'): nodes = list(nodes.order_by('tree_id', 'lft', 'pk')) nodes = list(nodes) opts = nodes[0]._mptt_meta return '\n'.join(['%s %s %s %s %s %s' % (n.pk, getattr(n, '%s_id' % opts.parent_attr) or '-', getattr(n, opts.tree_id_attr), getattr(n, opts.level_attr), getattr(n, opts.left_attr), getattr(n, opts.right_attr)) for n in nodes]) leading_whitespace_re = re.compile(r'^\s+', re.MULTILINE) def tree_details(text): """ Trims leading whitespace from the given text specifying tree details so triple-quoted strings can be used to provide tree details in a readable format (says who?), to be compared with the result of using the ``get_tree_details`` function. """ return leading_whitespace_re.sub('', text.rstrip()) class TreeTestCase(TestCase): def assertTreeEqual(self, tree1, tree2): if not isinstance(tree1, string_types): tree1 = get_tree_details(tree1) tree1 = tree_details(tree1) if not isinstance(tree2, string_types): tree2 = get_tree_details(tree2) tree2 = tree_details(tree2) return self.assertEqual(tree1, tree2, "\n%r\n != \n%r" % (tree1, tree2)) class DocTestTestCase(TreeTestCase): def test_run_doctest(self): class DummyStream: content = "" encoding = 'utf8' def write(self, text): self.content += text def flush(self): pass dummy_stream = DummyStream() before = sys.stdout sys.stdout = dummy_stream with open(os.path.join(os.path.dirname(__file__), 'doctests.txt')) as f: with tempfile.NamedTemporaryFile() as temp: text = f.read() if PY3: # unicode literals in the doctests screw up doctest on py3. # this is pretty icky, but I can't find any other # workarounds :( text = re.sub(r"""\bu(["\'])""", r"\1", text) temp.write(b(text)) else: temp.write(text) temp.flush() import doctest doctest.testfile( temp.name, module_relative=False, optionflags=doctest.IGNORE_EXCEPTION_DETAIL, encoding='utf-8', ) sys.stdout = before content = dummy_stream.content if content: before.write(content + '\n') self.fail() # genres.json defines the following tree structure # # 1 - 1 0 1 16 action # 2 1 1 1 2 9 +-- platformer # 3 2 1 2 3 4 | |-- platformer_2d # 4 2 1 2 5 6 | |-- platformer_3d # 5 2 1 2 7 8 | +-- platformer_4d # 6 1 1 1 10 15 +-- shmup # 7 6 1 2 11 12 |-- shmup_vertical # 8 6 1 2 13 14 +-- shmup_horizontal # 9 - 2 0 1 6 rpg # 10 9 2 1 2 3 |-- arpg # 11 9 2 1 4 5 +-- trpg class ReparentingTestCase(TreeTestCase): """ Test that trees are in the appropriate state after reparenting and that reparented items have the correct tree attributes defined, should they be required for use after a save. """ fixtures = ['genres.json'] def test_new_root_from_subtree(self): shmup = Genre.objects.get(id=6) shmup.parent = None shmup.save() self.assertTreeEqual([shmup], '6 - 3 0 1 6') self.assertTreeEqual(Genre.objects.all(), """ 1 - 1 0 1 10 2 1 1 1 2 9 3 2 1 2 3 4 4 2 1 2 5 6 5 2 1 2 7 8 9 - 2 0 1 6 10 9 2 1 2 3 11 9 2 1 4 5 6 - 3 0 1 6 7 6 3 1 2 3 8 6 3 1 4 5 """) def test_new_root_from_leaf_with_siblings(self): platformer_2d = Genre.objects.get(id=3) platformer_2d.parent = None platformer_2d.save() self.assertTreeEqual([platformer_2d], '3 - 3 0 1 2') self.assertTreeEqual(Genre.objects.all(), """ 1 - 1 0 1 14 2 1 1 1 2 7 4 2 1 2 3 4 5 2 1 2 5 6 6 1 1 1 8 13 7 6 1 2 9 10 8 6 1 2 11 12 9 - 2 0 1 6 10 9 2 1 2 3 11 9 2 1 4 5 3 - 3 0 1 2 """) def test_new_child_from_root(self): action = Genre.objects.get(id=1) rpg = Genre.objects.get(id=9) action.parent = rpg action.save() self.assertTreeEqual([action], '1 9 2 1 6 21') self.assertTreeEqual([rpg], '9 - 2 0 1 22') self.assertTreeEqual(Genre.objects.all(), """ 9 - 2 0 1 22 10 9 2 1 2 3 11 9 2 1 4 5 1 9 2 1 6 21 2 1 2 2 7 14 3 2 2 3 8 9 4 2 2 3 10 11 5 2 2 3 12 13 6 1 2 2 15 20 7 6 2 3 16 17 8 6 2 3 18 19 """) def test_move_leaf_to_other_tree(self): shmup_horizontal = Genre.objects.get(id=8) rpg = Genre.objects.get(id=9) shmup_horizontal.parent = rpg shmup_horizontal.save() self.assertTreeEqual([shmup_horizontal], '8 9 2 1 6 7') self.assertTreeEqual([rpg], '9 - 2 0 1 8') self.assertTreeEqual(Genre.objects.all(), """ 1 - 1 0 1 14 2 1 1 1 2 9 3 2 1 2 3 4 4 2 1 2 5 6 5 2 1 2 7 8 6 1 1 1 10 13 7 6 1 2 11 12 9 - 2 0 1 8 10 9 2 1 2 3 11 9 2 1 4 5 8 9 2 1 6 7 """) def test_move_subtree_to_other_tree(self): shmup = Genre.objects.get(id=6) trpg = Genre.objects.get(id=11) shmup.parent = trpg shmup.save() self.assertTreeEqual([shmup], '6 11 2 2 5 10') self.assertTreeEqual([trpg], '11 9 2 1 4 11') self.assertTreeEqual(Genre.objects.all(), """ 1 - 1 0 1 10 2 1 1 1 2 9 3 2 1 2 3 4 4 2 1 2 5 6 5 2 1 2 7 8 9 - 2 0 1 12 10 9 2 1 2 3 11 9 2 1 4 11 6 11 2 2 5 10 7 6 2 3 6 7 8 6 2 3 8 9 """) def test_move_child_up_level(self): shmup_horizontal = Genre.objects.get(id=8) action = Genre.objects.get(id=1) shmup_horizontal.parent = action shmup_horizontal.save() self.assertTreeEqual([shmup_horizontal], '8 1 1 1 14 15') self.assertTreeEqual([action], '1 - 1 0 1 16') self.assertTreeEqual(Genre.objects.all(), """ 1 - 1 0 1 16 2 1 1 1 2 9 3 2 1 2 3 4 4 2 1 2 5 6 5 2 1 2 7 8 6 1 1 1 10 13 7 6 1 2 11 12 8 1 1 1 14 15 9 - 2 0 1 6 10 9 2 1 2 3 11 9 2 1 4 5 """) def test_move_subtree_down_level(self): shmup = Genre.objects.get(id=6) platformer = Genre.objects.get(id=2) shmup.parent = platformer shmup.save() self.assertTreeEqual([shmup], '6 2 1 2 9 14') self.assertTreeEqual([platformer], '2 1 1 1 2 15') self.assertTreeEqual(Genre.objects.all(), """ 1 - 1 0 1 16 2 1 1 1 2 15 3 2 1 2 3 4 4 2 1 2 5 6 5 2 1 2 7 8 6 2 1 2 9 14 7 6 1 3 10 11 8 6 1 3 12 13 9 - 2 0 1 6 10 9 2 1 2 3 11 9 2 1 4 5 """) def test_move_to(self): rpg = Genre.objects.get(pk=9) action = Genre.objects.get(pk=1) rpg.move_to(action) rpg.save() self.assertEqual(rpg.parent, action) def test_invalid_moves(self): # A node may not be made a child of itself action = Genre.objects.get(id=1) action.parent = action platformer = Genre.objects.get(id=2) platformer.parent = platformer self.assertRaises(InvalidMove, action.save) self.assertRaises(InvalidMove, platformer.save) # A node may not be made a child of any of its descendants platformer_4d = Genre.objects.get(id=5) action.parent = platformer_4d platformer.parent = platformer_4d self.assertRaises(InvalidMove, action.save) self.assertRaises(InvalidMove, platformer.save) # New parent is still set when an error occurs self.assertEqual(action.parent, platformer_4d) self.assertEqual(platformer.parent, platformer_4d) class ConcurrencyTestCase(TreeTestCase): """ Test that tree structure remains intact when saving nodes (without setting new parent) after tree structure has been changed. """ def setUp(self): fruit = ConcreteModel.objects.create(name="Fruit") vegie = ConcreteModel.objects.create(name="Vegie") ConcreteModel.objects.create(name="Apple", parent=fruit) ConcreteModel.objects.create(name="Pear", parent=fruit) ConcreteModel.objects.create(name="Tomato", parent=vegie) ConcreteModel.objects.create(name="Carrot", parent=vegie) # sanity check self.assertTreeEqual(ConcreteModel.objects.all(), """ 1 - 1 0 1 6 3 1 1 1 2 3 4 1 1 1 4 5 2 - 2 0 1 6 5 2 2 1 2 3 6 2 2 1 4 5 """) def _modify_tree(self): fruit = ConcreteModel.objects.get(name="Fruit") vegie = ConcreteModel.objects.get(name="Vegie") vegie.move_to(fruit) def _assert_modified_tree_state(self): carrot = ConcreteModel.objects.get(id=6) self.assertTreeEqual([carrot], '6 2 1 2 5 6') self.assertTreeEqual(ConcreteModel.objects.all(), """ 1 - 1 0 1 12 2 1 1 1 2 7 5 2 1 2 3 4 6 2 1 2 5 6 3 1 1 1 8 9 4 1 1 1 10 11 """) def test_node_save_after_tree_restructuring(self): carrot = ConcreteModel.objects.get(id=6) self._modify_tree() carrot.name = "Purple carrot" carrot.save() self._assert_modified_tree_state() def test_node_save_after_tree_restructuring_with_update_fields(self): """ Test that model is saved properly when passing update_fields """ carrot = ConcreteModel.objects.get(id=6) self._modify_tree() # update with kwargs carrot.name = "Won't change" carrot.ghosts = "Will get updated" carrot.save(update_fields=["ghosts"]) self._assert_modified_tree_state() updated_carrot = ConcreteModel.objects.get(id=6) self.assertEqual(updated_carrot.ghosts, carrot.ghosts) self.assertNotEqual(updated_carrot.name, carrot.name) # update with positional arguments carrot.name = "Will change" carrot.ghosts = "Will not be updated" carrot.save(False, False, None, ["name"]) updated_carrot = ConcreteModel.objects.get(id=6) self.assertNotEqual(updated_carrot.ghosts, carrot.ghosts) self.assertEqual(updated_carrot.name, carrot.name) def test_update_fields_positional(self): """ Test that update_fields works as a positional argument Test for https://github.com/django-mptt/django-mptt/issues/384 """ carrot = ConcreteModel.objects.get(id=6) # Why would you do it this way? Meh. carrot.save(False, False, None, None) # categories.json defines the following tree structure: # # 1 - 1 0 1 20 games # 2 1 1 1 2 7 +-- wii # 3 2 1 2 3 4 | |-- wii_games # 4 2 1 2 5 6 | +-- wii_hardware # 5 1 1 1 8 13 +-- xbox360 # 6 5 1 2 9 10 | |-- xbox360_games # 7 5 1 2 11 12 | +-- xbox360_hardware # 8 1 1 1 14 19 +-- ps3 # 9 8 1 2 15 16 |-- ps3_games # 10 8 1 2 17 18 +-- ps3_hardware class DeletionTestCase(TreeTestCase): """ Tests that the tree structure is maintained appropriately in various deletion scenarios. """ fixtures = ['categories.json'] def test_delete_root_node(self): # Add a few other roots to verify that they aren't affected Category(name='Preceding root').insert_at(Category.objects.get(id=1), 'left', save=True) Category(name='Following root').insert_at(Category.objects.get(id=1), 'right', save=True) self.assertTreeEqual(Category.objects.all(), """ 11 - 1 0 1 2 1 - 2 0 1 20 2 1 2 1 2 7 3 2 2 2 3 4 4 2 2 2 5 6 5 1 2 1 8 13 6 5 2 2 9 10 7 5 2 2 11 12 8 1 2 1 14 19 9 8 2 2 15 16 10 8 2 2 17 18 12 - 3 0 1 2 """) Category.objects.get(id=1).delete() self.assertTreeEqual( Category.objects.all(), """ 11 - 1 0 1 2 12 - 3 0 1 2 """) def test_delete_last_node_with_siblings(self): Category.objects.get(id=9).delete() self.assertTreeEqual(Category.objects.all(), """ 1 - 1 0 1 18 2 1 1 1 2 7 3 2 1 2 3 4 4 2 1 2 5 6 5 1 1 1 8 13 6 5 1 2 9 10 7 5 1 2 11 12 8 1 1 1 14 17 10 8 1 2 15 16 """) def test_delete_last_node_with_descendants(self): Category.objects.get(id=8).delete() self.assertTreeEqual(Category.objects.all(), """ 1 - 1 0 1 14 2 1 1 1 2 7 3 2 1 2 3 4 4 2 1 2 5 6 5 1 1 1 8 13 6 5 1 2 9 10 7 5 1 2 11 12 """) def test_delete_node_with_siblings(self): child = Category.objects.get(id=6) parent = child.parent self.assertEqual(parent.get_descendant_count(), 2) child.delete() self.assertTreeEqual(Category.objects.all(), """ 1 - 1 0 1 18 2 1 1 1 2 7 3 2 1 2 3 4 4 2 1 2 5 6 5 1 1 1 8 11 7 5 1 2 9 10 8 1 1 1 12 17 9 8 1 2 13 14 10 8 1 2 15 16 """) self.assertEqual(parent.get_descendant_count(), 1) parent = Category.objects.get(pk=parent.pk) self.assertEqual(parent.get_descendant_count(), 1) def test_delete_node_with_descendants_and_siblings(self): """ Regression test for Issue 23 - we used to use pre_delete, which resulted in tree cleanup being performed for every node being deleted, rather than just the node on which ``delete()`` was called. """ Category.objects.get(id=5).delete() self.assertTreeEqual(Category.objects.all(), """ 1 - 1 0 1 14 2 1 1 1 2 7 3 2 1 2 3 4 4 2 1 2 5 6 8 1 1 1 8 13 9 8 1 2 9 10 10 8 1 2 11 12 """) class IntraTreeMovementTestCase(TreeTestCase): pass class InterTreeMovementTestCase(TreeTestCase): pass class PositionedInsertionTestCase(TreeTestCase): pass class CustomPKNameTestCase(TreeTestCase): def setUp(self): manager = CustomPKName.objects c1 = manager.create(name="c1") manager.create(name="c11", parent=c1) manager.create(name="c12", parent=c1) c2 = manager.create(name="c2") manager.create(name="c21", parent=c2) manager.create(name="c22", parent=c2) manager.create(name="c3") def test_get_next_sibling(self): root = CustomPKName.objects.get(name="c12") sib = root.get_next_sibling() self.assertTrue(sib is None) class DisabledUpdatesTestCase(TreeTestCase): def setUp(self): self.a = ConcreteModel.objects.create(name="a") self.b = ConcreteModel.objects.create(name="b", parent=self.a) self.c = ConcreteModel.objects.create(name="c", parent=self.a) self.d = ConcreteModel.objects.create(name="d") # state is now: self.assertTreeEqual(ConcreteModel.objects.all(), """ 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 """) def test_single_proxy(self): self.assertTrue(ConcreteModel._mptt_updates_enabled) self.assertTrue(SingleProxyModel._mptt_updates_enabled) self.assertRaises( CantDisableUpdates, SingleProxyModel.objects.disable_mptt_updates().__enter__) self.assertTrue(ConcreteModel._mptt_updates_enabled) self.assertTrue(SingleProxyModel._mptt_updates_enabled) with ConcreteModel.objects.disable_mptt_updates(): self.assertFalse(ConcreteModel._mptt_updates_enabled) self.assertFalse(SingleProxyModel._mptt_updates_enabled) self.assertTrue(ConcreteModel._mptt_updates_enabled) self.assertTrue(SingleProxyModel._mptt_updates_enabled) def test_double_proxy(self): self.assertTrue(ConcreteModel._mptt_updates_enabled) self.assertTrue(DoubleProxyModel._mptt_updates_enabled) self.assertRaises( CantDisableUpdates, DoubleProxyModel.objects.disable_mptt_updates().__enter__) self.assertTrue(ConcreteModel._mptt_updates_enabled) self.assertTrue(DoubleProxyModel._mptt_updates_enabled) with ConcreteModel.objects.disable_mptt_updates(): self.assertFalse(ConcreteModel._mptt_updates_enabled) self.assertFalse(DoubleProxyModel._mptt_updates_enabled) self.assertTrue(ConcreteModel._mptt_updates_enabled) self.assertTrue(DoubleProxyModel._mptt_updates_enabled) def test_insert_child(self): with self.assertNumQueries(2): with ConcreteModel.objects.disable_mptt_updates(): # 1 query here: with self.assertNumQueries(1): ConcreteModel.objects.create(name="e", parent=self.d) # 2nd query here: self.assertTreeEqual(ConcreteModel.objects.all(), """ 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 5 4 2 1 2 3 """) # yes, this is wrong. that's what disable_mptt_updates() does :/ self.assertTreeEqual(ConcreteModel.objects.all(), """ 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 5 4 2 1 2 3 """) def test_insert_root(self): with self.assertNumQueries(2): with ConcreteModel.objects.disable_mptt_updates(): with self.assertNumQueries(1): # 1 query here: ConcreteModel.objects.create(name="e") # 2nd query here: self.assertTreeEqual(ConcreteModel.objects.all(), """ 5 - 0 0 1 2 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 """) self.assertTreeEqual(ConcreteModel.objects.all(), """ 5 - 0 0 1 2 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 """) def test_move_node_same_tree(self): with self.assertNumQueries(2): with ConcreteModel.objects.disable_mptt_updates(): with self.assertNumQueries(1): # 2 queries here: # (django does a query to determine if the row is in the db yet) self.c.parent = self.b self.c.save() # 3rd query here: self.assertTreeEqual(ConcreteModel.objects.all(), """ 1 - 1 0 1 6 2 1 1 1 2 3 3 2 1 1 4 5 4 - 2 0 1 2 """) # yes, this is wrong. that's what disable_mptt_updates() does :/ self.assertTreeEqual(ConcreteModel.objects.all(), """ 1 - 1 0 1 6 2 1 1 1 2 3 3 2 1 1 4 5 4 - 2 0 1 2 """) def test_move_node_different_tree(self): with self.assertNumQueries(2): with ConcreteModel.objects.disable_mptt_updates(): with self.assertNumQueries(1): # 1 update query self.c.parent = self.d self.c.save() # query 2 here: self.assertTreeEqual(ConcreteModel.objects.all(), """ 1 - 1 0 1 6 2 1 1 1 2 3 3 4 1 1 4 5 4 - 2 0 1 2 """) # yes, this is wrong. that's what disable_mptt_updates() does :/ self.assertTreeEqual(ConcreteModel.objects.all(), """ 1 - 1 0 1 6 2 1 1 1 2 3 3 4 1 1 4 5 4 - 2 0 1 2 """) def test_move_node_to_root(self): with self.assertNumQueries(2): with ConcreteModel.objects.disable_mptt_updates(): with self.assertNumQueries(1): # 1 update query self.c.parent = None self.c.save() # query 2 here: self.assertTreeEqual(ConcreteModel.objects.all(), """ 1 - 1 0 1 6 2 1 1 1 2 3 3 - 1 1 4 5 4 - 2 0 1 2 """) # yes, this is wrong. that's what disable_mptt_updates() does :/ self.assertTreeEqual(ConcreteModel.objects.all(), """ 1 - 1 0 1 6 2 1 1 1 2 3 3 - 1 1 4 5 4 - 2 0 1 2 """) def test_move_root_to_child(self): with self.assertNumQueries(2): with ConcreteModel.objects.disable_mptt_updates(): with self.assertNumQueries(1): # 1 update query self.d.parent = self.c self.d.save() # query 2 here: self.assertTreeEqual(ConcreteModel.objects.all(), """ 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 3 2 0 1 2 """) # yes, this is wrong. that's what disable_mptt_updates() does :/ self.assertTreeEqual(ConcreteModel.objects.all(), """ 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 3 2 0 1 2 """) class DelayedUpdatesTestCase(TreeTestCase): def setUp(self): self.a = ConcreteModel.objects.create(name="a") self.b = ConcreteModel.objects.create(name="b", parent=self.a) self.c = ConcreteModel.objects.create(name="c", parent=self.a) self.d = ConcreteModel.objects.create(name="d") self.z = ConcreteModel.objects.create(name="z") # state is now: self.assertTreeEqual(ConcreteModel.objects.all(), """ 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 5 - 3 0 1 2 """) def test_proxy(self): self.assertFalse(ConcreteModel._mptt_is_tracking) self.assertFalse(SingleProxyModel._mptt_is_tracking) self.assertRaises( CantDisableUpdates, SingleProxyModel.objects.delay_mptt_updates().__enter__) self.assertFalse(ConcreteModel._mptt_is_tracking) self.assertFalse(SingleProxyModel._mptt_is_tracking) with ConcreteModel.objects.delay_mptt_updates(): self.assertTrue(ConcreteModel._mptt_is_tracking) self.assertTrue(SingleProxyModel._mptt_is_tracking) self.assertFalse(ConcreteModel._mptt_is_tracking) self.assertFalse(SingleProxyModel._mptt_is_tracking) def test_double_context_manager(self): with ConcreteModel.objects.delay_mptt_updates(): self.assertTrue(ConcreteModel._mptt_is_tracking) with ConcreteModel.objects.delay_mptt_updates(): self.assertTrue(ConcreteModel._mptt_is_tracking) self.assertTrue(ConcreteModel._mptt_is_tracking) self.assertFalse(ConcreteModel._mptt_is_tracking) def test_insert_child(self): with self.assertNumQueries(8): with ConcreteModel.objects.delay_mptt_updates(): with self.assertNumQueries(2): # 1 query for target stale check, # 1 query to save node. ConcreteModel.objects.create(name="e", parent=self.d) # 3rd query here: self.assertTreeEqual(ConcreteModel.objects.all(), """ 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 6 4 2 1 2 3 5 - 3 0 1 2 """) # remaining queries (4 through 8) are the partial rebuild process. self.assertTreeEqual(ConcreteModel.objects.all(), """ 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 4 6 4 2 1 2 3 5 - 3 0 1 2 """) def test_insert_root(self): with self.assertNumQueries(3): with ConcreteModel.objects.delay_mptt_updates(): with self.assertNumQueries(2): # 2 queries required here: # (one to get the correct tree_id, then one to insert) ConcreteModel.objects.create(name="e") # 3rd query here: self.assertTreeEqual(ConcreteModel.objects.all(), """ 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 5 - 3 0 1 2 6 - 4 0 1 2 """) # no partial rebuild necessary, as no trees were modified # (newly created tree is already okay) self.assertTreeEqual(ConcreteModel.objects.all(), """ 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 5 - 3 0 1 2 6 - 4 0 1 2 """) def test_move_node_same_tree(self): with self.assertNumQueries(10): with ConcreteModel.objects.delay_mptt_updates(): with self.assertNumQueries(2): # 1 query to ensure target fields aren't stale # 1 update query self.c.parent = self.b self.c.save() # query 3 here: self.assertTreeEqual(ConcreteModel.objects.all(), """ 1 - 1 0 1 6 2 1 1 1 2 3 3 2 1 2 3 4 4 - 2 0 1 2 5 - 3 0 1 2 """) # the remaining 7 queries are the partial rebuild. self.assertTreeEqual(ConcreteModel.objects.all(), """ 1 - 1 0 1 6 2 1 1 1 2 5 3 2 1 2 3 4 4 - 2 0 1 2 5 - 3 0 1 2 """) def test_move_node_different_tree(self): with self.assertNumQueries(12): with ConcreteModel.objects.delay_mptt_updates(): with self.assertNumQueries(2): # 2 queries here: # 1. update the node # 2. collapse old tree since it is now empty. self.d.parent = self.c self.d.save() # query 3 here: self.assertTreeEqual(ConcreteModel.objects.all(), """ 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 3 1 2 5 6 5 - 2 0 1 2 """) # the other 9 queries are the partial rebuild self.assertTreeEqual(ConcreteModel.objects.all(), """ 1 - 1 0 1 8 2 1 1 1 2 3 3 1 1 1 4 7 4 3 1 2 5 6 5 - 2 0 1 2 """) def test_move_node_to_root(self): with self.assertNumQueries(4): with ConcreteModel.objects.delay_mptt_updates(): with self.assertNumQueries(3): # 3 queries here! # 1. find the next tree_id to move to # 2. update the tree_id on all nodes to the right of that # 3. update tree fields on self.c self.c.parent = None self.c.save() # 4th query here: self.assertTreeEqual(ConcreteModel.objects.all(), """ 1 - 1 0 1 6 2 1 1 1 2 3 4 - 2 0 1 2 5 - 3 0 1 2 3 - 4 0 1 2 """) self.assertTreeEqual(ConcreteModel.objects.all(), """ 1 - 1 0 1 6 2 1 1 1 2 3 4 - 2 0 1 2 5 - 3 0 1 2 3 - 4 0 1 2 """) def test_move_root_to_child(self): with self.assertNumQueries(12): with ConcreteModel.objects.delay_mptt_updates(): with self.assertNumQueries(2): # 2 queries here: # 1. update the node # 2. collapse old tree since it is now empty. self.d.parent = self.c self.d.save() # query 3 here: self.assertTreeEqual(ConcreteModel.objects.all(), """ 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 3 1 2 5 6 5 - 2 0 1 2 """) # the remaining 9 queries are the partial rebuild. self.assertTreeEqual(ConcreteModel.objects.all(), """ 1 - 1 0 1 8 2 1 1 1 2 3 3 1 1 1 4 7 4 3 1 2 5 6 5 - 2 0 1 2 """) class OrderedInsertionDelayedUpdatesTestCase(TreeTestCase): def setUp(self): self.c = OrderedInsertion.objects.create(name="c") self.d = OrderedInsertion.objects.create(name="d", parent=self.c) self.e = OrderedInsertion.objects.create(name="e", parent=self.c) self.f = OrderedInsertion.objects.create(name="f") self.z = OrderedInsertion.objects.create(name="z") # state is now: self.assertTreeEqual(OrderedInsertion.objects.all(), """ 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 5 - 3 0 1 2 """) def test_insert_child(self): with self.assertNumQueries(12): with OrderedInsertion.objects.delay_mptt_updates(): with self.assertNumQueries(2): # 1 query here: OrderedInsertion.objects.create(name="dd", parent=self.c) # 2nd query here: self.assertTreeEqual(OrderedInsertion.objects.all(), """ 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 6 1 1 1 6 7 4 - 2 0 1 2 5 - 3 0 1 2 """) # remaining 9 queries are the partial rebuild process. self.assertTreeEqual(OrderedInsertion.objects.all(), """ 1 - 1 0 1 8 2 1 1 1 2 3 6 1 1 1 4 5 3 1 1 1 6 7 4 - 2 0 1 2 5 - 3 0 1 2 """) def test_insert_root(self): with self.assertNumQueries(4): with OrderedInsertion.objects.delay_mptt_updates(): with self.assertNumQueries(3): # 3 queries required here: # 1. get correct tree_id (delay_mptt_updates doesn't handle # root-level ordering when using ordered insertion) # 2. increment tree_id of all following trees # 3. insert the object OrderedInsertion.objects.create(name="ee") # 4th query here: self.assertTreeEqual(OrderedInsertion.objects.all(), """ 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 6 - 2 0 1 2 4 - 3 0 1 2 5 - 4 0 1 2 """) # no partial rebuild is required self.assertTreeEqual(OrderedInsertion.objects.all(), """ 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 6 - 2 0 1 2 4 - 3 0 1 2 5 - 4 0 1 2 """) def test_move_node_same_tree(self): with self.assertNumQueries(9): with OrderedInsertion.objects.delay_mptt_updates(): with self.assertNumQueries(1): # 1 update query self.e.name = 'before d' self.e.save() # query 2 here: self.assertTreeEqual(OrderedInsertion.objects.all(), """ 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 5 - 3 0 1 2 """) # the remaining 7 queries are the partial rebuild. self.assertTreeEqual(OrderedInsertion.objects.all(), """ 1 - 1 0 1 6 3 1 1 1 2 3 2 1 1 1 4 5 4 - 2 0 1 2 5 - 3 0 1 2 """) def test_move_node_different_tree(self): with self.assertNumQueries(12): with OrderedInsertion.objects.delay_mptt_updates(): with self.assertNumQueries(2): # 2 queries here: # 1. update the node # 2. collapse old tree since it is now empty. self.f.parent = self.c self.f.name = 'dd' self.f.save() # query 3 here: self.assertTreeEqual(OrderedInsertion.objects.all(), """ 1 - 1 0 1 6 2 1 1 1 2 3 4 1 1 1 2 3 3 1 1 1 4 5 5 - 2 0 1 2 """) # the remaining 9 queries are the partial rebuild self.assertTreeEqual(OrderedInsertion.objects.all(), """ 1 - 1 0 1 8 2 1 1 1 2 3 4 1 1 1 4 5 3 1 1 1 6 7 5 - 2 0 1 2 """) def test_move_node_to_root(self): with self.assertNumQueries(4): with OrderedInsertion.objects.delay_mptt_updates(): with self.assertNumQueries(3): # 3 queries here! # 1. find the next tree_id to move to # 2. update the tree_id on all nodes to the right of that # 3. update tree fields on self.c self.e.parent = None self.e.save() # query 4 here: self.assertTreeEqual(OrderedInsertion.objects.all(), """ 1 - 1 0 1 6 2 1 1 1 2 3 3 - 2 0 1 2 4 - 3 0 1 2 5 - 4 0 1 2 """) self.assertTreeEqual(OrderedInsertion.objects.all(), """ 1 - 1 0 1 6 2 1 1 1 2 3 3 - 2 0 1 2 4 - 3 0 1 2 5 - 4 0 1 2 """) def test_move_root_to_child(self): with self.assertNumQueries(12): with OrderedInsertion.objects.delay_mptt_updates(): with self.assertNumQueries(2): # 2 queries here: # 1. update the node # 2. collapse old tree since it is now empty. self.f.parent = self.e self.f.save() # query 3 here: self.assertTreeEqual(OrderedInsertion.objects.all(), """ 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 3 1 2 5 6 5 - 2 0 1 2 """) # the remaining 9 queries are the partial rebuild. self.assertTreeEqual(OrderedInsertion.objects.all(), """ 1 - 1 0 1 8 2 1 1 1 2 3 3 1 1 1 4 7 4 3 1 2 5 6 5 - 2 0 1 2 """) class ManagerTests(TreeTestCase): fixtures = ['categories.json', 'genres.json', 'persons.json'] def test_all_managers_are_different(self): # all tree managers should be different. otherwise, possible infinite recursion. seen = {} for model in apps.get_models(): if not issubclass(model, MPTTModel): continue tm = model._tree_manager if id(tm) in seen: self.fail( "Tree managers for %s and %s are the same manager" % (model.__name__, seen[id(tm)].__name__)) seen[id(tm)] = model def test_all_managers_have_correct_model(self): # all tree managers should have the correct model. for model in apps.get_models(): if not issubclass(model, MPTTModel): continue self.assertEqual(model._tree_manager.model, model) def test_base_manager_infinite_recursion(self): # repeatedly calling _base_manager should eventually return None for model in apps.get_models(): if not issubclass(model, MPTTModel): continue manager = model._tree_manager for i in range(20): manager = manager._base_manager if manager is None: break else: self.fail("Detected infinite recursion in %s._tree_manager._base_manager" % model) def test_proxy_custom_manager(self): self.assertIsInstance(SingleProxyModel._tree_manager, CustomTreeManager) self.assertIsInstance(SingleProxyModel._tree_manager._base_manager, TreeManager) self.assertIsInstance(SingleProxyModel.objects, CustomTreeManager) self.assertIsInstance(SingleProxyModel.objects._base_manager, TreeManager) def test_get_queryset_descendants(self): def get_desc_names(qs, include_self=False): desc = qs.model.objects.get_queryset_descendants( qs, include_self=include_self) return list(desc.values_list('name', flat=True).order_by('name')) qs = Category.objects.filter(Q(name='Nintendo Wii') | Q(name='PlayStation 3')) self.assertEqual( get_desc_names(qs), ['Games', 'Games', 'Hardware & Accessories', 'Hardware & Accessories'], ) self.assertEqual( get_desc_names(qs, include_self=True), ['Games', 'Games', 'Hardware & Accessories', 'Hardware & Accessories', 'Nintendo Wii', 'PlayStation 3'] ) qs = Genre.objects.filter(parent=None) self.assertEqual( get_desc_names(qs), ['2D Platformer', '3D Platformer', '4D Platformer', 'Action RPG', 'Horizontal Scrolling Shootemup', 'Platformer', 'Shootemup', 'Tactical RPG', 'Vertical Scrolling Shootemup'] ) self.assertEqual( get_desc_names(qs, include_self=True), ['2D Platformer', '3D Platformer', '4D Platformer', 'Action', 'Action RPG', 'Horizontal Scrolling Shootemup', 'Platformer', 'Role-playing Game', 'Shootemup', 'Tactical RPG', '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): qs = Category.objects.filter(Q(name='Nintendo Wii') | Q(name='PlayStation 3')) self.assertEqual( self._get_anc_names(qs), ['PC & Video Games'] ) self.assertEqual( 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']) 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')) # Check that empty querysets get custom methods self.assertTrue(hasattr(Person.objects.all()[0].get_children().none(), 'custom_method')) self.assertEqual( type(Person.objects.all()), type(Person.objects.root_nodes()) ) 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) def test_num_queries_on_get_queryset_descendants(self): """ Test the number of queries to access descendants is not O(n). At the moment it is O(1)+1. Ideally we should aim for O(1). """ with self.assertNumQueries(2): qs = Category.objects.get_queryset_descendants( Category.objects.all(), include_self=True) self.assertEqual(len(qs), 10) class CacheTreeChildrenTestCase(TreeTestCase): """ Tests for the ``cache_tree_children`` template filter. """ fixtures = ['categories.json'] def test_cache_tree_children_caches_parents(self): """ Ensures that each node's parent is cached by ``cache_tree_children``. """ # Ensure only 1 query is used during this test with self.assertNumQueries(1): roots = cache_tree_children(Category.objects.all()) games = roots[0] wii = games.get_children()[0] wii_games = wii.get_children()[0] # Ensure that ``wii`` is cached as ``parent`` on ``wii_games``, and # likewise for ``games`` being ``parent`` on the attached ``wii`` self.assertEqual(wii, wii_games.parent) self.assertEqual(games, wii_games.parent.parent) def test_cache_tree_children_with_invalid_ordering(self): """ Ensures that ``cache_tree_children`` fails with a ``ValueError`` when passed a list which is not in tree order. """ with self.assertNumQueries(1): with self.assertRaises(ValueError): cache_tree_children(list(Category.objects.order_by('-id'))) # Passing a list with correct ordering should work, though. with self.assertNumQueries(1): cache_tree_children(list(Category.objects.all())) # The exact ordering tuple doesn't matter, long as the nodes end up in depth-first order. cache_tree_children(Category.objects.order_by('tree_id', 'lft', 'name')) cache_tree_children(Category.objects.filter(tree_id=1).order_by('lft')) class RecurseTreeTestCase(TreeTestCase): """ Tests for the ``recursetree`` template filter. """ fixtures = ['categories.json'] template = re.sub(r'(?m)^[\s]+', '', ''' {% load mptt_tags %}
    {% recursetree nodes %}
  • {{ node.name }} {% if not node.is_leaf_node %}
      {{ children }}
    {% endif %}
  • {% endrecursetree %}
''') def test_leaf_html(self): html = Template(self.template).render(Context({ 'nodes': Category.objects.filter(pk=10), })).replace('\n', '') self.assertEqual(html, '
  • Hardware & Accessories
') def test_nonleaf_html(self): qs = Category.objects.get(pk=8).get_descendants(include_self=True) html = Template(self.template).render(Context({ 'nodes': qs, })).replace('\n', '') self.assertEqual(html, ( '
  • PlayStation 3
      ' '
    • Games
    • Hardware & Accessories
' )) def test_parsing_fail(self): self.assertRaises( TemplateSyntaxError, Template, '{% load mptt_tags %}{% recursetree %}{% endrecursetree %}') def test_cached_ancestors(self): template = Template(''' {% load mptt_tags %} {% recursetree nodes %} {{ node.get_ancestors|join:" > " }} {{ node.name }} {% if not node.is_leaf_node %} {{ children }} {% endif %} {% endrecursetree %} ''') with self.assertNumQueries(1): qs = Category.objects.all() template.render(Context({'nodes': qs})) class TreeInfoTestCase(TreeTestCase): fixtures = ['genres.json'] template = re.sub(r'(?m)^[\s]+', '', ''' {% load mptt_tags %} {% for node, structure in nodes|tree_info %} {% if structure.new_level %}
  • {% else %}
  • {% endif %} {{ node.pk }} {% for level in structure.closed_levels %}
{% endfor %} {% endfor %}''') template_with_ancestors = re.sub(r'(?m)^[\s]+', '', ''' {% load mptt_tags %} {% for node, structure in nodes|tree_info:"ancestors" %} {% if structure.new_level %}
  • {% else %}
  • {% endif %} {{ node.pk }} {% for ancestor in structure.ancestors %} {% if forloop.first %}A:{% endif %} {{ ancestor }}{% if not forloop.last %},{% endif %} {% endfor %} {% for level in structure.closed_levels %}
{% endfor %} {% endfor %}''') def test_tree_info_html(self): html = Template(self.template).render(Context({ 'nodes': Genre.objects.all(), })).replace('\n', '') self.assertEqual( html, '
  • 1
    • 2
      • 3
      • 4
      • 5
    • ' '
    • 6
      • 7
      • 8
  • 9
      ' '
    • 10
    • 11
') html = Template(self.template).render(Context({ 'nodes': Genre.objects.filter(**{ '%s__gte' % Genre._mptt_meta.level_attr: 1, '%s__lte' % Genre._mptt_meta.level_attr: 2, }), })).replace('\n', '') self.assertEqual( html, '
  • 2
    • 3
    • 4
    • 5
  • 6
      ' '
    • 7
    • 8
  • 10
  • 11
') html = Template(self.template_with_ancestors).render(Context({ 'nodes': Genre.objects.filter(**{ '%s__gte' % Genre._mptt_meta.level_attr: 1, '%s__lte' % Genre._mptt_meta.level_attr: 2, }), })).replace('\n', '') self.assertEqual( html, '
  • 2
    • 3A:Platformer
    • 4A:Platformer
    • ' '
    • 5A:Platformer
  • 6
    • 7A:Shootemup
    • ' '
    • 8A:Shootemup
  • 10
  • 11
') class FullTreeTestCase(TreeTestCase): fixtures = ['genres.json'] template = re.sub(r'(?m)^[\s]+', '', ''' {% load mptt_tags %} {% full_tree_for_model myapp.Genre as tree %} {% for node in tree %}{{ node.pk }},{% endfor %} ''') def test_full_tree_html(self): html = Template(self.template).render(Context({})).replace('\n', '') self.assertEqual( html, '1,2,3,4,5,6,7,8,9,10,11,') class DrilldownTreeTestCase(TreeTestCase): fixtures = ['genres.json'] template = re.sub(r'(?m)^[\s]+', '', ''' {% load mptt_tags %} {% drilldown_tree_for_node node as tree count myapp.Game.genre in game_count %} {% for n in tree %} {% ifequal n node %}[{% endifequal %} {{ n.pk }}:{{ n.game_count }} {% ifequal n node %}]{% endifequal %}{% if not forloop.last %},{% endif %} {% endfor %} ''') def render_for_node(self, pk, cumulative=False, m2m=False): template = self.template if cumulative: template = template.replace(' count ', ' cumulative count ') if m2m: template = template.replace('Game.genre', 'Game.genres_m2m') return Template(template).render(Context({ 'node': Genre.objects.get(pk=pk), })).replace('\n', '') def test_drilldown_html(self): for idx, genre in enumerate(Genre.objects.all()): for i in range(idx): game = genre.game_set.create(name='Game %s' % i) genre.games_m2m.add(game) self.assertEqual( self.render_for_node(1), '[1:],2:1,6:5') self.assertEqual( self.render_for_node(2), '1:,[2:],3:2,4:3,5:4') self.assertEqual( self.render_for_node(1, cumulative=True), '[1:],2:10,6:18') self.assertEqual( self.render_for_node(2, cumulative=True), '1:,[2:],3:2,4:3,5:4') self.assertEqual( self.render_for_node(1, m2m=True), '[1:],2:1,6:5') self.assertEqual( self.render_for_node(2, m2m=True), '1:,[2:],3:2,4:3,5:4') self.assertEqual( self.render_for_node(1, cumulative=True, m2m=True), '[1:],2:10,6:18') self.assertEqual( self.render_for_node(2, cumulative=True, m2m=True), '1:,[2:],3:2,4:3,5:4') class TestAutoNowDateFieldModel(TreeTestCase): # https://github.com/django-mptt/django-mptt/issues/175 def test_save_auto_now_date_field_model(self): a = AutoNowDateFieldModel() a.save() class RegisteredRemoteModel(TreeTestCase): def test_save_registered_model(self): g1 = Group.objects.create(name='group 1') g1.save() class TestForms(TreeTestCase): fixtures = ['categories.json'] def test_adminform_instantiation(self): # https://github.com/django-mptt/django-mptt/issues/264 c = Category.objects.get(name='Nintendo Wii') CategoryForm = modelform_factory( Category, form=MPTTAdminForm, fields=('name', 'parent'), ) self.assertTrue(CategoryForm(instance=c)) # Test that the parent field is properly limited. (queryset) form = CategoryForm({ 'name': c.name, 'parent': c.children.all()[0].pk, }, instance=c) self.assertFalse(form.is_valid()) self.assertIn( 'Select a valid choice', '%s' % form.errors) # Test that even though we remove the field queryset limit, # validation still fails. form = CategoryForm({ 'name': c.name, 'parent': c.children.all()[0].pk, }, instance=c) form.fields['parent'].queryset = Category.objects.all() self.assertFalse(form.is_valid()) self.assertIn( 'Invalid parent', '%s' % form.errors) def test_field_types(self): ReferencingModelForm = modelform_factory( ReferencingModel, exclude=('id',)) form = ReferencingModelForm() # Also check whether we have the correct form field type self.assertTrue(isinstance( form.fields['fk'], TreeNodeChoiceField)) self.assertTrue(isinstance( form.fields['one'], TreeNodeChoiceField)) self.assertTrue(isinstance( form.fields['m2m'], TreeNodeMultipleChoiceField)) def test_movenodeform(self): c = Category.objects.get(pk=2) form = MoveNodeForm(c, { 'target': '5', 'position': 'first-child', }) self.assertTrue(form.is_valid()) form.save() self.assertTreeEqual(Category.objects.all(), ''' 1 - 1 0 1 20 5 1 1 1 2 13 2 5 1 2 3 8 3 2 1 3 4 5 4 2 1 3 6 7 6 5 1 2 9 10 7 5 1 2 11 12 8 1 1 1 14 19 9 8 1 2 15 16 10 8 1 2 17 18 ''') class TestAltersData(TreeTestCase): def test_alters_data(self): node = Node() output = Template('{{ node.save }}').render(Context({ 'node': node, })) self.assertEqual(output, '') self.assertEqual(node.pk, None) node.save() self.assertNotEqual(node.pk, None) output = Template('{{ node.delete }}').render(Context({ 'node': node, })) self.assertEqual(node, Node.objects.get(pk=node.pk)) class TestDebugInfo(TreeTestCase): fixtures = ['categories.json'] def test_debug_info(self): # Currently fails either on PY2 or PY3. stream_type = io.StringIO if PY3 else io.BytesIO with stream_type() as out: print_debug_info(Category.objects.all(), file=out) output = out.getvalue() self.assertIn('1,0,,1,1,20', output) class AdminBatch(TreeTestCase): fixtures = ['categories.json'] def test_changelist(self): user = User.objects.create_superuser('admin', 'test@example.com', 'p') self.client.login(username=user.username, password='p') response = self.client.get('/admin/myapp/category/') self.assertContains( response, 'name="_selected_action"', 10) mptt_opts = Category._mptt_meta self.assertEqual( response.context['cl'].result_list.query.order_by[:2], [mptt_opts.tree_id_attr, mptt_opts.left_attr]) data = { 'action': 'delete_selected', '_selected_action': ['5', '8', '9'], } response = self.client.post('/admin/myapp/category/', data) self.assertContains(response, 'value="Yes, I\'m sure"', 1) data['post'] = 'yes' response = self.client.post('/admin/myapp/category/', data) self.assertRedirects( response, '/admin/myapp/category/') self.assertEqual(Category.objects.count(), 4) # Batch deletion has not clobbered MPTT values, because our method # delete_selected_tree has been used. self.assertTreeEqual(Category.objects.all(), ''' 1 - 1 0 1 8 2 1 1 1 2 7 3 2 1 2 3 4 4 2 1 2 5 6 ''') class TestUnsaved(TreeTestCase): def test_unsaved(self): for method in [ 'get_ancestors', 'get_family', 'get_children', 'get_descendants', 'get_leafnodes', 'get_next_sibling', 'get_previous_sibling', 'get_root', 'get_siblings', ]: assertRaisesRegex( self, ValueError, 'Cannot call %s on unsaved Genre instances' % method, getattr(Genre(), method)) class QuerySetTests(TreeTestCase): fixtures = ['categories.json'] def test_get_ancestors(self): self.assertEqual( [ c.pk for c in Category.objects.get(name="Nintendo Wii").get_ancestors(include_self=False)], [ c.pk for c in Category.objects.filter(name="Nintendo Wii").get_ancestors(include_self=False)], ) self.assertEqual( [ c.pk for c in Category.objects.get(name="Nintendo Wii").get_ancestors(include_self=True)], [ c.pk for c in Category.objects.filter(name="Nintendo Wii").get_ancestors(include_self=True)], ) def test_get_descendants(self): self.assertEqual( [ c.pk for c in Category.objects.get(name="Nintendo Wii").get_descendants(include_self=False)], [ c.pk for c in Category.objects.filter(name="Nintendo Wii").get_descendants(include_self=False)], ) self.assertEqual( [ c.pk for c in Category.objects.get(name="Nintendo Wii").get_descendants(include_self=True)], [ c.pk for c in Category.objects.filter(name="Nintendo Wii").get_descendants(include_self=True)], ) class TreeManagerTestCase(TreeTestCase): fixtures = ['categories.json', 'items.json'] def test_add_related_count_with_fk_to_natural_key(self): # Regression test for #284 queryset = Category.objects.filter(name='Xbox 360').order_by('id') # Test using FK that doesn't point to a primary key for c in Category.objects.add_related_count( queryset, Item, 'category_fk', 'item_count', cumulative=False): self.assertEqual(c.item_count, c.items_by_pk.count()) # Also works when using the FK that *does* point to a primary key for c in Category.objects.add_related_count( queryset, Item, 'category_pk', 'item_count', cumulative=False): self.assertEqual(c.item_count, c.items_by_pk.count()) class TestOrderedInsertionBFS(TreeTestCase): def test_insert_ordered_DFS_backwards_root_nodes(self): rock = OrderedInsertion.objects.create(name="Rock") OrderedInsertion.objects.create(name="Led Zeppelin", parent=rock) OrderedInsertion.objects.create(name="Classical") self.assertTreeEqual(OrderedInsertion.objects.all(), """ 3 - 1 0 1 2 1 - 2 0 1 4 2 1 2 1 2 3 """) def test_insert_ordered_BFS_backwards_root_nodes(self): rock = OrderedInsertion.objects.create(name="Rock") self.assertTreeEqual(OrderedInsertion.objects.all(), """ 1 - 1 0 1 2 """) OrderedInsertion.objects.create(name="Classical") self.assertTreeEqual(OrderedInsertion.objects.all(), """ 2 - 1 0 1 2 1 - 2 0 1 2 """) # This tends to fail if it uses `rock.tree_id`, which is 1, although # in the database Rock's tree_id has been updated to 2. OrderedInsertion.objects.create(name="Led Zeppelin", parent=rock) self.assertTreeEqual(OrderedInsertion.objects.all(), """ 2 - 1 0 1 2 1 - 2 0 1 4 3 1 2 1 2 3 """) def test_insert_ordered_DFS_backwards_nonroot_nodes(self): music = OrderedInsertion.objects.create(name='music') rock = OrderedInsertion.objects.create(name="Rock", parent=music) OrderedInsertion.objects.create(name="Led Zeppelin", parent=rock) OrderedInsertion.objects.create(name="Classical", parent=music) self.assertTreeEqual(OrderedInsertion.objects.all(), """ 1 - 1 0 1 8 4 1 1 1 2 3 2 1 1 1 4 7 3 2 1 2 5 6 """) def test_insert_ordered_BFS_backwards_nonroot_nodes(self): music = OrderedInsertion.objects.create(name='music') rock = OrderedInsertion.objects.create(name="Rock", parent=music) OrderedInsertion.objects.create(name="Classical", parent=music) self.assertTreeEqual(OrderedInsertion.objects.all(), """ 1 - 1 0 1 6 3 1 1 1 2 3 2 1 1 1 4 5 """) OrderedInsertion.objects.create(name="Led Zeppelin", parent=rock) self.assertTreeEqual(OrderedInsertion.objects.all(), """ 1 - 1 0 1 8 3 1 1 1 2 3 2 1 1 1 4 7 4 2 1 2 5 6 """) class CacheChildrenTestCase(TreeTestCase): """ Tests that the queryset function `get_cached_trees` results in a minimum number of database queries. """ fixtures = ['genres.json'] def test_genre_iter(self): """ Test a query with two root nodes. """ with self.assertNumQueries(1): root_nodes = Genre.objects.all().get_cached_trees() # `get_cached_trees` should only return the root nodes self.assertEqual(len(root_nodes), 2) # Getting the children of each node should not result in db hits. with self.assertNumQueries(0): for genre in root_nodes: self.assertIsInstance(genre, Genre) for child in genre.get_children(): self.assertIsInstance(child, Genre) for child2 in child.get_children(): self.assertIsInstance(child2, Genre) @unittest.skipUnless(mock_signal_receiver, "Signals tests require mock_django installed") class Signals(TestCase): fixtures = ['categories.json'] def setUp(self): self.signal = node_moved self.wii = Category.objects.get(pk=2) self.ps3 = Category.objects.get(pk=8) def test_signal_should_not_be_sent_when_parent_hasnt_changed(self): with mock_signal_receiver(self.signal, sender=Category) as receiver: self.wii.name = 'Woo' self.wii.save() self.assertEqual(receiver.call_count, 0) def test_signal_should_not_be_sent_when_model_created(self): with mock_signal_receiver(self.signal, sender=Category) as receiver: Category.objects.create(name='Descriptive name') self.assertEqual(receiver.call_count, 0) def test_move_by_using_move_to_should_send_signal(self): with mock_signal_receiver(self.signal, sender=Category) as receiver: self.wii.move_to(self.ps3) receiver.assert_called_once_with( instance=self.wii, signal=self.signal, target=self.ps3, sender=Category, position='first-child' ) def test_move_by_changing_parent_should_send_signal(self): '''position is not set when sent from save(). I assume it would be the default(first-child) but didn't feel comfortable setting it. ''' with mock_signal_receiver(self.signal, sender=Category) as receiver: self.wii.parent = self.ps3 self.wii.save() receiver.assert_called_once_with( instance=self.wii, signal=self.signal, target=self.ps3, sender=Category ) class DeferredAttributeTests(TreeTestCase): """ Regression tests for #176 and #424 """ def setUp(self): OrderedInsertion.objects.create(name="a") def test_deferred_order_insertion_by(self): qs = OrderedInsertion.objects.defer('name') with self.assertNumQueries(1): nodes = list(qs) with self.assertNumQueries(0): self.assertTreeEqual(nodes, ''' 1 - 1 0 1 2 ''') def test_deferred_cached_field_undeferred(self): obj = OrderedInsertion.objects.defer('name').get() self.assertEqual(obj._mptt_cached_fields['name'], DeferredAttribute) with self.assertNumQueries(1): obj.name with self.assertNumQueries(3): # does a node move, since the order_insertion_by field changed obj.save() self.assertEqual(obj._mptt_cached_fields['name'], 'a') def test_deferred_cached_field_change(self): obj = OrderedInsertion.objects.defer('name').get() self.assertEqual(obj._mptt_cached_fields['name'], DeferredAttribute) with self.assertNumQueries(0): obj.name = 'b' with self.assertNumQueries(3): # does a node move, since the order_insertion_by field changed obj.save() self.assertEqual(obj._mptt_cached_fields['name'], 'b') django-mptt-0.8.0/tests/myapp/tests.pyc0000644000076500000240000023267112635626177021424 0ustar cdestigterstaff00000000000000 +wVcsddlmZddlZddlZddlZddlZddlZddlZddlm Z m Z ddl m Z ddl mZddlmZddlmZddlmZmZmZdd lmZdd lmZmZmZmZydd lmZWne k r)dZnXdd l!m"Z"m#Z#dd l$m%Z%m&Z&m'Z'm(Z(ddl)m*Z*ddl+m,Z,ddl-m.Z.ddl/m0Z0ddl1m2Z2ddl3m4Z4m5Z5m6Z6m7Z7m8Z8m9Z9m:Z:m;Z;m<Z<m=Z=m>Z>m?Z?m@Z@mAZAdZBeCdejDZEdZFdefdYZGdeGfdYZHdeGfdYZIdeGfdYZJdeGfd YZKd!eGfd"YZLd#eGfd$YZMd%eGfd&YZNd'eGfd(YZOd)eGfd*YZPd+eGfd,YZQd-eGfd.YZRd/eGfd0YZSd1eGfd2YZTd3eGfd4YZUd5eGfd6YZVd7eGfd8YZWd9eGfd:YZXd;eGfd<YZYd=eGfd>YZZd?eGfd@YZ[dAeGfdBYZ\dCeGfdDYZ]dEeGfdFYZ^dGeGfdHYZ_dIeGfdJYZ`dKeGfdLYZadMeGfdNYZbdOeGfdPYZcededQdRefdSYZedTeGfdUYZfdS(Vi(sunicode_literalsN(sGroupsUser(sQ(sDeferredAttribute(sapps(smodelform_factory(sTemplatesTemplateSyntaxErrorsContext(sTestCase(s string_typessPY3sbsassertRaisesRegex(smock_signal_receiver(sCantDisableUpdatess InvalidMove(s MPTTAdminFormsTreeNodeChoiceFieldsTreeNodeMultipleChoiceFields MoveNodeForm(s MPTTModel(s TreeManager(s node_moved(scache_tree_children(sprint_debug_info(sCategorysItemsGenres CustomPKNamesSingleProxyModelsDoubleProxyModels ConcreteModelsOrderedInsertionsAutoNowDateFieldModelsPersonsCustomTreeQuerysetsNodesReferencingModelsCustomTreeManagercCst|dr-t|ddd}nt|}|dj}d|D]k}d|jt|d|jp{d t||jt||j t||j t||j f^qSS( u Creates pertinent tree details for the given list of nodes. The fields are: id parent_id tree_id level left right uorder_byutree_idulftupkiu u%s %s %s %s %s %su%s_idu-( thasattrtlisttorder_byt _mptt_metatjointpktgetattrt parent_attrt tree_id_attrt level_attrt left_attrt right_attr(tnodestoptstn((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pytget_tree_details)s   u^\s+cCstd|S(u Trims leading whitespace from the given text specifying tree details so triple-quoted strings can be used to provide tree details in a readable format (says who?), to be compared with the result of using the ``get_tree_details`` function. u(tleading_whitespace_retsubtrstrip(ttext((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyt tree_details<ss TreeTestCasecseZdZRS(cCsqt|tst|}nt|}t|tsHt|}nt|}|||d||fS(Nu %r != %r(t isinstancet string_typestget_tree_detailst tree_detailst assertEqual(tselfttree1ttree2((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pytassertTreeEqualGs  (t__name__t __module__tassertTreeEqual(((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyt TreeTestCaseFssDocTestTestCasecseZdZRS(c Cs2dfdY}|}tj}|t_ttjtjtd}t }| }t rt dd|}|t|n |||ddl}||jdtd|jd d |t_|j}|r"||d |nWdQXWdQXdS( Ns DummyStreamcs&eZdZdZdZdZRS(uuutf8cCs|j|7_dS(N(tcontent(tselfttext((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pytwriteWscCsdS(N((tself((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pytflushZs(t__name__t __module__tcontenttencodingtwritetflush(((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyt DummyStreamSs u doctests.txtu \bu(["\'])u\1ismodule_relatives optionflagssencodinguutf-8u (tsyststdouttopentostpathtjointdirnamet__file__ttempfiletNamedTemporaryFiletreadtPY3tretsubtwritetbtflushtdoctestttestfiletnametFalsetIGNORE_EXCEPTION_DETAILtcontenttfail( tselft DummyStreamt dummy_streamtbeforetfttempttexttdoctesttcontent((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_run_doctestRs.   *        (t__name__t __module__ttest_run_doctest(((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pytDocTestTestCaseQssReparentingTestCasecsheZdZdgZdZdZdZdZdZdZ dZ d Z d Z RS( u Test that trees are in the appropriate state after reparenting and that reparented items have the correct tree attributes defined, should they be required for use after a save. u genres.jsoncCsXtjdd}d|_|||gd|tjddS(Nsidiu 6 - 3 0 1 6u 1 - 1 0 1 10 2 1 1 1 2 9 3 2 1 2 3 4 4 2 1 2 5 6 5 2 1 2 7 8 9 - 2 0 1 6 10 9 2 1 2 3 11 9 2 1 4 5 6 - 3 0 1 6 7 6 3 1 2 3 8 6 3 1 4 5 (tGenretobjectstgettparenttsavetassertTreeEqualtall(tselftshmup((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_new_root_from_subtrees   cCsXtjdd}d|_|||gd|tjddS(Nsidiu 3 - 3 0 1 2u 1 - 1 0 1 14 2 1 1 1 2 7 4 2 1 2 3 4 5 2 1 2 5 6 6 1 1 1 8 13 7 6 1 2 9 10 8 6 1 2 11 12 9 - 2 0 1 6 10 9 2 1 2 3 11 9 2 1 4 5 3 - 3 0 1 2 (tGenretobjectstgettparenttsavetassertTreeEqualtall(tselft platformer_2d((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyt%test_new_root_from_leaf_with_siblingss   cCstjdd}tjdd}||_|||gd||gd|tjddS(Nsidii u 1 9 2 1 6 21u 9 - 2 0 1 22u  9 - 2 0 1 22 10 9 2 1 2 3 11 9 2 1 4 5 1 9 2 1 6 21 2 1 2 2 7 14 3 2 2 3 8 9 4 2 2 3 10 11 5 2 2 3 12 13 6 1 2 2 15 20 7 6 2 3 16 17 8 6 2 3 18 19 (tGenretobjectstgettparenttsavetassertTreeEqualtall(tselftactiontrpg((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_new_child_from_roots  cCstjdd}tjdd}||_|||gd||gd|tjddS(Nsidii u 8 9 2 1 6 7u 9 - 2 0 1 8u 1 - 1 0 1 14 2 1 1 1 2 9 3 2 1 2 3 4 4 2 1 2 5 6 5 2 1 2 7 8 6 1 1 1 10 13 7 6 1 2 11 12 9 - 2 0 1 8 10 9 2 1 2 3 11 9 2 1 4 5 8 9 2 1 6 7 (tGenretobjectstgettparenttsavetassertTreeEqualtall(tselftshmup_horizontaltrpg((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_move_leaf_to_other_trees  cCstjdd}tjdd}||_|||gd||gd|tjddS(Nsidii u 6 11 2 2 5 10u 11 9 2 1 4 11u 1 - 1 0 1 10 2 1 1 1 2 9 3 2 1 2 3 4 4 2 1 2 5 6 5 2 1 2 7 8 9 - 2 0 1 12 10 9 2 1 2 3 11 9 2 1 4 11 6 11 2 2 5 10 7 6 2 3 6 7 8 6 2 3 8 9 (tGenretobjectstgettparenttsavetassertTreeEqualtall(tselftshmupttrpg((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_move_subtree_to_other_trees  cCstjdd}tjdd}||_|||gd||gd|tjddS(Nsidiiu 8 1 1 1 14 15u 1 - 1 0 1 16u 1 - 1 0 1 16 2 1 1 1 2 9 3 2 1 2 3 4 4 2 1 2 5 6 5 2 1 2 7 8 6 1 1 1 10 13 7 6 1 2 11 12 8 1 1 1 14 15 9 - 2 0 1 6 10 9 2 1 2 3 11 9 2 1 4 5 (tGenretobjectstgettparenttsavetassertTreeEqualtall(tselftshmup_horizontaltaction((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_move_child_up_levels  cCstjdd}tjdd}||_|||gd||gd|tjddS(Nsidiiu 6 2 1 2 9 14u 2 1 1 1 2 15u 1 - 1 0 1 16 2 1 1 1 2 15 3 2 1 2 3 4 4 2 1 2 5 6 5 2 1 2 7 8 6 2 1 2 9 14 7 6 1 3 10 11 8 6 1 3 12 13 9 - 2 0 1 6 10 9 2 1 2 3 11 9 2 1 4 5 (tGenretobjectstgettparenttsavetassertTreeEqualtall(tselftshmupt platformer((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_move_subtree_down_levels  cCsXtjdd}tjdd}|||||j|dS(Nspki i(tGenretobjectstgettmove_totsavet assertEqualtparent(tselftrpgtaction((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyt test_move_to#s   cCstjdd}||_tjdd}||_|t|j|t|jtjdd}||_||_|t|j|t|j||j|||j|dS(Nsidiii(tGenretobjectstgettparentt assertRaisest InvalidMovetsavet assertEqual(tselftactiont platformert platformer_4d((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_invalid_moves*s    ( t__name__t __module__t__doc__tfixturesttest_new_root_from_subtreet%test_new_root_from_leaf_with_siblingsttest_new_child_from_rootttest_move_leaf_to_other_treettest_move_subtree_to_other_treettest_move_child_up_levelttest_move_subtree_down_levelt test_move_tottest_invalid_moves(((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pytReparentingTestCases         sConcurrencyTestCasecsDeZdZdZdZdZdZdZdZRS(u Test that tree structure remains intact when saving nodes (without setting new parent) after tree structure has been changed. cCstjdd}tjdd}tjddd|tjddd|tjddd|tjddd||tjd dS( NsnameuFruituVegieuApplesparentuPearuTomatouCarrotu 1 - 1 0 1 6 3 1 1 1 2 3 4 1 1 1 4 5 2 - 2 0 1 6 5 2 2 1 2 3 6 2 2 1 4 5 (t ConcreteModeltobjectstcreatetassertTreeEqualtall(tselftfruittvegie((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pytsetUpDscCs;tjdd}tjdd}||dS(NsnameuFruituVegie(t ConcreteModeltobjectstgettmove_to(tselftfruittvegie((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyt _modify_treeVscCsEtjdd}||gd|tjddS(Nsidiu 6 2 1 2 5 6u 1 - 1 0 1 12 2 1 1 1 2 7 5 2 1 2 3 4 6 2 1 2 5 6 3 1 1 1 8 9 4 1 1 1 10 11 (t ConcreteModeltobjectstgettassertTreeEqualtall(tselftcarrot((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyt_assert_modified_tree_state[scCs@tjdd}|d|_||dS(Nsidiu Purple carrot(t ConcreteModeltobjectstgett _modify_treetnametsavet_assert_modified_tree_state(tselftcarrot((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyt'test_node_save_after_tree_restructuringgs    cCstjdd}|d|_d|_|ddg|tjdd}||j|j| |j|jd|_d|_|t t d d gtjdd}| |j|j||j|jd S( uN Test that model is saved properly when passing update_fields sidiu Won't changeuWill get updateds update_fieldsughostsu Will changeuWill not be updatedNuname( t ConcreteModeltobjectstgett _modify_treetnametghoststsavet_assert_modified_tree_statet assertEqualtassertNotEqualtFalse(tselftcarrottupdated_carrot((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyt:test_node_save_after_tree_restructuring_with_update_fieldsqs      cCs/tjdd}|ttdddS(u Test that update_fields works as a positional argument Test for https://github.com/django-mptt/django-mptt/issues/384 sidiN(t ConcreteModeltobjectstgettsavetFalse(tselftcarrot((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_update_fields_positionals( t__name__t __module__t__doc__tsetUpt _modify_treet_assert_modified_tree_statet'test_node_save_after_tree_restructuringt:test_node_save_after_tree_restructuring_with_update_fieldsttest_update_fields_positional(((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pytConcurrencyTestCase?s   sDeletionTestCasecsDeZdZdgZdZdZdZdZdZRS(uf Tests that the tree structure is maintained appropriately in various deletion scenarios. ucategories.jsoncCstddtjddddttddtjddddt|tjd tjdd|tjd dS( NsnameuPreceding rootsidiuleftssaveuFollowing rooturightu7 11 - 1 0 1 2 1 - 2 0 1 20 2 1 2 1 2 7 3 2 2 2 3 4 4 2 2 2 5 6 5 1 2 1 8 13 6 5 2 2 9 10 7 5 2 2 11 12 8 1 2 1 14 19 9 8 2 2 15 16 10 8 2 2 17 18 12 - 3 0 1 2 u; 11 - 1 0 1 2 12 - 3 0 1 2 (tCategoryt insert_attobjectstgettTruetassertTreeEqualtalltdelete(tself((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_delete_root_nodes! ! cCs6tjdd|tjddS(Nsidi u 1 - 1 0 1 18 2 1 1 1 2 7 3 2 1 2 3 4 4 2 1 2 5 6 5 1 1 1 8 13 6 5 1 2 9 10 7 5 1 2 11 12 8 1 1 1 14 17 10 8 1 2 15 16 (tCategorytobjectstgettdeletetassertTreeEqualtall(tself((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyt#test_delete_last_node_with_siblingsscCs6tjdd|tjddS(Nsidiu 1 - 1 0 1 14 2 1 1 1 2 7 3 2 1 2 3 4 4 2 1 2 5 6 5 1 1 1 8 13 6 5 1 2 9 10 7 5 1 2 11 12 (tCategorytobjectstgettdeletetassertTreeEqualtall(tself((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyt&test_delete_last_node_with_descendantsscCstjdd}|j}||d||tjd||dtjd|j }||ddS(Nsidiiu 1 - 1 0 1 18 2 1 1 1 2 7 3 2 1 2 3 4 4 2 1 2 5 6 5 1 1 1 8 11 7 5 1 2 9 10 8 1 1 1 12 17 9 8 1 2 13 14 10 8 1 2 15 16 ispk( tCategorytobjectstgettparentt assertEqualtget_descendant_counttdeletetassertTreeEqualtalltpk(tselftchildtparent((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_delete_node_with_siblingss   cCs6tjdd|tjddS(u Regression test for Issue 23 - we used to use pre_delete, which resulted in tree cleanup being performed for every node being deleted, rather than just the node on which ``delete()`` was called. sidiu 1 - 1 0 1 14 2 1 1 1 2 7 3 2 1 2 3 4 4 2 1 2 5 6 8 1 1 1 8 13 9 8 1 2 9 10 10 8 1 2 11 12 N(tCategorytobjectstgettdeletetassertTreeEqualtall(tself((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyt.test_delete_node_with_descendants_and_siblingss( t__name__t __module__t__doc__tfixturesttest_delete_root_nodet#test_delete_last_node_with_siblingst&test_delete_last_node_with_descendantsttest_delete_node_with_siblingst.test_delete_node_with_descendants_and_siblings(((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pytDeletionTestCases    sIntraTreeMovementTestCasecseZRS((t__name__t __module__(((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pytIntraTreeMovementTestCase ssInterTreeMovementTestCasecseZRS((t__name__t __module__(((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pytInterTreeMovementTestCasessPositionedInsertionTestCasecseZRS((t__name__t __module__(((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pytPositionedInsertionTestCasessCustomPKNameTestCasecseZdZdZRS(cCstj}|dd}|ddd||ddd||dd}|ddd||ddd||dd dS( Nsnameuc1uc11sparentuc12uc2uc21uc22uc3(t CustomPKNametobjectstcreate(tselftmanagertc1tc2((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pytsetUps cCs8tjdd}|}||dkdS(Nsnameuc12(t CustomPKNametobjectstgettget_next_siblingt assertTrue(tselftroottsib((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_get_next_sibling&s (t__name__t __module__tsetUpttest_get_next_sibling(((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pytCustomPKNameTestCases sDisabledUpdatesTestCasecsYeZdZdZdZdZdZdZdZdZ dZ RS( cCstjdd|_tjddd|j|_tjddd|j|_tjdd|_|tjddS(Nsnameuaubsparentucudui 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 ( t ConcreteModeltobjectstcreatetatbtctdtassertTreeEqualtall(tself((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pytsetUp-s !!cCs|tj|tj|ttjj|tj|tjtj%| tj| tjWdQX|tj|tjdS(N( t assertTruet ConcreteModelt_mptt_updates_enabledtSingleProxyModelt assertRaisestCantDisableUpdatestobjectstdisable_mptt_updatest __enter__t assertFalse(tself((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_single_proxy:scCs|tj|tj|ttjj|tj|tjtj%| tj| tjWdQX|tj|tjdS(N( t assertTruet ConcreteModelt_mptt_updates_enabledtDoubleProxyModelt assertRaisestCantDisableUpdatestobjectstdisable_mptt_updatest __enter__t assertFalse(tself((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_double_proxyLsc Cs|dftjP|d!tjddd|jWdQX|tjdWdQXWdQX|tjddS(Niisnameuesparentu 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 5 4 2 1 2 3 u 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 5 4 2 1 2 3 (tassertNumQueriest ConcreteModeltobjectstdisable_mptt_updatestcreatetdtassertTreeEqualtall(tself((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_insert_child^s "% c Cs|d]tjG|dtjddWdQX|tjdWdQXWdQX|tjddS(Niisnameueu 5 - 0 0 1 2 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 u 5 - 0 0 1 2 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 (tassertNumQueriest ConcreteModeltobjectstdisable_mptt_updatestcreatetassertTreeEqualtall(tself((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_insert_rootvs %c Cs|dftjP|d!|j|j_|jWdQX|tj dWdQXWdQX|tj ddS(Niiu 1 - 1 0 1 6 2 1 1 1 2 3 3 2 1 1 4 5 4 - 2 0 1 2 ui 1 - 1 0 1 6 2 1 1 1 2 3 3 2 1 1 4 5 4 - 2 0 1 2 ( tassertNumQueriest ConcreteModeltobjectstdisable_mptt_updatestbtctparenttsavetassertTreeEqualtall(tself((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_move_node_same_trees%c Cs|dftjP|d!|j|j_|jWdQX|tj dWdQXWdQX|tj ddS(Niiu 1 - 1 0 1 6 2 1 1 1 2 3 3 4 1 1 4 5 4 - 2 0 1 2 ui 1 - 1 0 1 6 2 1 1 1 2 3 3 4 1 1 4 5 4 - 2 0 1 2 ( tassertNumQueriest ConcreteModeltobjectstdisable_mptt_updatestdtctparenttsavetassertTreeEqualtall(tself((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_move_node_different_trees%c Cs|dctjM|dd|j_|jWdQX|tjdWdQXWdQX|tjddS(Niiu 1 - 1 0 1 6 2 1 1 1 2 3 3 - 1 1 4 5 4 - 2 0 1 2 ui 1 - 1 0 1 6 2 1 1 1 2 3 3 - 1 1 4 5 4 - 2 0 1 2 ( tassertNumQueriest ConcreteModeltobjectstdisable_mptt_updatestctparenttsavetassertTreeEqualtall(tself((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_move_node_to_roots %c Cs|dftjP|d!|j|j_|jWdQX|tj dWdQXWdQX|tj ddS(Niiu 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 3 2 0 1 2 ui 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 3 2 0 1 2 ( tassertNumQueriest ConcreteModeltobjectstdisable_mptt_updatestctdtparenttsavetassertTreeEqualtall(tself((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_move_root_to_childs%( t__name__t __module__tsetUpttest_single_proxyttest_double_proxyttest_insert_childttest_insert_rootttest_move_node_same_treettest_move_node_different_treettest_move_node_to_rootttest_move_root_to_child(((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pytDisabledUpdatesTestCase,s       sDelayedUpdatesTestCasecsYeZdZdZdZdZdZdZdZdZ dZ RS( cCstjdd|_tjddd|j|_tjddd|j|_tjdd|_tjdd|_|tj ddS( Nsnameuaubsparentucuduzu 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 5 - 3 0 1 2 ( t ConcreteModeltobjectstcreatetatbtctdtztassertTreeEqualtall(tself((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pytsetUps !!cCs|tj|tj|ttjj|tj|tjtj%| tj| tjWdQX|tj|tjdS(N( t assertFalset ConcreteModelt_mptt_is_trackingtSingleProxyModelt assertRaisestCantDisableUpdatestobjectstdelay_mptt_updatest __enter__t assertTrue(tself((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyt test_proxyscCsptjK|tjtj|tjWdQX|tjWdQX|tjdS(N(t ConcreteModeltobjectstdelay_mptt_updatest assertTruet_mptt_is_trackingt assertFalse(tself((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_double_context_manager s c Cs|dftjP|d!tjddd|jWdQX|tjdWdQXWdQX|tjddS(Niisnameuesparentu 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 6 4 2 1 2 3 5 - 3 0 1 2 u 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 4 6 4 2 1 2 3 5 - 3 0 1 2 (tassertNumQueriest ConcreteModeltobjectstdelay_mptt_updatestcreatetdtassertTreeEqualtall(tself((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_insert_childs "% c Cs|d]tjG|dtjddWdQX|tjdWdQXWdQX|tjddS(Niisnameueu 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 5 - 3 0 1 2 6 - 4 0 1 2 u 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 5 - 3 0 1 2 6 - 4 0 1 2 (tassertNumQueriest ConcreteModeltobjectstdelay_mptt_updatestcreatetassertTreeEqualtall(tself((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_insert_root/s % c Cs|dftjP|d!|j|j_|jWdQX|tj dWdQXWdQX|tj ddS(Ni iu 1 - 1 0 1 6 2 1 1 1 2 3 3 2 1 2 3 4 4 - 2 0 1 2 5 - 3 0 1 2 u 1 - 1 0 1 6 2 1 1 1 2 5 3 2 1 2 3 4 4 - 2 0 1 2 5 - 3 0 1 2 ( tassertNumQueriest ConcreteModeltobjectstdelay_mptt_updatestbtctparenttsavetassertTreeEqualtall(tself((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_move_node_same_treeJs% c Cs|dftjP|d!|j|j_|jWdQX|tj dWdQXWdQX|tj ddS(Ni iu 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 3 1 2 5 6 5 - 2 0 1 2 u 1 - 1 0 1 8 2 1 1 1 2 3 3 1 1 1 4 7 4 3 1 2 5 6 5 - 2 0 1 2 ( tassertNumQueriest ConcreteModeltobjectstdelay_mptt_updatestctdtparenttsavetassertTreeEqualtall(tself((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_move_node_different_treeds% c Cs|dctjM|dd|j_|jWdQX|tjdWdQXWdQX|tjddS(Niiu 1 - 1 0 1 6 2 1 1 1 2 3 4 - 2 0 1 2 5 - 3 0 1 2 3 - 4 0 1 2 u 1 - 1 0 1 6 2 1 1 1 2 3 4 - 2 0 1 2 5 - 3 0 1 2 3 - 4 0 1 2 ( tassertNumQueriest ConcreteModeltobjectstdelay_mptt_updatestctparenttsavetassertTreeEqualtall(tself((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_move_node_to_roots %c Cs|dftjP|d!|j|j_|jWdQX|tj dWdQXWdQX|tj ddS(Ni iu 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 3 1 2 5 6 5 - 2 0 1 2 u 1 - 1 0 1 8 2 1 1 1 2 3 3 1 1 1 4 7 4 3 1 2 5 6 5 - 2 0 1 2 ( tassertNumQueriest ConcreteModeltobjectstdelay_mptt_updatestctdtparenttsavetassertTreeEqualtall(tself((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_move_root_to_childs% ( t__name__t __module__tsetUpt test_proxyttest_double_context_managerttest_insert_childttest_insert_rootttest_move_node_same_treettest_move_node_different_treettest_move_node_to_rootttest_move_root_to_child(((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pytDelayedUpdatesTestCases        s&OrderedInsertionDelayedUpdatesTestCasecsGeZdZdZdZdZdZdZdZRS(cCstjdd|_tjddd|j|_tjddd|j|_tjdd|_tjdd|_|tj ddS( Nsnameucudsparentueufuzu 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 5 - 3 0 1 2 ( tOrderedInsertiontobjectstcreatetctdtetftztassertTreeEqualtall(tself((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pytsetUps !!c Cs|dftjP|d!tjddd|jWdQX|tjdWdQXWdQX|tjddS(Ni isnameuddsparentu 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 6 1 1 1 6 7 4 - 2 0 1 2 5 - 3 0 1 2 u 1 - 1 0 1 8 2 1 1 1 2 3 6 1 1 1 4 5 3 1 1 1 6 7 4 - 2 0 1 2 5 - 3 0 1 2 (tassertNumQueriestOrderedInsertiontobjectstdelay_mptt_updatestcreatetctassertTreeEqualtall(tself((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_insert_childs "% c Cs|d]tjG|dtjddWdQX|tjdWdQXWdQX|tjddS(Niisnameueeu 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 6 - 2 0 1 2 4 - 3 0 1 2 5 - 4 0 1 2 u 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 6 - 2 0 1 2 4 - 3 0 1 2 5 - 4 0 1 2 (tassertNumQueriestOrderedInsertiontobjectstdelay_mptt_updatestcreatetassertTreeEqualtall(tself((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_insert_roots % c Cs|dctjM|dd|j_|jWdQX|tjdWdQXWdQX|tjddS(Ni iubefore du 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 - 2 0 1 2 5 - 3 0 1 2 u 1 - 1 0 1 6 3 1 1 1 2 3 2 1 1 1 4 5 4 - 2 0 1 2 5 - 3 0 1 2 ( tassertNumQueriestOrderedInsertiontobjectstdelay_mptt_updatestetnametsavetassertTreeEqualtall(tself((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_move_node_same_trees % c Cs|drtj\|d-|j|j_d|j_|jWdQX| tj dWdQXWdQX| tj ddS(Ni iuddu 1 - 1 0 1 6 2 1 1 1 2 3 4 1 1 1 2 3 3 1 1 1 4 5 5 - 2 0 1 2 u 1 - 1 0 1 8 2 1 1 1 2 3 4 1 1 1 4 5 3 1 1 1 6 7 5 - 2 0 1 2 ( tassertNumQueriestOrderedInsertiontobjectstdelay_mptt_updatestctftparenttnametsavetassertTreeEqualtall(tself((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_move_node_different_trees % c Cs|dctjM|dd|j_|jWdQX|tjdWdQXWdQX|tjddS(Niiu 1 - 1 0 1 6 2 1 1 1 2 3 3 - 2 0 1 2 4 - 3 0 1 2 5 - 4 0 1 2 u 1 - 1 0 1 6 2 1 1 1 2 3 3 - 2 0 1 2 4 - 3 0 1 2 5 - 4 0 1 2 ( tassertNumQueriestOrderedInsertiontobjectstdelay_mptt_updatestetparenttsavetassertTreeEqualtall(tself((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_move_node_to_root2s %c Cs|dftjP|d!|j|j_|jWdQX|tj dWdQXWdQX|tj ddS(Ni iu 1 - 1 0 1 6 2 1 1 1 2 3 3 1 1 1 4 5 4 3 1 2 5 6 5 - 2 0 1 2 u 1 - 1 0 1 8 2 1 1 1 2 3 3 1 1 1 4 7 4 3 1 2 5 6 5 - 2 0 1 2 ( tassertNumQueriestOrderedInsertiontobjectstdelay_mptt_updatestetftparenttsavetassertTreeEqualtall(tself((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_move_root_to_childMs% ( t__name__t __module__tsetUpttest_insert_childttest_insert_rootttest_move_node_same_treettest_move_node_different_treettest_move_node_to_rootttest_move_root_to_child(((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyt&OrderedInsertionDelayedUpdatesTestCases      s ManagerTestscs}eZdddgZdZdZdZdZdZedZ d Z d Z d Z d Z d ZRS(ucategories.jsonu genres.jsonu persons.jsoncCsi}x~tD]p}t|ts.qn|j}t||krs|d|j|t|jfn||t|tdD]}|j}|dkr]Pq>q>W|d|q WdS(Niu=Detected infinite recursion in %s._tree_manager._base_manager(tappst get_modelst issubclasst MPTTModelt _tree_managertranget _base_managertfail(tselftmodeltmanagerti((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyt$test_base_manager_infinite_recursions     cCsV|tjt|tjjt|tjt|tjjtdS(N(tassertIsInstancetSingleProxyModelt _tree_managertCustomTreeManagert _base_managert TreeManagertobjects(tself((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_proxy_custom_managerscCs td}tjtddtddB}|||ddddg|||dtddddddgtjdd}|||d d d d d ddddg |||dtd d d dd d dddddg dS(NcSs=|jj|d|}t|ddtdS(Ns include_selfunamesflat(tmodeltobjectstget_queryset_descendantstlistt values_listtTruetorder_by(tqst include_selftdesc((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pytget_desc_namess snameu Nintendo Wiiu PlayStation 3uGamesuHardware & Accessoriess include_selfsparentu 2D Platformeru 3D Platformeru 4D Platformeru Action RPGuHorizontal Scrolling Shootemupu Platformeru Shootemupu Tactical RPGuVertical Scrolling ShootemupuActionuRole-playing Game(tFalsetCategorytobjectstfiltertQt assertEqualtTruetGenre(tselftget_desc_namestqs((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_get_queryset_descendantss, (         cCs=|jj|d|}t|ddtdS(Ns include_selfunamesflat(tmodeltobjectstget_queryset_ancestorstlistt values_listtTruetorder_by(tselftqst include_selftanc((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyt_get_anc_namess cCstjtddtddB}|||dg|||dtdddgtjdd}|||g|||dtddgdS( Nsnameu Nintendo Wiiu PlayStation 3uPC & Video Gamess include_selfsparentuActionuRole-playing Game(tCategorytobjectstfiltertQt assertEqualt_get_anc_namestTruetGenre(tselftqs((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_get_queryset_ancestorss(  c CsStj}|||dtttjddtddS(Ns include_selfunamesflat( tGenretobjectstallt assertEqualt_get_anc_namestTruetlistt values_listtorder_by(tselftqs((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyt*test_get_queryset_ancestors_regression_379scCs|ttjt|ttjdt|ttjd|ttjdd| t tjt tj dS(uL Test that a custom manager also provides custom querysets. iu custom_methodN( t assertTruet isinstancetPersontobjectstalltCustomTreeQuerysett get_childrenthasattrtnonet assertEqualttypet root_nodes(tself((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_custom_querysetss)/cCs9tttd|tjtdS(um Test that a manager created from a custom queryset works. Regression test for #378. u my_managerN(t TreeManagert from_querysettCustomTreeQuerysettcontribute_to_classtGenretassertIsInstancet my_managert get_queryset(tself((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyt!test_manager_from_custom_querysetscCsQ|d<tjtjdt}|t|dWdQXdS(u Test the number of queries to access descendants is not O(n). At the moment it is O(1)+1. Ideally we should aim for O(1). is include_selfi N(tassertNumQueriestCategorytobjectstget_queryset_descendantstalltTruet assertEqualtlen(tselftqs((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyt,test_num_queries_on_get_queryset_descendantss (t__name__t __module__tfixturesttest_all_managers_are_differentt$test_all_managers_have_correct_modelt$test_base_manager_infinite_recursionttest_proxy_custom_managerttest_get_queryset_descendantstFalset_get_anc_namesttest_get_queryset_ancestorst*test_get_queryset_ancestors_regression_379ttest_custom_querysetst!test_manager_from_custom_querysett,test_num_queries_on_get_queryset_descendants(((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyt ManagerTestsis    %     sCacheTreeChildrenTestCasecs)eZdZdgZdZdZRS(u@ Tests for the ``cache_tree_children`` template filter. ucategories.jsoncCs|dmttj}|d}|d}|d}|||j|||jjWdQXdS(uW Ensures that each node's parent is cached by ``cache_tree_children``. iiN(tassertNumQueriestcache_tree_childrentCategorytobjectstallt get_childrent assertEqualtparent(tselftrootstgamestwiit wii_games((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyt'test_cache_tree_children_caches_parentss c Cs|d7|t!tttjdWdQXWdQX|dtttjWdQXttjdddttj ddddS(u Ensures that ``cache_tree_children`` fails with a ``ValueError`` when passed a list which is not in tree order. iu-idNutree_idulftunamestree_id( tassertNumQueriest assertRaisest ValueErrortcache_tree_childrentlisttCategorytobjectstorder_bytalltfilter(tself((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyt.test_cache_tree_children_with_invalid_orderings((t__name__t __module__t__doc__tfixturest'test_cache_tree_children_caches_parentst.test_cache_tree_children_with_invalid_ordering(((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pytCacheTreeChildrenTestCases  sRecurseTreeTestCasecsPeZdZdgZedddZdZdZdZ dZ RS( u8 Tests for the ``recursetree`` template filter. ucategories.jsonu (?m)^[\s]+uu {% load mptt_tags %}
    {% recursetree nodes %}
  • {{ node.name }} {% if not node.is_leaf_node %}
      {{ children }}
    {% endif %}
  • {% endrecursetree %}
cCsTt|jtitjddd6dd}||ddS(Nspki unodesu uu,
  • Hardware & Accessories
( tTemplatettemplatetrendertContexttCategorytobjectstfiltertreplacet assertEqual(tselfthtml((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_leaf_html>s cCsftjdddt}t|jti|d6 dd}| |ddS(Nspkis include_selfunodesu uuj
  • PlayStation 3
    • Games
    • Hardware & Accessories
( tCategorytobjectstgettget_descendantstTruetTemplatettemplatetrendertContexttreplacet assertEqual(tselftqsthtml((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_nonleaf_htmlDs !  cCs|ttddS(Nu9{% load mptt_tags %}{% recursetree %}{% endrecursetree %}(t assertRaisestTemplateSyntaxErrortTemplate(tself((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_parsing_failNscCsOtd}|d.tj}|ti|d6WdQXdS(Nu! {% load mptt_tags %} {% recursetree nodes %} {{ node.get_ancestors|join:" > " }} {{ node.name }} {% if not node.is_leaf_node %} {{ children }} {% endif %} {% endrecursetree %} iunodes(tTemplatetassertNumQueriestCategorytobjectstalltrendertContext(tselfttemplatetqs((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_cached_ancestorsTs ( t__name__t __module__t__doc__tfixturestretsubttemplatettest_leaf_htmlttest_nonleaf_htmlttest_parsing_failttest_cached_ancestors(((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pytRecurseTreeTestCase)s   sTreeInfoTestCasecsDeZdgZedddZedddZdZRS(u genres.jsonu (?m)^[\s]+uu {% load mptt_tags %} {% for node, structure in nodes|tree_info %} {% if structure.new_level %}
  • {% else %}
  • {% endif %} {{ node.pk }} {% for level in structure.closed_levels %}
{% endfor %} {% endfor %}u {% load mptt_tags %} {% for node, structure in nodes|tree_info:"ancestors" %} {% if structure.new_level %}
  • {% else %}
  • {% endif %} {{ node.pk }} {% for ancestor in structure.ancestors %} {% if forloop.first %}A:{% endif %} {{ ancestor }}{% if not forloop.last %},{% endif %} {% endfor %} {% for level in structure.closed_levels %}
{% endfor %} {% endfor %}c Cs,t|jtitjd6dd}||dt|jtitjj iddtj j 6ddtj j 6d6dd}||d t|j titjj iddtj j 6ddtj j 6d6dd}||d dS( Nunodesu uu
  • 1
    • 2
      • 3
      • 4
      • 5
    • 6
      • 7
      • 8
  • 9
    • 10
    • 11
iu%s__gteiu%s__lteuw
  • 2
    • 3
    • 4
    • 5
  • 6
    • 7
    • 8
  • 10
  • 11
u
  • 2
    • 3A:Platformer
    • 4A:Platformer
    • 5A:Platformer
  • 6
    • 7A:Shootemup
    • 8A:Shootemup
  • 10
  • 11
( tTemplatettemplatetrendertContexttGenretobjectstalltreplacet assertEqualtfiltert _mptt_metat level_attrttemplate_with_ancestors(tselfthtml((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_tree_info_htmlys,  !  ! (t__name__t __module__tfixturestretsubttemplatettemplate_with_ancestorsttest_tree_info_html(((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pytTreeInfoTestCasecs  sFullTreeTestCasecs/eZdgZedddZdZRS(u genres.jsonu (?m)^[\s]+uu {% load mptt_tags %} {% full_tree_for_model myapp.Genre as tree %} {% for node in tree %}{{ node.pk }},{% endfor %} cCs>t|jtidd}||ddS(Nu uu1,2,3,4,5,6,7,8,9,10,11,(tTemplatettemplatetrendertContexttreplacet assertEqual(tselfthtml((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_full_tree_htmls*(t__name__t __module__tfixturestretsubttemplatettest_full_tree_html(((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pytFullTreeTestCases sDrilldownTreeTestCasecs>eZdgZedddZeedZdZRS(u genres.jsonu (?m)^[\s]+uub {% load mptt_tags %} {% drilldown_tree_for_node node as tree count myapp.Game.genre in game_count %} {% for n in tree %} {% ifequal n node %}[{% endifequal %} {{ n.pk }}:{{ n.game_count }} {% ifequal n node %}]{% endifequal %}{% if not forloop.last %},{% endif %} {% endfor %} cCsz|j}|r$|dd}n|r?|dd}nt|titjd|d6ddS( Nu count u cumulative count u Game.genreuGame.genres_m2mspkunodeu u(ttemplatetreplacetTemplatetrendertContexttGenretobjectstget(tselftpkt cumulativetm2mttemplate((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pytrender_for_nodes c CsbxcttjD]L\}}x=t|D]/}|jdd|}|j|q/WqW| | dd| | dd| | ddt d| | ddt d| | dd t d| | dd t d| | ddt d t d| | ddt d t ddS( NsnameuGame %siu [1:],2:1,6:5iu1:,[2:],3:2,4:3,5:4s cumulativeu[1:],2:10,6:18sm2m( t enumeratetGenretobjectstalltrangetgame_settcreatet games_m2mtaddt assertEqualtrender_for_nodetTrue(tselftidxtgenretitgame((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_drilldown_htmls<    ( t__name__t __module__tfixturestretsubttemplatetFalsetrender_for_nodettest_drilldown_html(((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pytDrilldownTreeTestCases   sTestAutoNowDateFieldModelcseZdZRS(cCst}|dS(N(tAutoNowDateFieldModeltsave(tselfta((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyt#test_save_auto_now_date_field_models (t__name__t __module__t#test_save_auto_now_date_field_model(((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pytTestAutoNowDateFieldModelssRegisteredRemoteModelcseZdZRS(cCs#tjdd}|dS(Nsnameugroup 1(tGrouptobjectstcreatetsave(tselftg1((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_save_registered_models(t__name__t __module__ttest_save_registered_model(((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pytRegisteredRemoteModelss TestFormscs,eZdgZdZdZdZRS(ucategories.jsoncCstjdd}ttdtdd}||d||i|jd6|jdj d 6d|}| | | d d |j |i|jd6|jdj d 6d|}tj|jd _| | | d d |j dS( Nsnameu Nintendo Wiisformsfields(unameuparentsinstanceunameiuparentuSelect a valid choiceu%suInvalid parent(tCategorytobjectstgettmodelform_factoryt MPTTAdminFormt assertTruetnametchildrentalltpkt assertFalsetis_validtassertInterrorstfieldstqueryset(tselftct CategoryFormtform((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_adminform_instantiations.     cCsvttdd}|}|t|jdt|t|jdt|t|jdtdS(Nsexclude(uidufkuoneum2m(tmodelform_factorytReferencingModelt assertTruet isinstancetfieldstTreeNodeChoiceFieldtTreeNodeMultipleChoiceField(tselftReferencingModelFormtform((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_field_typess          cCsltjdd}t|idd6dd6}||||tjddS(Nspkiu5utargetu first-childupositionu 1 - 1 0 1 20 5 1 1 1 2 13 2 5 1 2 3 8 3 2 1 3 4 5 4 2 1 3 6 7 6 5 1 2 9 10 7 5 1 2 11 12 8 1 1 1 14 19 9 8 1 2 15 16 10 8 1 2 17 18 ( tCategorytobjectstgett MoveNodeFormt assertTruetis_validtsavetassertTreeEqualtall(tselftctform((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_movenodeform*s   (t__name__t __module__tfixturesttest_adminform_instantiationttest_field_typesttest_movenodeform(((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyt TestFormss  sTestAltersDatacseZdZRS(cCst}tdti|d6}||d||jd|||jdtdti|d6}||tj d|jdS(Nu{{ node.save }}unodeuu{{ node.delete }}spk( tNodetTemplatetrendertContextt assertEqualtpktsavetassertNotEqualtobjectstget(tselftnodetoutput((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_alters_dataBs  (t__name__t __module__ttest_alters_data(((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pytTestAltersDataAss TestDebugInfocseZdgZdZRS(ucategories.jsoncCsctrtjntj}|,}ttjd||}WdQX| d|dS(Nsfileu 1,0,,1,1,20( tPY3tiotStringIOtBytesIOtprint_debug_infotCategorytobjectstalltgetvaluetassertIn(tselft stream_typetouttoutput((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_debug_infoWs  (t__name__t __module__tfixturesttest_debug_info(((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyt TestDebugInfoTs s AdminBatchcseZdgZdZRS(ucategories.jsoncCs<tjddd}|jd|jdd|jd}||ddtj }| |j d j j jd |j|jgid d 6d ddgd6}|jd|}||ddd|d<|jd|}||d| tjd|tjddS(Nuadminutest@example.comupsusernamespasswordu/admin/myapp/category/uname="_selected_action"i ucliudelete_selecteduactionu5u8u9u_selected_actionuvalue="Yes, I'm sure"iuyesupostiui 1 - 1 0 1 8 2 1 1 1 2 7 3 2 1 2 3 4 4 2 1 2 5 6 (tUsertobjectstcreate_superusertclienttlogintusernametgettassertContainstCategoryt _mptt_metat assertEqualtcontextt result_listtquerytorder_byt tree_id_attrt left_attrtposttassertRedirectstcounttassertTreeEqualtall(tselftusertresponset mptt_optstdata((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_changelistcs.  (t__name__t __module__tfixturesttest_changelist(((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyt AdminBatch`s s TestUnsavedcseZdZRS(c CsSxLddddddddd g D])}t|td |tt|q"WdS( Nu get_ancestorsu get_familyu get_childrenuget_descendantsu get_leafnodesuget_next_siblinguget_previous_siblinguget_rootu get_siblingsu)Cannot call %s on unsaved Genre instances(tassertRaisesRegext ValueErrortgetattrtGenre(tselftmethod((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyt test_unsaveds (t__name__t __module__t test_unsaved(((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyt TestUnsavedss QuerySetTestscs#eZdgZdZdZRS(ucategories.jsoncCs|tjdddtD]}|j^q(tjdddtD]}|j^q\|tjdddtD]}|j^qtjdddtD]}|j^qdS(Nsnameu Nintendo Wiis include_self( t assertEqualtCategorytobjectstgett get_ancestorstFalsetpktfiltertTrue(tselftc((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_get_ancestorss 484cCs|tjdddtD]}|j^q(tjdddtD]}|j^q\|tjdddtD]}|j^qtjdddtD]}|j^qdS(Nsnameu Nintendo Wiis include_self( t assertEqualtCategorytobjectstgettget_descendantstFalsetpktfiltertTrue(tselftc((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_get_descendantss 484(t__name__t __module__tfixturesttest_get_ancestorsttest_get_descendants(((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyt QuerySetTestss  sTreeManagerTestCasecseZddgZdZRS(ucategories.jsonu items.jsoncCstjddd}xEtj|tdddtD]"}||j|j q@WxEtj|tdddtD]"}||j|j qWdS(NsnameuXbox 360uidu category_fku item_counts cumulativeu category_pk( tCategorytobjectstfiltertorder_bytadd_related_counttItemtFalset assertEqualt item_countt items_by_pktcount(tselftquerysettc((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyt-test_add_related_count_with_fk_to_natural_keys   (t__name__t __module__tfixturest-test_add_related_count_with_fk_to_natural_key(((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pytTreeManagerTestCases sTestOrderedInsertionBFScs,eZdZdZdZdZRS(cCs^tjdd}tjddd|tjdd|tjddS(NsnameuRocku Led Zeppelinsparentu ClassicaluQ 3 - 1 0 1 2 1 - 2 0 1 4 2 1 2 1 2 3 (tOrderedInsertiontobjectstcreatetassertTreeEqualtall(tselftrock((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyt,test_insert_ordered_DFS_backwards_root_nodesscCstjdd}|tjdtjdd|tjdtjddd||tjddS( NsnameuRocku! 1 - 1 0 1 2 u Classicalu9 2 - 1 0 1 2 1 - 2 0 1 2 u Led ZeppelinsparentuQ 2 - 1 0 1 2 1 - 2 0 1 4 3 1 2 1 2 3 (tOrderedInsertiontobjectstcreatetassertTreeEqualtall(tselftrock((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyt,test_insert_ordered_BFS_backwards_root_nodess cCstjdd}tjddd|}tjddd|tjddd||tjddS(NsnameumusicuRocksparentu Led Zeppelinu Classicalui 1 - 1 0 1 8 4 1 1 1 2 3 2 1 1 1 4 7 3 2 1 2 5 6 (tOrderedInsertiontobjectstcreatetassertTreeEqualtall(tselftmusictrock((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyt/test_insert_ordered_DFS_backwards_nonroot_nodess cCstjdd}tjddd|}tjddd||tjdtjddd||tjddS( NsnameumusicuRocksparentu ClassicaluQ 1 - 1 0 1 6 3 1 1 1 2 3 2 1 1 1 4 5 u Led Zeppelinui 1 - 1 0 1 8 3 1 1 1 2 3 2 1 1 1 4 7 4 2 1 2 5 6 (tOrderedInsertiontobjectstcreatetassertTreeEqualtall(tselftmusictrock((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyt/test_insert_ordered_BFS_backwards_nonroot_nodess (t__name__t __module__t,test_insert_ordered_DFS_backwards_root_nodest,test_insert_ordered_BFS_backwards_root_nodest/test_insert_ordered_DFS_backwards_nonroot_nodest/test_insert_ordered_BFS_backwards_nonroot_nodes(((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pytTestOrderedInsertionBFSs  sCacheChildrenTestCasecs eZdZdgZdZRS(ur Tests that the queryset function `get_cached_trees` results in a minimum number of database queries. u genres.jsonc Cs|dtj}WdQX|t|d|dtxl|D]d}||txK|D]=}||tx$|D]}||tqWq{WqXWWdQXdS(u3 Test a query with two root nodes. iNii( tassertNumQueriestGenretobjectstalltget_cached_treest assertEqualtlentassertIsInstancet get_children(tselft root_nodestgenretchildtchild2((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyttest_genre_iter-s  (t__name__t __module__t__doc__tfixturesttest_genre_iter(((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pytCacheChildrenTestCase&s u+Signals tests require mock_django installedsSignalscs>eZdgZdZdZdZdZdZRS(ucategories.jsoncCs=t|_tjdd|_tjdd|_dS(Nspkii(t node_movedtsignaltCategorytobjectstgettwiitps3(tself((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pytsetUpEs cCsNt|jdt3}d|j_|j||jdWdQXdS(NssenderuWooi(tmock_signal_receivertsignaltCategorytwiitnametsavet assertEqualt call_count(tselftreceiver((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyt8test_signal_should_not_be_sent_when_parent_hasnt_changedJs  cCsHt|jdt-}tjdd||jdWdQXdS(NssendersnameuDescriptive namei(tmock_signal_receivertsignaltCategorytobjectstcreatet assertEqualt call_count(tselftreceiver((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyt1test_signal_should_not_be_sent_when_model_createdQsc Csft|jdtK}|j|j|d|jd|jd|jdtddWdQXdS(Nssendersinstancessignalstargetspositionu first-child(tmock_signal_receivertsignaltCategorytwiitmove_totps3tassert_called_once_with(tselftreceiver((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyt-test_move_by_using_move_to_should_send_signalWs    c Csit|jdtN}|j|j_|j|d|jd|jd|jdtWdQXdS(uposition is not set when sent from save(). I assume it would be the default(first-child) but didn't feel comfortable setting it. ssendersinstancessignalstargetN(tmock_signal_receivertsignaltCategorytps3twiitparenttsavetassert_called_once_with(tselftreceiver((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyt/test_move_by_changing_parent_should_send_signalcs     (t__name__t __module__tfixturestsetUpt8test_signal_should_not_be_sent_when_parent_hasnt_changedt1test_signal_should_not_be_sent_when_model_createdt-test_move_by_using_move_to_should_send_signalt/test_move_by_changing_parent_should_send_signal(((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pytSignalsAs      sDeferredAttributeTestscs2eZdZdZdZdZdZRS(u, Regression tests for #176 and #424 cCstjdddS(Nsnameua(tOrderedInsertiontobjectstcreate(tself((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pytsetUpxscCs^tjd}|dt|}WdQX|d||dWdQXdS(Nunameiiu) 1 - 1 0 1 2 (tOrderedInsertiontobjectstdefertassertNumQueriestlisttassertTreeEqual(tselftqstnodes((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyt test_deferred_order_insertion_by{s c Cstjd}||jdt|d |jWdQX|d| WdQX||jdddS(Nunameiiua( tOrderedInsertiontobjectstdefertgett assertEqualt_mptt_cached_fieldstDeferredAttributetassertNumQueriestnametsave(tselftobj((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyt%test_deferred_cached_field_undeferreds c Cstjd}||jdt|dd|_WdQX|d| WdQX||jdddS(Nunameiubi( tOrderedInsertiontobjectstdefertgett assertEqualt_mptt_cached_fieldstDeferredAttributetassertNumQueriestnametsave(tselftobj((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pyt!test_deferred_cached_field_changes(t__name__t __module__t__doc__tsetUpt test_deferred_order_insertion_byt%test_deferred_cached_field_undeferredt!test_deferred_cached_field_change(((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pytDeferredAttributeTeststs   (gt __future__tunicode_literalstiotostretsysttempfiletunittesttdjango.contrib.auth.modelstGrouptUsertdjango.db.modelstQtdjango.db.models.query_utilstDeferredAttributet django.appstappstdjango.forms.modelstmodelform_factorytdjango.templatetTemplatetTemplateSyntaxErrortContextt django.testtTestCasetdjango.utils.sixt string_typestPY3tbtassertRaisesRegext mock_djangotmock_signal_receivert ImportErrortmptt.exceptionstCantDisableUpdatest InvalidMovet mptt.formst MPTTAdminFormtTreeNodeChoiceFieldtTreeNodeMultipleChoiceFieldt MoveNodeFormt mptt.modelst MPTTModelt mptt.managerst TreeManagert mptt.signalst node_movedtmptt.templatetags.mptt_tagstcache_tree_childrent mptt.utilstprint_debug_infot myapp.modelstCategorytItemtGenret CustomPKNametSingleProxyModeltDoubleProxyModelt ConcreteModeltOrderedInsertiontAutoNowDateFieldModeltPersontCustomTreeQuerysettNodetReferencingModeltCustomTreeManagertget_tree_detailstcompilet MULTILINEtleading_whitespace_ret tree_detailst TreeTestCasetDocTestTestCasetReparentingTestCasetConcurrencyTestCasetDeletionTestCasetIntraTreeMovementTestCasetInterTreeMovementTestCasetPositionedInsertionTestCasetCustomPKNameTestCasetDisabledUpdatesTestCasetDelayedUpdatesTestCaset&OrderedInsertionDelayedUpdatesTestCaset ManagerTeststCacheTreeChildrenTestCasetRecurseTreeTestCasetTreeInfoTestCasetFullTreeTestCasetDrilldownTreeTestCasetTestAutoNowDateFieldModeltRegisteredRemoteModelt TestFormstTestAltersDatat TestDebugInfot AdminBatcht TestUnsavedt QuerySetTeststTreeManagerTestCasetTestOrderedInsertionBFStCacheChildrenTestCaset skipUnlesstSignalstDeferredAttributeTests(((t;/Users/cdestigter/checkout/django-mptt/tests/myapp/tests.pytsz      "  "^   ;jd(:;:L -(J2django-mptt-0.8.0/tests/myapp/urls.py0000644000076500000240000000024612634472470021065 0ustar cdestigterstaff00000000000000from django.conf.urls import include, url from django.contrib import admin admin.autodiscover() urlpatterns = [ url(r'^admin/', include(admin.site.urls)), ] django-mptt-0.8.0/tests/myapp/urls.pyc0000644000076500000240000000062712635626201021225 0ustar cdestigterstaff00000000000000 8urVcsRddlmZmZddlmZeedeejjgZdS(i(sincludesurl(sadmins^admin/N( tdjango.conf.urlstincludeturltdjango.contribtadmint autodiscovertsiteturlst urlpatterns(((t:/Users/cdestigter/checkout/django-mptt/tests/myapp/urls.pyts django-mptt-0.8.0/tests/mydatabase0000644000076500000240000225000012546316430020425 0ustar cdestigterstaff00000000000000SQLite format 3@ ~- P MP G-5I#indexsqlite_autoindex_myapp_genre_1myapp_genre!!ytablemyapp_itemmyapp_itemCREATE TABLE "myapp_item" ( "id" integer NOT NULL PRIMARY KEY, "name" varchar(100) NOT NULL, "category_fk_id" varchar(50) REFERENCES "myapp_category" ("category_uuid"), "category_pk_id" integer REFERENCES "myapp_category" ("id") )m))tablemyapp_categorymyapp_categoryCREATE TABLE "myapp_category" ( "id" integer NOT NULL PRIMARY KEY, "name" varchar(50) NOT NULL, "parent_id" integer, "category_uuid" varchar(50) UNIQUE, "lft" integer unsigned NOT NULL, "rght" integer unsigned NOT NULL, "tree_id" integer unsigned NOT NULL, "level" integer unsigned NOT NULL );O)indexsqlite_autoindex_myapp_category_1myapp_categoryD))Ctabledjango_sessiondjango_sessionCREATE TABLE "django_session" ( "session_key" varchar(40) NOT NULL PRIMARY KEY, "session_data" text NOT NULL, "expire_date" datetime NOT NULL );O)indexsqlite_autoindex_django_session_1django_session33Ctabledjango_content_typedjango_content_typeCREATE TABLE "django_content_type" ( "id" integer NOT NULL PRIMARY KEY, "name" varchar(100) NOT NULL, "app_label" varchar(100) NOT NULL, "model" varchar(100) NOT NULL, UNIQUE ("app_label", "model") )EY3indexsqlite_autoindex_django_content_type_1django_content_typeJ ctableauth_userauth_user CREATE TABLE "auth_user" ( "id" integer NOT NULL PRIMARY KEY, "username" varchar(30) NOT NULL UNIQUE, "first_name" varchar(30) NOT NULL, "last_name" varchar(30) NOT NULL, "email" varchar(75) NOT NULL, "password" varchar(128) NOT NULL, "is_staff" bool NOT NULL, "is_active" bool NOT NULL, "is_superuser" bool NOT NULL, "last_login" datetime NOT NULL, "date_joined" datetime NOT NULL )1 Eindexsqlite_autoindex_auth_user_1auth_userz --'tableauth_user_groupsauth_user_groups CREATE TABLE "auth_user_groups" ( "id" integer NOT NULL PRIMARY KEY, "user_id" integer NOT NULL, "group_id" integer NOT NULL REFERENCES "auth_group" ("id"), UNIQUE ("user_id", "group_id") )? S-indexsqlite_autoindex_auth_user_groups_1auth_user_groups 'AAYtableauth_user_user_permissionsauth_user_user_permissions CREATE TABLE "auth_user_user_permissions" ( "id" integer NOT NULL PRIMARY KEY, "user_id" integer NOT NULL, "permission_id" integer NOT NULL REFERENCES "auth_permission" ("id"), UNIQUE ("user_id", "permission_id") )S gAindexsqlite_autoindex_auth_user_user_permissions_1auth_user_user_permissions !!ctableauth_groupauth_groupCREATE TABLE "auth_group" ( "id" integer NOT NULL PRIMARY KEY, "name" varchar(80) NOT NULL UNIQUE )3G!indexsqlite_autoindex_auth_group_1auth_group99Utableauth_group_permissionsauth_group_permissionsCREATE TABLE "auth_group_permissions" ( "id" integer NOT NULL PRIMARY KEY, "group_id" integer NOT NULL, "permission_id" integer NOT NULL REFERENCES "auth_permission" ("id"), UNIQUE ("group_id", "permission_id") )K_9indexsqlite_autoindex_auth_group_permissions_1auth_group_permissions++Stableauth_permissionauth_permissionCREATE TABLE "auth_permission" ( "id" integer NOT NULL PRIMARY KEY, "name" varchar(50) NOT NULL, "content_type_id" integer NOT NULL, "codename" varchar(100) NOT NULL, UNIQUE ("content_type_id", "codename") )=Q+indexsqlite_autoindex_auth_permission_1auth_permissionk-- tabledjango_admin_logdjango_admin_logCREATE TABLE "django_admin_log" ( "id" integer NOT NULL PRIMARY KEY, "action_time" datetime NOT NULL, "user_id" integer NOT NULL, "content_type_id" integer, "object_id" text, "object_repr" varchar(200) NOT NULL, "action_flag" smallint unsigned NOT NULL, "cxz\f g ?  g I %  [ 5 p N  e C rL&Rs.Sx-Rn](9QE;Can delete concrete concretedelete_concreteconcrete9PE;Can change concrete concretechange_concreteconcrete3O?5Can add concrete concreteadd_concreteconcrete[Ni[Can delete concrete abstract concrete abstractdelete_concreteabstractconcreteabstract[Mi[Can change concrete abstract concrete abstractchange_concreteabstractconcreteabstractULcUCan add concrete abstract concrete abstractadd_concreteabstractconcreteabstract3K?5Can delete concrete modeldelete_concretemodel3J?5Can change concrete modelchange_concretemodel-I9/Can add concrete modeladd_concretemodelIHWICan delete multi table inheritance b2delete_multitableinheritanceb2IGWICan change multi table inheritance b2change_multitableinheritanceb2CFQCCan add multi table inheritance b2add_multitableinheritanceb2IEWICan delete multi table inheritance b1delete_multitableinheritanceb1IDWICan change multi table inheritance b1change_multitableinheritanceb1CCQCCan add multi table inheritance b1add_multitableinheritanceb1IBWICan delete multi table inheritance a2delete_multitableinheritancea2IAWICan change multi table inheritance a2change_multitableinheritancea2C@QCCan add multi table inheritance a2add_multitableinheritancea2I?WICan delete multi table inheritance a1delete_multitableinheritancea1I>WICan change multi table inheritance a1change_multitableinheritancea1C=QCCan add multi table inheritance a1add_multitableinheritancea19<E;Can delete referencing modeldelete_referencingmodel9;E;Can change referencing modelchange_referencingmodel3:?5Can add referencing modeladd_referencingmodel29?3Can delete custom pk namedelete_custompkname28?3Can change custom pk namechange_custompkname,79-Can add custom pk nameadd_custompkname&61)Can delete studentdelete_student&51)Can change studentchange_student 4+#Can add studentadd_student$3/'Can delete persondelete_person$2/'Can change personchange_person1)!Can add personadd_person;0I;Can delete new style mptt metadelete_newstylempttmeta;/I;Can change new style mptt metachange_newstylempttmeta5.C5Can add new style mptt metaadd_newstylempttmeta -+#Can delete treedelete_tree ,+#Can change treechange_tree+%Can add treeadd_tree9*E;Can delete ordered insertiondelete_orderedinsertion9)E;Can change ordered insertionchange_orderedinsertion3(?5Can add ordered insertionadd_orderedinsertion '+#Can delete node delete_node &+#Can change node change_node%%Can add node add_node-$9/Can delete multi order delete_multiorder-#9/Can change multi order change_multiorder'"3)Can add multi order add_multiorder$!/'Can delete insert delete_insert$ /'Can change insert change_insert)!Can add insert add_insert +#Can delete game delete_game +#Can change game change_game%Can add game add_game"-%Can delete genre delete_genre"-%Can change genre change_genre'Can add genre add_genre +#Can delete itemdelete_item +#Can change itemchange_item%Can add itemadd_item(3+Can delete categorydelete_category(3+Can change categorychange_category"-%Can add categoryadd_category&1)Can delete sessiondelete_session&1)Can change sessionchange_session +#Can add sessionadd_session/;1Can delete content typedelete_contenttype/;1Can change content typechange_contenttype) 5+Can add content typeadd_contenttype +#Can delete userdelete_user +#Can change userchange_user %Can add useradd_user" -%Can delete groupdelete_group"-%Can change groupchange_group'Can add groupadd_group,7/Can delete permissiondelete_permission,7/Can change permissionchange_permission&1)Can add permissionadd_permission(5 +Can delete log entrydelete_logentry(5 +Can change log entrychange_logentry"/ %Can add log entryadd_log4Q ]p]J;)jTE3!  j R : +   } b D &   f K -  \ 7  _ G , oQ3eB%delete_group]%change_group\add_group["Edelete_autonowdatefieldmodelZ"Echange_autonowdatefieldmodelY?add_autonowdatefieldmodelX;delete_doubleproxymodelW;change_doubleproxymodelV5add_doubleproxymodelU;delete_singleproxymodelT;change_singleproxymodelS5add_singleproxymodelR;delete_concreteconcreteQ;change_concreteconcreteP5add_concreteconcreteO-[delete_concreteabstractconcreteabstractN-[change_concreteabstractconcreteabstractM*Uadd_concreteabstractconcreteabstractL5delete_concretemodelK5change_concretemodelJ/add_concretemodelI$Idelete_multitableinheritanceb2H$Ichange_multitableinheritanceb2G!Cadd_multitableinheritanceb2F$Idelete_multitableinheritanceb1E$Ichange_multitableinheritanceb1D!Cadd_multitableinheritanceb1C$Idelete_multitableinheritancea2B$Ichange_multitableinheritancea2A!Cadd_multitableinheritancea2@$Idelete_multitableinheritancea1?$Ichange_multitableinheritancea1>!Cadd_multitableinheritancea1=;delete_referencingmodel<;change_referencingmodel;5add_referencingmodel:3delete_custompkname93change_custompkname8-add_custompkname7)delete_student6)change_student5#add_student4'delete_person3'change_person2!add_person1;delete_newstylempttmeta0;change_newstylempttmeta/5add_newstylempttmeta.#delete_tree-#change_tree,add_tree+;delete_orderedinsertion*;change_orderedinsertion)5add_orderedinsertion(# delete_node'# change_node& add_node%/ delete_multiorder$/ change_multiorder#) add_multiorder"' delete_insert!' change_insert ! add_insert# delete_game# change_game add_game% delete_genre% change_genre add_genre#delete_item#change_itemadd_item+delete_category+change_category%add_category)delete_session)change_session#add_session1delete_contenttype1change_contenttype+add_contenttype #delete_user #change_user add_user %delete_group %change_groupadd_group/delete_permission/change_permission)add_permission +delete_logentry +change_logentry % add_logentry            vrU9%m>& | ?  O " v,1-single proxy modelmyappsingleproxymodel,1-double proxy modelmyappdoubleproxymodelgroupmyappgroup8?7auto now date field modelmyappautonowdatefieldmodel+/-concrete concretemyappconcreteconcreteMSMconcrete abstract concrete abstractmyappconcreteabstractconcreteabstract%)'concrete modelmyappconcretemodel;A;multi table inheritance b2myappmultitableinheritanceb2;A;multi table inheritance b1myappmultitableinheritanceb1;A;multi table inheritance a2myappmultitableinheritancea2;A;multi table inheritance a1myappmultitableinheritancea1+/-referencing modelmyappreferencingmodel$)%custom pk namemyappcustompknamestudentmyappstudentpersonmyappperson-3-new style mptt metamyappnewstylempttmetatreemyapptree+/-ordered insertionmyapporderedinsertion nodemyappnode #!multi ordermyappmultiorder insertmyappinsert gamemyappgame genremyappgenreitemmyappitemcategorymyappcategorysessionsessionssession(%%#content typecontenttypescontenttypeuserauthusergroupauthgroup!!permissionauthpermissionlog entryadminlogentry  / ux  JJY e9i$Y7 { /-myappsingleproxymodel-myappdoubleproxymodelmyappgroup7myappautonowdatefieldmodel-myappconcreteconcrete*Mmyappconcreteabstractconcreteabstract'myappconcretemodel!;myappmultitableinheritanceb2!;myappmultitableinheritanceb1!;myappmultitableinheritancea2!;myappmultitableinheritancea1-myappreferencingmodel%myappcustompknamemyappstudentmyappperson-myappnewstylempttmetamyapptree-myapporderedinsertionmyappnode !myappmultiorder myappinsert myappgame myappgenre myappitemmyappcategorysessionssession%#contenttypescontenttype authuserauthgroup!authpermission adminlogentry      sI % Tactical RPG  ! Action RPG  / Role-playing Game*I Horizontal Scrolling Shootemup (E Vertical Scrolling Shootemup   Shootemup ' 4D Platformer' 3D Platformer' 2D Platformer!  Platformer   Action ):_I)%Tactical RPG !Action RPG /Role-playing Game "IHorizontal Scrolling Shootemup EVertical Scrolling Shootemup Shootemup'4D Platformer'3D Platformer'2D Platformer!Platformer  Action KK )v:d  OS55I#indexsqlite_autoindex_myapp_genre_1myapp_genrek-- tabledjango_admin_logdjango_admin_logCREATE TABLE "django_admin_log" ( "id" integer NOT NULL PRIMARY KEY, "action_time" datetime NOT NULL, "user_id" integer NOT NULL, "content_type_id" integer, "object_id" text, "object_repr" varchar(200) NOT NULLk-- tabledjango_admin_logdjango_admin_logCREATE TABLE "django_admin_log" ( "id" integer NOT NULL PRIMARY KEY, "action_time" datetime NOT NULL, "user_id" integer NOT NULL, "content_type_id" integer, "object_id" text, "object_repr" varchar(200) NOT NULL, "action_flag" smallint unsigned NOT NULL, "change_message" text NOT NULL )++Stableauth_permissionauth_permissionCREATE TABLE "auth_permission" ( "id" integer NOT NULL PRIMARY KEY, "name" varchar(50) NOT NULL, "content_type_id" integer NOT NULL, "codename" varchar(100) NOT NULL, UNIQUE ("content_type_id", "codename") )=Q+indexsqlite_autoindex_auth_permission_1auth_permission99Utableauth_group_permissionsauth_group_permissionsCREATE TABLE "auth_group_permissions" ( "id" integer NOT NULL PRIMARY KEY, "group_id" integer NOT NULL, "permission_id" integer NOT NULL REFERENCES "auth_permission" ("id"), UNIQUE ("group_id", "permission_id") )K_9indexsqlite_autoindex_auth_group_permissions_1auth_group_permissions !!ctableauth_groupauth_groupCREATE TABLE "auth_group" ( "id" integer NOT NULL PRIMARY KEY, "name" varchar(80) NOT NULL UNIQUE )3G!indexsqlite_autoindex_auth_group_1auth_group'AAYtableauth_user_user_permissionsauth_user_user_permissions CREATE TABLE "auth_user_user_permissions" ( "id" integer NOT NULL PRIMARY KEY, "user_id" integer NOT NULL, "permission_id" integer NOT NULL REFERENCES "auth_permission" ("id"), UNIQUE ("user_id", "permission_id") )S gAindexsqlite_autoindex_auth_user_user_permissions_1auth_user_user_permissions z --'tableauth_user_groupsauth_user_groups CREATE TABLE "auth_user_groups" ( "id" integer NOT NULL PRIMARY KEY, "user_id" integer NOT NULL, "group_id" integer NOT NULL REFERENCES "auth_group" ("id"), UNIQUE ("user_id", "group_id") )? S-indexsqlite_autoindex_auth_user_groups_1auth_user_groups J ctableauth_userauth_user CREATE TABLE "auth_user" ( "id" integer NOT NULL PRIMARY KEY, "username" varchar(30) NOT NULL UNIQUE, "first_name" varchar(30) NOT NULL, "last_name" varchar(30) NOT NULL, "email" varchar(75) NOT NULL, "password" varchar(128) NOT NULL, "is_staff" bool NOT NULL, "is_active" bool NOT NULL, "is_superuser" bool NOT NULL, "last_login" datetime NOT NULL, "date_joined" datetime NOT NULL )1 Eindexsqlite_autoindex_auth_user_1auth_user33Ctabledjango_content_typedjango_content_typeCREATE TABLE "django_content_type" ( "id" integer NOT NULL PRIMARY KEY, "name" varchar(100) NOT NULL, "app_label" varchar(100) NOT NULL, "model" varchar(100) NOT NULL, UNIQUE ("app_label", "model") )EY3indexsqlite_autoindex_django_content_type_1django_content_typeD))Ctabledjango_sessiondjango_sessionCREATE TABLE "django_session" ( "session_key" varchar(40) NOT NULL PRIMARY KEY, "session_data" text NOT NULL, "expire_date" datetime NOT NULL );O)indexsqlite_autoindex_django_session_1django_sessionm))tablemyapp_categorymyapp_categoryCREATE TABLE "myapp_category" ( "id" integer NOT NULL PRIMARY KEY, "name" varchar(50) NOT NULL, "parent_id" integer, "category_uuid" varchar(50) UNIQUE, "lft" integer unsigned NOT NULL, "rght" integer unsigned NOT NULL, "tree_id" integer unsigned NOT NULL, "level" integer unsigned NOT NULL );O)indexsqlite_autoindex_myapp_category_1myapp_category -dq 4 C C(c"11qtablemyapp_custoC##Mtablemyapp_genremyapp_gen!!ytablemyapp_itemmyapp_itemCREATE TABLE "myapp_item" ( "id" integer NOT NULL PRIMARY KEY, "name" varchar(100) NOT NULL, "category_fk_id" varchar(50) REFERENCES "myapp_category" ("category_uuid"), "category_pk_id" integer REFERENCES "myapp_category" ("id") )C##Mtablemyapp_genremyapp_genreCREATE TABLE "myapp_genre" ( "id" !!ytablemyapp_itemmyapp_itemCREATE TABLE "myapp_item" ( "id" integer NOT NULL PRIMARY KEY, "name" varchar(100) NOT NULL, "category_fk_id" varchar(50) REFERENCES "myapp_category" ("category_uuid"), "category_pk_id" integer REFERENCES "myapp_category" ("id") )C##Mtablemyapp_genremyapp_genreCREATE TABLE "myapp_genre" ( "id" integer NOT NULL PRIMARY KEY, "name" varchar(50) NOT NULL UNIQUE, "parent_id" integer, "lft" integer unsigned NOT NULL, "rght" integer unsigned NOT NULL, "tree_id" integer unsigned NOT NULL, "level" integer unsigned NOT NULL )5I#indexsqlite_autoindex_myapp_genre_1myapp_genre 773tablemyapp_game_genres_m2mmyapp_game_genres_m2mCREATE TABLE "myapp_game_genres_m2m" ( "id" integer NOT NULL PRIMARY KEY, "game_id" integer NOT NULL, "genre_id" integer NOT NULL REFERENCES "myapp_genre" ("id"), UNIQUE ("game_id", "genre_id") )I]7indexsqlite_autoindex_myapp_game_genres_m2m_1myapp_game_genres_m2mF!!Wtablemyapp_gamemyapp_gameCREATE TABLE "myapp_game" ( "id" integer NOT NULL PRIMARY KEY, "genre_id" integer NOT NULL REFERENCES "myapp_genre" ("id"), "name" varchar(50) NOT NULL )%%tablemyapp_insertmyapp_insertCREATE TABLE "myapp_insert" ( "id" integer NOT NULL PRIMARY KEY, "parent_id" integer, "lft" integer unsigned NOT NULL, "rght" integer unsigned NOT NULL, "tree_id" integer unsigned NOT NULL, "level" integer unsigned NOT NULL ) --Itablemyapp_multiordermyapp_multiorderCREATE TABLE "myapp_multiorder" ( "id" integer NOT NULL PRIMARY KEY, "name" varchar(50) NOT NULL, "size" integer unsigned NOT NULL, "date" date NOT NULL, "parent_id" integer, "lft" integer unsigned NOT NULL, "rght" integer unsigned NOT NULL, "tree_id" integer unsigned NOT NULL, "level" integer unsigned NOT NULL ) !!ctablemyapp_nodemyapp_nodeCREATE TABLE "myapp_node" ( "id" integer NOT NULL PRIMARY KEY, "parent_id" integer, "level" integer NOT NULL, "does" integer unsigned NOT NULL, "zis" integer unsigned NOT NULL, "work" integer unsigned NOT NULL )]99Utablemyapp_orderedinsertionmyapp_orderedinsertion CREATE TABLE "myapp_orderedinsertion" ( "id" integer NOT NULL PRIMARY KEY, "name" varchar(50) NOT NULL, "parent_id" integer, "lft" integer unsigned NOT NULL, "rght" integer unsigned NOT NULL, "tree_id" integer unsigned NOT NULL, "level" integer unsigned NOT NULL )!!{tablemyapp_treemyapp_tree!CREATE TABLE "myapp_tree" ( "id" integer NOT NULL PRIMARY KEY, "parent_id" integer, "lft" integer unsigned NOT NULL, "rght" integer unsigned NOT NULL, "tree_id" integer unsigned NOT NULL, "level" integer unsigned NOT NULL )@99tablemyapp_newstylempttmetamyapp_newstylempttmeta"CREATE TABLE "myapp_newstylempttmeta" ( "id" integer NOT NULL PRIMARY KEY, "parent_id" integer, "testing" integer unsigned NOT NULL, "rght" integer unsigned NOT NULL, "tree_id" integer unsigned NOT NULL, "level" integer unsigned NOT NULL )? %%Atablemyapp_personmyapp_person#CREATE TABLE "myapp_person" ( "id" integer NOT NULL PRIMARY KEY, "name" varchar(50) NOT NULL, "parent_id" integer, "lft" integer unsigned NOT NULL, "rght" integer unsigned NOT NULL, "tree_id" integer unsigned NOT NULL, "level" integer unsigned NOT NULL ):!''3tablemyapp_studentmyapp_student$CREATE TABLE "myapp_student" ( "person_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "myapp_person" ("id"), "type" varchar(50) NOT NULL )               g[  ! U}d:#AAtablemyapp_referencingmodel_m2mmyapp_referencingmodel_m2m&CREAc"11qtablemyapp_custompknamemyapp_custompkname%CREATE TABLE "myapp_custompkname" ( "my_custom_name" integer NOT NULL PRIMAc"11qtablemyapp_custompknamemyapp_custompkname%CREATE TABLE "myapp_custompkname" ( "my_custom_name" integer NOT NULL PRIMARY KEY, "name" varchar(50) NOT NULL, "my_cusom_parent" integer, "lft" integer unsigned NOT NULL, "rght" integer unsigned NOT NULL, "tree_id" integer unsigned NOT NULL, "level" integer unsigned NOT NULL ):#AAtablemyapp_referencingmodel_m2mmyapp_referencingmodel_m2m&CREATE TABLE "myapp_referencingmodel_m2m" ( "id" integer NOT NULL PRIMARY KEY, "referencingmodel_id" integer NOT NULL, "category_id" integer NOT NULL REFERENCES "myapp_category" ("id"), UNIQUE ("referencingmodel_id", "category_id") )S$gAindexsqlite_autoindex_myapp_referencingmodel_m2m_1myapp_referencingmodel_m2m'%99?tablemyapp_referencingmodelmyapp_referencingmodel)CREATE TABLE "myapp_referencingmodel" ( "id" integer NOT NULL PRIMARY KEY, "fk_id" integer NOT NULL REFERENCES "myapp_category" ("id"), "one_id" integer NOT NULL UNIQUE REFERENCES "myapp_category" ("id") )K&_9indexsqlite_autoindex_myapp_referencingmodel_1myapp_referencingmodel*Q'GG!tablemyapp_multitableinheritancea1myapp_multitableinheritancea1+CREATE TABLE "myapp_multitableinheritancea1" ( "id" integer NOT NULL PRIMARY KEY, "parent_id" integer, "lft" integer unsigned NOT NULL, "rght" integer unsigned NOT NULL, "tree_id" integer unsigned NOT NULL, "level" integer unsigned NOT NULL ) (GGtablemyapp_multitableinheritancea2myapp_multitableinheritancea2,CREATE TABLE "myapp_multitableinheritancea2" ( "multitableinheritancea1_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "myapp_multitableinheritancea1" ("id"), "name" varchar(50) NOT NULL )Y)GG1tablemyapp_multitableinheritanceb1myapp_multitableinheritanceb1-CREATE TABLE "myapp_multitableinheritanceb1" ( "id" integer NOT NULL PRIMARY KEY, "name" varchar(50) NOT NULL, "lft" integer unsigned NOT NULL, "rght" integer unsigned NOT NULL, "tree_id" integer unsigned NOT NULL, "level" integer unsigned NOT NULL )*GGtablemyapp_multitableinheritanceb2myapp_multitableinheritanceb2.CREATE TABLE "myapp_multitableinheritanceb2" ( "multitableinheritanceb1_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "myapp_multitableinheritanceb1" ("id"), "parent_id" integer )w+33tablemyapp_concretemodelmyapp_concretemodel/CREATE TABLE "myapp_concretemodel" ( "id" integer NOT NULL PRIMARY KEY, "parent_id" integer, "ghosts" varchar(50) NOT NULL, "name" varchar(50) NOT NULL, "lft" integer unsigned NOT NULL, "rght" integer unsigned NOT NULL, "tree_id" integer unsigned NOT NULL, "level" integer unsigned NOT NULL )r,YY?tablemyapp_concreteabstractconcreteabstractmyapp_concreteabstractconcreteabstract0CREATE TABLE "myapp_concreteabstractconcreteabstract" ( "concretemodel_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "myapp_concretemodel" ("id") )B-99tablemyapp_concreteconcretemyapp_concreteconcrete1CREATE TABLE "myapp_concreteconcrete" ( "concretemodel_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "myapp_concretemodel" ("id") )h.CCWtablemyapp_autonowdatefieldmodelmyapp_autonowdatefieldmodel2CREATE TABLE "myapp_autonowdatefieldmodel" ( "id" integer NOT NULL PRIMARY KEY, "parent_id" integer, "now" datetime NOT NULL, "lft" integer unsigned NOT NULL, "rght" integer unsigned NOT NULL, "tree_id" integer unsigned NOT NULL, "level" integer unsigned NOT NULL )=/##Atablemyapp_groupmyapp_group3CREATE TABLE "myapp_group" ( "id" integer NOT NULL PRIMARY KEY, "name" varchar(100) NOT NULL, "parent_id" integer, "lft" integer unsigned NOT NULL, "rght" integer unsigned NOT NULL, "tree_id" integer unsigned NOT NULL, "level" integer unsigned NOT NULL )            Q`2`> g ?  g I %  [ 5 p N  e C rL&Rs.Sx-Rn](9QE;Can delete concrete concretedelete_concreteconcrete9PE;Can change concrete concretechange_concreteconcrete3O?5Can add concrete concreteadd_concreteconcrete[Ni[Can delete concrete abstract concrete abstractdelete_concreteabstractconcreteabstract[Mi[Can change concrete abstract concrete abstractchange_concreteabstractconcreteabstractULcUCan add concrete abstract concrete abstractadd_concreteabstractconcreteabstract3K?5Can delete concrete modeldelete_concretemodel3J?5Can change concrete modelchange_concretemodel-I9/Can add concrete modeladd_concretemodelIHWICan delete multi table inheritance b2delete_multitableinheritanceb2IGWICan change multi table inheritance b2change_multitableinheritanceb2CFQCCan add multi table inheritance b2add_multitableinheritanceb2IEWICan delete multi table inheritance b1delete_multitableinheritanceb1IDWICan change multi table inheritance b1change_multitableinheritanceb1CCQCCan add multi table inheritance b1add_multitableinheritanceb1IBWICan delete multi table inheritance a2delete_multitableinheritancea2IAWICan change multi table inheritance a2change_multitableinheritancea2C@QCCan add multi table inheritance a2add_multitableinheritancea2I?WICan delete multi table inheritance a1delete_multitableinheritancea1I>WICan change multi table inheritance a1change_multitableinheritancea1C=QCCan add multi table inheritance a1add_multitableinheritancea19<E;Can delete referencing modeldelete_referencingmodel9;E;Can change referencing modelchange_referencingmodel3:?5Can add referencing modeladd_referencingmodel29?3Can delete custom pk namedelete_custompkname28?3Can change custom pk namechange_custompkname,79-Can add custom pk nameadd_custompkname&61)Can delete studentdelete_student&51)Can change studentchange_student 4+#Can add studentadd_student$3/'Can delete persondelete_person$2/'Can change personchange_person1)!Can add personadd_person;0I;Can delete new style mptt metadelete_newstylempttmeta;/I;Can change new style mptt metachange_newstylempttmeta5.C5Can add new style mptt metaadd_newstylempttmeta -+#Can delete treedelete_tree ,+#Can change treechange_tree+%Can add treeadd_tree9*E;Can delete ordered insertiondelete_orderedinsertion9)E;Can change ordered insertionchange_orderedinsertion3(?5Can add ordered insertionadd_orderedinsertion '+#Can delete node delete_node &+#Can change node change_node%%Can add node add_node-$9/Can delete multi order delete_multiorder-#9/Can change multi order change_multiorder'"3)Can add multi order add_multiorder$!/'Can delete insert delete_insert$ /'Can change insert change_insert)!Can add insert add_insert +#Can delete game delete_game +#Can change game change_game%Can add game add_game"-%Can delete genre delete_genre"-%Can change genre change_genre'Can add genre add_genre +#Can delete itemdelete_item +#Can change itemchange_item%Can add itemadd_item(3+Can delete categorydelete_category(3+Can change categorychange_category"-%Can add categoryadd_category&1)Can delete sessiondelete_session&1)Can change sessionchange_session +#Can add sessionadd_session/;1Can delete content typedelete_contenttype/;1Can change content typechange_contenttype) 5+Can add content typeadd_contenttype +#Can delete userdelete_user +#Can change userchange_user %Can add useradd_user" -%Can delete groupdelete_group"-%Can change groupchange_group'Can add groupadd_group,7/Can delete permissiondelete_permission,7/Can change permissionchange_permission&1)Can add permissionadd_permission(5 +Can delete log entrydelete_logentry(5 +Can change log entrychange_logentry"/ %Can add log entryadd_logentry lRb l"]-%Can delete groupdelete_group"\-%Can change groupchange_group['Can add groupadd_groupFZUECan delete auto now date field modeldelete_autonowdatefieldmodelFYUECan change auto now date field modelchange_autonowdatefieldmodel@XO?Can add auto now date field modeladd_autonowdatefieldmodel:WG;Can delete double proxy modeldelete_doubleproxymodel:VG;Can change double proxy modelchange_doubleproxymodel4UA5Can add double proxy modeladd_doubleproxymodel:TG;Can delete single proxy modeldelete_singleproxymodel:SG;Can change single proxy modelchange_singleproxymodel4RA5Can add single proxy modeladd_singleproxymodel   ] ztnhb\VPJD>82,& ~xrlf`ZTNHB<60*$  ]\[ZYXQPONMLWVUTSRKJIHGFEDCBA@?>=<;:9876543210/.-,+*)( ' & % $ # " !                   74;5 "  f ;  D# {djS3! indexmyapp_node_63f17a16myapp_nodeZCREATE INDEX "m0?-!indexdjango_admin_log_fbfc09f1django_admin_log6CREATE INDEX "django_admin_log_fbfc09f1" ON "django_admin_log" ("user_id")1?-1indexdjango_admin_log_e4470c6edjango_admin_log7CREATE INDEX "django_admin_log_e4470c6e" ON "django_admin_log" ("content_type_id")2=+-indexauth_permission_e4470c6eauth_permission8CREATE INDEX "auth_permission_e4470?-!indexdjango_admin_log_fbfc09f1django_admin_log6CREATE INDEX "django_admin_log_fbfc09f1" ON "django_admin_log" ("user_id")1?-1indexdjango_admin_log_e4470c6edjango_admin_log7CREATE INDEX "django_admin_log_e4470c6e" ON "django_admin_log" ("content_type_id")2=+-indexauth_permission_e4470c6eauth_permission8CREATE INDEX "auth_permission_e4470c6e" ON "auth_permission" ("content_type_id")3K9;indexauth_group_permissions_bda51c3cauth_group_permissions9CREATE INDEX "auth_group_permissions_bda51c3c" ON "auth_group_permissions" ("group_id")4K9Eindexauth_group_permissions_1e014c8fauth_group_permissions:CREATE INDEX "auth_group_permissions_1e014c8f" ON "auth_group_permissions" ("permission_id")(5SAIindexauth_user_user_permissions_fbfc09f1auth_user_user_permissions;CREATE INDEX "auth_user_user_permissions_fbfc09f1" ON "auth_user_user_permissions" ("user_id").6SAUindexauth_user_user_permissions_1e014c8fauth_user_user_permissions=CREATE INDEX "auth_user_user_permissions_1e014c8f" ON "auth_user_user_permissions" ("permission_id")7?-!indexauth_user_groups_fbfc09f1auth_user_groups>CREATE INDEX "auth_user_groups_fbfc09f1" ON "auth_user_groups" ("user_id")8?-#indexauth_user_groups_bda51c3cauth_user_groups?CREATE INDEX "auth_user_groups_bda51c3c" ON "auth_user_groups" ("group_id")|9;)!indexdjango_session_c25c2c28django_session@CREATE INDEX "django_session_c25c2c28" ON "django_session" ("expire_date")z:;)indexmyapp_category_63f17a16myapp_categoryACREATE INDEX "myapp_category_63f17a16" ON "myapp_category" ("parent_id")t;;)indexmyapp_category_42b06ff6myapp_categoryBCREATE INDEX "myapp_category_42b06ff6" ON "myapp_category" ("lft")u<;)indexmyapp_category_91543e5amyapp_categoryCCREATE INDEX "myapp_category_91543e5a" ON "myapp_category" ("rght")x=;)indexmyapp_category_efd07f28myapp_categoryDCREATE INDEX "myapp_category_efd07f28" ON "myapp_category" ("tree_id")v>;)indexmyapp_category_2a8f42e8myapp_categoryECREATE INDEX "myapp_category_2a8f42e8" ON "myapp_category" ("level")o?3!indexmyapp_item_1d121359myapp_itemFCREATE INDEX "myapp_item_1d121359" ON "myapp_item" ("category_fk_id")o@3!indexmyapp_item_dfe38ac1myapp_itemGCREATE INDEX "myapp_item_dfe38ac1" ON "myapp_item" ("category_pk_id")nA5#indexmyapp_genre_63f17a16myapp_genreHCREATE INDEX "myapp_genre_63f17a16" ON "myapp_genre" ("parent_id")hB5#indexmyapp_genre_42b06ff6myapp_genreICREATE INDEX "myapp_genre_42b06ff6" ON "myapp_genre" ("lft")iC5#indexmyapp_genre_91543e5amyapp_genreJCREATE INDEX "myapp_genre_91543e5a" ON "myapp_genre" ("rght")lD5# indexmyapp_genre_efd07f28myapp_genreKCREATE INDEX "myapp_genre_efd07f28" ON "myapp_genre" ("tree_id")jE5# indexmyapp_genre_2a8f42e8myapp_genreLCREATE INDEX "myapp_genre_2a8f42e8" ON "myapp_genre" ("level")FI75indexmyapp_game_genres_m2m_7b333d1emyapp_game_genres_m2mMCREATE INDEX "myapp_game_genres_m2m_7b333d1e" ON "myapp_game_genres_m2m" ("game_id")GI77indexmyapp_game_genres_m2m_f8d711d0myapp_game_genres_m2mNCREATE INDEX "myapp_game_genres_m2m_f8d711d0" ON "myapp_game_genres_m2m" ("genre_id")iH3! indexmyapp_game_f8d711d0myapp_gameOCREATE INDEX "myapp_game_f8d711d0" ON "myapp_game" ("genre_id")rI7%indexmyapp_insert_63f17a16myapp_insertPCREATE INDEX "myapp_insert_63f17a16" ON "myapp_insert" ("parent_id")lJ7% indexmyapp_insert_42b06ff6myapp_insertQCREATE INDEX "myapp_insert_42b06ff6" ON "myapp_insert" ("lft")mK7% indexmyapp_insert_91543e5amyapp_insertRCREATE INDEX "myapp_insert_91543e5a" ON "myapp_insert" ("rght")                                                                   xxZ]_K K  Q " ' X ^xJnj7% indexmyapp_person_2a8f42e8myap}P?-inde|O?-indexmyapp_multiorder_42b06ff6myapp_multiorderVCREATE INDEX "myapp_multiorder_42b06ff6" ON "myapp_multiorder" ("lft")}P?-indexmyapp_multiorder_91543e5amyapp_multiorderWCREATE INDEX "myapp_multiorder_91543e5a" ON "myapp_multiorder" ("rght")Q?-!indexmyapp_multiorder_efd07f28myapp_multiorderXCREATE INDEX "myapp_multiorder_efd07f28" ON "myapp_multiorder" ("tree_id")~R?-indexmyapp_multiorder_2a8f42e8myapp_multiorderYCREATE INDEX "myapp_multiorder_2a8f42e8" ON "myapp_multiorder" ("lpL7%indexmyapp_insert_efd07f28myapp_insertSCREATE INDEX "myapp_insert_efd07f28" ON "myapp_insert" ("tree_id")nM7% indexmyapp_insert_2a8f42e8myapp_insertTCREATE INDEX "myapp_insert_2a8f42e8" ON "myapp_insert" ("level")N?-%indexmyapp_multiorder_63f17a16myapp_multiorderUCREATE INDEX "myapp_multiorder_63f17a16" ON "myapp_multiorder" ("parent_id")|O?-indexmyapp_multiorder_42b06ff6myapp_multiorderVCREATE INDEX "myapp_multiorder_42b06ff6" ON "myapp_multiorder" ("lft")}P?-indexmyapp_multiorder_91543e5amyapp_multiorderWCREATE INDEX "myapp_multiorder_91543e5a" ON "myapp_multiorder" ("rght")Q?-!indexmyapp_multiorder_efd07f28myapp_multiorderXCREATE INDEX "myapp_multiorder_efd07f28" ON "myapp_multiorder" ("tree_id")~R?-indexmyapp_multiorder_2a8f42e8myapp_multiorderYCREATE INDEX "myapp_multiorder_2a8f42e8" ON "myapp_multiorder" ("level")jS3! indexmyapp_node_63f17a16myapp_nodeZCREATE INDEX "myapp_node_63f17a16" ON "myapp_node" ("parent_id")eT3!indexmyapp_node_3132601cmyapp_node[CREATE INDEX "myapp_node_3132601c" ON "myapp_node" ("does")dU3!indexmyapp_node_2669bc52myapp_node]CREATE INDEX "myapp_node_2669bc52" ON "myapp_node" ("zis")eV3!indexmyapp_node_d79af30cmyapp_node^CREATE INDEX "myapp_node_d79af30c" ON "myapp_node" ("work")WK9=indexmyapp_orderedinsertion_63f17a16myapp_orderedinsertion_CREATE INDEX "myapp_orderedinsertion_63f17a16" ON "myapp_orderedinsertion" ("parent_id")XK91indexmyapp_orderedinsertion_42b06ff6myapp_orderedinsertion`CREATE INDEX "myapp_orderedinsertion_42b06ff6" ON "myapp_orderedinsertion" ("lft")YK93indexmyapp_orderedinsertion_91543e5amyapp_orderedinsertionaCREATE INDEX "myapp_orderedinsertion_91543e5a" ON "myapp_orderedinsertion" ("rght")ZK99indexmyapp_orderedinsertion_efd07f28myapp_orderedinsertionbCREATE INDEX "myapp_orderedinsertion_efd07f28" ON "myapp_orderedinsertion" ("tree_id")[K95indexmyapp_orderedinsertion_2a8f42e8myapp_orderedinsertioncCREATE INDEX "myapp_orderedinsertion_2a8f42e8" ON "myapp_orderedinsertion" ("level")j\3! indexmyapp_tree_63f17a16myapp_treedCREATE INDEX "myapp_tree_63f17a16" ON "myapp_tree" ("parent_id")d]3!indexmyapp_tree_42b06ff6myapp_treeeCREATE INDEX "myapp_tree_42b06ff6" ON "myapp_tree" ("lft")e^3!indexmyapp_tree_91543e5amyapp_treefCREATE INDEX "myapp_tree_91543e5a" ON "myapp_tree" ("rght")h_3! indexmyapp_tree_efd07f28myapp_treegCREATE INDEX "myapp_tree_efd07f28" ON "myapp_tree" ("tree_id")f`3!indexmyapp_tree_2a8f42e8myapp_treehCREATE INDEX "myapp_tree_2a8f42e8" ON "myapp_tree" ("level")aK9=indexmyapp_newstylempttmeta_63f17a16myapp_newstylempttmetaiCREATE INDEX "myapp_newstylempttmeta_63f17a16" ON "myapp_newstylempttmeta" ("parent_id")bI97indexmyapp_newstylempttmeta_c71e76cmyapp_newstylempttmetajCREATE INDEX "myapp_newstylempttmeta_c71e76c" ON "myapp_newstylempttmeta" ("testing")cK93indexmyapp_newstylempttmeta_91543e5amyapp_newstylempttmetakCREATE INDEX "myapp_newstylempttmeta_91543e5a" ON "myapp_newstylempttmeta" ("rght")dK99indexmyapp_newstylempttmeta_efd07f28myapp_newstylempttmetalCREATE INDEX "myapp_newstylempttmeta_efd07f28" ON "myapp_newstylempttmeta" ("tree_id")eK95indexmyapp_newstylempttmeta_2a8f42e8myapp_newstylempttmetamCREATE INDEX "myapp_newstylempttmeta_2a8f42e8" ON "myapp_newstylempttmeta" ("level")rf7%indexmyapp_person_63f17a16myapp_personnCREATE INDEX "myapp_person_63f17a16" ON "myapp_person" ("parent_id")                            L-0?S R r ) H REh-UCMindexmyapp_autonowdatefieldmodel_efd07f28myapp_autonowdatefieldmodelCREATE INDEX "myapp_autonowdatefieldmodel_efd07f28" ON "myapp_autonowdatefieldmodel" ("tree_id")nC1)indexmyapp_custompkname_efd07f28myapp_custompknamevCREATE INDEX "myapp_custompkname_efd07f28" ON "myapp_custompkname" ("tree_id")oC1%indexmyapp_custompkname_2a8f42e8myapp_custompknamewCREATE INDEX "myapp_custompkname_2a8f42e8" ON "myapp_custompkname" ("level")4pSAaindexmyapp_referencingmodel_m2m_99486f13myapp_referencingmodel_m2myCREATE INDEX "myapp_referencingmodel_m2m_99486f13" ON "myapp_referencingmodel_m2m" ("referencingmodel_id"),qSAQindexmyapp_referencingmodel_m2m_42dc49bcmyapp_referencingmodel_m2mzCREATE INDEX "myapp_referencingmodel_m2m_42dc49bc" ON "myapp_referencingmodel_m2m" ("category_id")rK95indexmyapp_referencingmodel_37789993myapp_referencingmodel{CREATE INDEX "myapp_referencingmodel_3778lg7% indexmyapp_person_42b06ff6myapp_personoCREATE INDEX "myapp_person_42b06ff6" ON "myapp_person" ("lft")mh7% indexmyapp_person_91543e5amyapp_personpCREATE INDEX "myapp_person_91543e5a" ON "myapp_person" ("rght")pi7%indexmyapp_person_efd07f28myapp_personqCREATE INDEX "myapp_person_efd07f28" ON "myapp_person" ("tree_id")nj7% indexmyapp_person_2a8f42e8myapp_personrCREATE INDEX "myapp_person_2a8f42e8" ON "myapp_person" ("level")kC19indexmyapp_custompkname_b29d415bmyapp_custompknamesCREATE INDEX "myapp_custompkname_b29d415b" ON "myapp_custompkname" ("my_cusom_parent")lC1!indexmyapp_custompkname_42b06ff6myapp_custompknametCREATE INDEX "myapp_custompkname_42b06ff6" ON "myapp_custompkname" ("lft")mC1#indexmyapp_custompkname_91543e5amyapp_custompknameuCREATE INDEX "myapp_custompkname_91543e5a" ON "myapp_custompkname" ("rght")nC1)indexmyapp_custompkname_efd07f28myapp_custompknamevCREATE INDEX "myapp_custompkname_efd07f28" ON "myapp_custompkname" ("tree_id")oC1%indexmyapp_custompkname_2a8f42e8myapp_custompknamewCREATE INDEX "myapp_custompkname_2a8f42e8" ON "myapp_custompkname" ("level")4pSAaindexmyapp_referencingmodel_m2m_99486f13myapp_referencingmodel_m2myCREATE INDEX "myapp_referencingmodel_m2m_99486f13" ON "myapp_referencingmodel_m2m" ("referencingmodel_id"),qSAQindexmyapp_referencingmodel_m2m_42dc49bcmyapp_referencingmodel_m2mzCREATE INDEX "myapp_referencingmodel_m2m_42dc49bc" ON "myapp_referencingmodel_m2m" ("category_id")rK95indexmyapp_referencingmodel_37789993myapp_referencingmodel{CREATE INDEX "myapp_referencingmodel_37789993" ON "myapp_referencingmodel" ("fk_id")6sYGYindexmyapp_multitableinheritancea1_63f17a16myapp_multitableinheritancea1|CREATE INDEX "myapp_multitableinheritancea1_63f17a16" ON "myapp_multitableinheritancea1" ("parent_id")0tYGMindexmyapp_multitableinheritancea1_42b06ff6myapp_multitableinheritancea1}CREATE INDEX "myapp_multitableinheritancea1_42b06ff6" ON "myapp_multitableinheritancea1" ("lft")1uYGOindexmyapp_multitableinheritancea1_91543e5amyapp_multitableinheritancea1~CREATE INDEX "myapp_multitableinheritancea1_91543e5a" ON "myapp_multitableinheritancea1" ("rght")4vYGUindexmyapp_multitableinheritancea1_efd07f28myapp_multitableinheritancea1CREATE INDEX "myapp_multitableinheritancea1_efd07f28" ON "myapp_multitableinheritancea1" ("tree_id")3wYGQindexmyapp_multitableinheritancea1_2a8f42e8myapp_multitableinheritancea1CREATE INDEX "myapp_multitableinheritancea1_2a8f42e8" ON "myapp_multitableinheritancea1" ("level")1xYGMindexmyapp_multitableinheritanceb1_42b06ff6myapp_multitableinheritanceb1CREATE INDEX "myapp_multitableinheritanceb1_42b06ff6" ON "myapp_multitableinheritanceb1" ("lft")2yYGOindexmyapp_multitableinheritanceb1_91543e5amyapp_multitableinheritanceb1CREATE INDEX "myapp_multitableinheritanceb1_91543e5a" ON "myapp_multitableinheritanceb1" ("rght")5zYGUindexmyapp_multitableinheritanceb1_efd07f28myapp_multitableinheritanceb1CREATE INDEX "myapp_multitableinheritanceb1_efd07f28" ON "myapp_multitableinheritanceb1" ("tree_id")                        .W Y r  E Qy .k 5# indexmyapp_group_2a8f42e8myapp_groupCREATE INDEX "myapp_group_2a8f42e8" ON "myapp_group" ("level")m 5# indexmyapp_group_efd07f28myapp_groupCREATE INDEX "myapp_group_efd07f28" ON "myapp_group" ("tree_id")j 5#indexmyapp_group_91543e5amyapp_groupCREATE INDEX "myapp_group_91543e5a" ON "myapp_group" ("rght")i5#indexmyapp_group_42b06ff6myapp_groupCREATE INDEX "myapp_group_42b06ff6" ON "myapp_group" ("lft")o5#indexmyapp_group_63f17a16myapp_groupCREATE INDEX "myapp_group_63f17a16" ON "myapp_group" ("parent_id")3{YGQindexmyapp_multitableinheritanceb1_2a8f42e8myapp_multitableinheritanceb1CREATE INDEX "myapp_multitableinheritanceb1_2a8f42e8" ON "myapp_multitableinheritanceb1" ("level")7|YGYindexmyapp_multitableinheritanceb2_63f17a16myapp_multitableinheritanceb2CREATE INDEX "myapp_multitableinheritanceb2_63f17a16" ON "myapp_multitableinheritanceb2" ("parent_id")}E31indexmyapp_concretemodel_63f17a16myapp_concretemodelCREATE INDEX "myapp_concretemodel_63f17a16" ON "myapp_concretemodel" ("parent_id") ~E3%indexmyapp_concretemodel_42b06ff6myapp_concretemodelCREATE INDEX "myapp_concretemodel_42b06ff6" ON "myapp_concretemodel" ("lft") E3'indexmyapp_concretemodel_91543e5amyapp_concretemodelCREATE INDEX "myapp_concretemodel_91543e5a" ON "myapp_concretemodel" ("rght") E3-indexmyapp_concretemodel_efd07f28myapp_concretemodelCREATE INDEX "myapp_concretemodel_efd07f28" ON "myapp_concretemodel" ("tree_id") E3)indexmyapp_concretemodel_2a8f42e8myapp_concretemodelCREATE INDEX "myapp_concretemodel_2a8f42e8" ON "myapp_concretemodel" ("level")/UCQindexmyapp_autonowdatefieldmodel_63f17a16myapp_autonowdatefieldmodelCREATE INDEX "myapp_autonowdatefieldmodel_63f17a16" ON "myapp_autonowdatefieldmodel" ("parent_id"))UCEindexmyapp_autonowdatefieldmodel_42b06ff6myapp_autonowdatefieldmodelCREATE INDEX "myapp_autonowdatefieldmodel_42b06ff6" ON "myapp_autonowdatefieldmodel" ("lft")*UCGindexmyapp_autonowdatefieldmodel_91543e5amyapp_autonowdatefieldmodelCREATE INDEX "myapp_autonowdatefieldmodel_91543e5a" ON "myapp_autonowdatefieldmodel" ("rght")-UCMindexmyapp_autonowdatefieldmodel_efd07f28myapp_autonowdatefieldmodelCREATE INDEX "myapp_autonowdatefieldmodel_efd07f28" ON "myapp_autonowdatefieldmodel" ("tree_id")+UCIindexmyapp_autonowdatefieldmodel_2a8f42e8myapp_autonowdatefieldmodelCREATE INDEX "myapp_autonowdatefieldmodel_2a8f42e8" ON "myapp_autonowdatefieldmodel" ("level")     django-mptt-0.8.0/tests/requirements.txt0000644000076500000240000000003412635466021021653 0ustar cdestigterstaff00000000000000mock-django Django coverage django-mptt-0.8.0/tests/runtests.sh0000755000076500000240000000117712515002575020623 0ustar cdestigterstaff00000000000000#!/bin/sh set -e export PYTHONPATH="./" export DJANGO_SETTINGS_MODULE='settings' if [ `which django-admin.py` ] ; then export DJANGO_ADMIN=`which django-admin.py` else export DJANGO_ADMIN=`which django-admin` fi if [ `which coverage` ] ; then export COVERAGE='coverage run' else export COVERAGE='' fi export args="$@" if [ -z "$args" ] ; then # avoid running the tests for django.contrib.* (they're in INSTALLED_APPS) export args=myapp fi $COVERAGE $DJANGO_ADMIN test --traceback --settings=$DJANGO_SETTINGS_MODULE --verbosity 2 --pythonpath="../" "$args" if [ `which coverage` ] ; then coverage report fi django-mptt-0.8.0/tests/settings.py0000644000076500000240000000262212634472470020612 0ustar cdestigterstaff00000000000000from __future__ import unicode_literals import os DIRNAME = os.path.dirname(__file__) DEBUG = True DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': 'mydatabase' } } INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.messages', 'django.contrib.sessions', 'mptt', 'myapp', ) STATIC_URL = '/static/' SECRET_KEY = 'abc123' MIDDLEWARE_CLASSES = ( 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', ) TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] ROOT_URLCONF = 'myapp.urls' # Swappable model testing MPTT_SWAPPABLE_MODEL = 'myapp.SwappedInModel' django-mptt-0.8.0/tests/settings.pyc0000644000076500000240000000307512635626176020765 0ustar cdestigterstaff00000000000000 8urVcsddlmZddlZejeZeZiidd6dd6d6Z dZ d Z d Z d Z id d 6gd6ed6iddddgd6d6gZdZdZdS(i(sunicode_literalsNudjango.db.backends.sqlite3uENGINEu mydatabaseuNAMEudefault(udjango.contrib.adminudjango.contrib.authudjango.contrib.contenttypesudjango.contrib.messagesudjango.contrib.sessionsumpttumyappu/static/uabc123(u)django.middleware.common.CommonMiddlewareu4django.contrib.sessions.middleware.SessionMiddlewareu)django.middleware.csrf.CsrfViewMiddlewareu7django.contrib.auth.middleware.AuthenticationMiddlewareu4django.contrib.messages.middleware.MessageMiddlewareu>django.contrib.auth.middleware.SessionAuthenticationMiddlewareu/django.template.backends.django.DjangoTemplatesuBACKENDuDIRSuAPP_DIRSu(django.template.context_processors.debugu*django.template.context_processors.requestu+django.contrib.auth.context_processors.authu3django.contrib.messages.context_processors.messagesucontext_processorsuOPTIONSu myapp.urlsumyapp.SwappedInModel(t __future__tunicode_literalstostpathtdirnamet__file__tDIRNAMEtTruetDEBUGt DATABASEStINSTALLED_APPSt STATIC_URLt SECRET_KEYtMIDDLEWARE_CLASSESt TEMPLATESt ROOT_URLCONFtMPTT_SWAPPABLE_MODEL(((t8/Users/cdestigter/checkout/django-mptt/tests/settings.pyts,