Utilities for implementing a modified pre-order traversal tree in django.

django-mptt

Utilities for implementing Modified Preorder Tree Traversal with your Django Models and working with trees of Model instances.

Build Status

Project home: https://github.com/django-mptt/django-mptt/

Documentation: https://django-mptt.readthedocs.io/

Discussion group: https://groups.google.com/forum/#!forum/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:

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 3.5+
  • A supported version of Django (currently 1.11+)

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.
  • Admin classes for visualizing and modifying trees in Django's administration interface.
Comments
  • Django 1.10 support: can't set attribute _default_manager at startup

    Django 1.10 support: can't set attribute _default_manager at startup

    While testing Django 1.10, mptt seems to mess with internal API of the framework, that has changed since 1.9. Here's the stacktrace I get at the loading of manage.py runserver:

    $ ./manage.py runserver
    Unhandled exception in thread started by <function wrapper at 0x7f32e681faa0>
    Traceback (most recent call last):
      File "My_VENV/lib/python2.7/site-packages/django/utils/autoreload.py", line 226, in wrapper
        fn(*args, **kwargs)
      File "My_VENV/lib/python2.7/site-packages/django/core/management/commands/runserver.py", line 113, in inner_run
        autoreload.raise_last_exception()
      File "My_VENV/lib/python2.7/site-packages/django/utils/autoreload.py", line 249, in raise_last_exception
        six.reraise(*_exception)
      File "My_VENV/lib/python2.7/site-packages/django/utils/autoreload.py", line 226, in wrapper
        fn(*args, **kwargs)
      File "My_VENV/lib/python2.7/site-packages/django/__init__.py", line 27, in setup
        apps.populate(settings.INSTALLED_APPS)
      File "My_VENV/lib/python2.7/site-packages/django/apps/registry.py", line 108, in populate
        app_config.import_models(all_models)
      File "My_VENV/lib/python2.7/site-packages/django/apps/config.py", line 199, in import_models
        self.models_module = import_module(models_module_name)
      File "/usr/lib/python2.7/importlib/__init__.py", line 37, in import_module
        __import__(name)
      File "My_VENV/lib/python2.7/site-packages/mptt/models.py", line 379, in <module>
        class MPTTModel(six.with_metaclass(MPTTModelBase, models.Model)):
      File "My_VENV/lib/python2.7/site-packages/django/utils/six.py", line 808, in __new__
        return meta(name, bases, d)
      File "My_VENV/lib/python2.7/site-packages/mptt/models.py", line 262, in __new__
        cls = super_new(meta, class_name, bases, class_dict)
      File "My_VENV/lib/python2.7/site-packages/django/db/models/base.py", line 157, in __new__
        new_class.add_to_class(obj_name, obj)
      File "My_VENV/lib/python2.7/site-packages/django/db/models/base.py", line 316, in add_to_class
        value.contribute_to_class(cls, name)
      File "My_VENV/lib/python2.7/site-packages/mptt/managers.py", line 81, in contribute_to_class
        super(TreeManager, self).contribute_to_class(model, name)
      File "My_VENV/lib/python2.7/site-packages/django/db/models/manager.py", line 120, in contribute_to_class
        setattr(model, name, ManagerDescriptor(self))
    AttributeError: can't set attribute
    

    In contribute_to_class (the method in Django file), arguments are model=<class 'mptt.models.MPTTModel'> name=_default_manager. It looks like _default_manager is now read only.

    It might be linked to the following change (from the release notes):

    The new Meta.base_manager_name and Meta.default_manager_name options allow controlling the _base_manager and _default_manager, respectively.

  • Tree gets corrupted when concurrently adding multiple root objects

    Tree gets corrupted when concurrently adding multiple root objects

    Reproduced with django-mptt 0.5.2

    Steps to reproduce:

    1. Concurrently add 5 root MPTTModel objects
    2. Check the tree ids of the newly created objects

    Expected: Each object has a separate tree_id

    Actual: All tree ids are the same

    The problem is in:

    def insert_node(self, node, target, position='last-child', save=False, allow_existing_pk=False):
        if self._base_manager:
            return self._base_manager.insert_node(node, target, position=position, save=save)
        if node.pk and not allow_existing_pk and _exists(self.filter(pk=node.pk)):
            raise ValueError(_('Cannot insert a node which has already been saved.'))
        if target is None:
            setattr(node, self.left_attr, 1)
            setattr(node, self.right_attr, 2)
            setattr(node, self.level_attr, 0)
            setattr(node, self.tree_id_attr, self._get_next_tree_id())
            setattr(node, self.parent_attr, None)
    

    All requests call insert_node before the new MPTTModel gets saved => all new objects will get the same tree id...

    I don't know how this can be fixed... Probably acquire read locks on the entire table when adding new objects? The problem with this is that it would be a performance overkill.

  • Make `_get_queryset_relatives` generate more efficient SQL

    Make `_get_queryset_relatives` generate more efficient SQL

    Explanation:

    I'll use the following tree from this article in the explanation that follows:

    mptt tree

    Given the tree above, we know that in order to retrieve the descendants of the Meat node, we need to execute an SQL query that looks like this:

    SELECT * FROM tree WHERE lft BETWEEN 12 AND 17;
    

    And for the ancestors of the Pork node, we'd need to execute an SQL query that looks like this:

    SELECT * FROM tree WHERE lft < 15 AND rght > 16;
    

    But what if we wanted the all of the descendants of both the Fruit and Meat nodes? We could do the following:

    SELECT * FROM tree WHERE lft BETWEEN 2 AND 11 OR lft BETWEEN 12 AND 17;
    

    And if we wanted to retrieve all of the ancestors of the Red, Beef and Pork nodes, we could do this:

    SELECT * FROM tree WHERE lft < 3 AND rght > 6 OR lft < 13 AND rght > 14 OR lft < 15 AND rght > 16;
    

    Now take a look at that last query again. Couldn't it be simplified and still accomplish the same result? Sure it could:

    SELECT * FROM tree WHERE lft < 3 AND rght > 6 OR lft < 15 AND rght > 14;
    

    The difference is that we've put the nodes into groups and then identified the highest lft value and the lowest rght value per group. A group is defined as any collection of nodes that share a tree_id and a parent_id. Any ancestors of these groups of nodes will have a lft value less than the highest lft value and a rght value greater than the lowest rght value.

    ~~For the descendants of a group of nodes, it is just the opposite:~~ (See the edit below, addressing the problem with this method.) Any descendants of a group of nodes will have a lft value greater than the lowest lft value and a rght value less than the highest rght value. For example, if we wanted to retrieve the descendants of Red, Yellow, and Meat:

    SELECT * FROM tree WHERE lft > 3 AND rght < 10 OR lft > 12 AND rght < 17;
    

    (Edit: 30 Sept. 2014) However, this method returns more nodes than is required, as pointed out by craigds in the comments below. I'm currently working on a method to fix this.

    Why?

    Both get_queryset_descendants and get_queryset_ancestors use a Q object in order to accomplish their job: retrieving either the ancestors or descendants of a queryset (i.e., group) of nodes. This Q object can become quite large and in some cases it can raise an OperationalError if there are too many SQL variables involved. For example, sqlite only allows a single query to have <1000 SQL variables; well, if you wanted to get the ancestors of a queryset that contains 400 distinct nodes, you're looking at a query that contains 1200 variables. Now we could discuss the various techniques involved in shrinking that queryset, but with sufficiently large sets of data, sometimes there just isn't a way to do it.

    That's where the aggregate kwarg comes into play. This kwarg tells get_queryset_ancestors and get_queryset_descendants to group nodes first by their tree_id then by their parent_id. Then, for each "group", it finds the the appropriate lft and rght values to ensure that there is only one Q object per group of nodes. To reuse the example above, if you had a queryset with 400 distinct nodes but 200 of them shared the same tree_id and parent_id and the other 200 shared a different tree_id/parent_id combination, you would have two groups, with a total of 6 SQL variables (tree_id, lft, rght variables for each group.) Not only does this make for faster queries, but it has the potential to avoid the scenario I mentioned earlier where too many SQL variables raises an OperationalError.

    Examples:

    models.py

    from django.db import models
    from mptt.models import TreeForeignKey, MPTTModel
    
    class Directory(MPTTModel):
        parent = TreeForeignKey(
            'self',
            null = True,
            blank = True,
            related_name = 'children')
    
        path = models.CharField(
            max_length = 255)
    
        def __unicode__(self):
            return self.path
    
    
    class File(models.Model):
        parent = models.ForeignKey(
            Directory,
            null = True,
            blank = True,
            related_name = 'files')
    
        path = models.CharField(
            max_length = 255)
    
        def __unicode__(self):
            return self.path
    

    Our dataset is a directory structure that matches the example tree in the above graphic:

    mkdir -p Food/Fruit/Red/Cherry
    mkdir -p Food/Fruit/Yellow/Banana
    mkdir -p Food/Meat/{Beef,Pork}
    

    Then:

    import os
    from myapp.filesanddirectories.models import *
    
    for root, directories, files in os.walk('/Food'):
        parent_dir, created = Directory.objects.get_or_create(path = root)
        for directory in directories:
            path = os.path.join(root, directory)
            print path
            object, created = Directory.objects.get_or_create(path = path, parent = parent_dir)
            if created:
                print("CREATED: %s" % path)
    

    Running in the python interpreter (with debugging output):

    >>> from myapp.filesanddirectories.models import *
    >>> from django.db.models import Q
    >>> from django.db import transaction
    >>> dirs = Directory.objects.filter(Q(path = '/Food/Fruit') | Q(path = '/Food/Meat'))
    >>> dirs
    [<Directory: /Food/Fruit>, <Directory: /Food/Meat>]
    >>> 
    >>> ### get_queryset_descendants ###
    >>> 
    >>> transaction.commit()
    >>> Directory.objects.get_queryset_descendants(dirs)
    Finished in 0.000601
    Q Object: (OR: (AND: (u'lft__gt', 2), (u'tree_id', 1), (u'rght__lt', 11)), (AND: (u'lft__gt', 12), (u'tree_id', 1), (u'rght__lt', 17)))
    [<Directory: /Food/Fruit/Red>, <Directory: /Food/Fruit/Red/Cherry>, <Directory: /Food/Fruit/Yellow>, <Directory: /Food/Fruit/Yellow/Banana>, <Directory: /Food/Meat/Pork>, <Directory: /Food/Meat/Beef>]
    >>> 
    >>> transaction.commit()
    >>> Directory.objects.get_queryset_descendants(dirs, aggregate=True)
    Finished in 0.000036
    Q Object: (AND: (u'lft__gt', 2), (u'tree_id', 1), (u'rght__lt', 17))
    [<Directory: /Food/Fruit/Red>, <Directory: /Food/Fruit/Red/Cherry>, <Directory: /Food/Fruit/Yellow>, <Directory: /Food/Fruit/Yellow/Banana>, <Directory: /Food/Meat/Pork>, <Directory: /Food/Meat/Beef>]
    >>> 
    >>> transaction.commit()
    >>> Directory.objects.get_queryset_descendants(dirs, include_self=True)
    Finished in 0.000043
    Q Object: (OR: (AND: (u'lft__gt', 1), (u'tree_id', 1), (u'rght__lt', 12)), (AND: (u'lft__gt', 11), (u'tree_id', 1), (u'rght__lt', 18)))
    [<Directory: /Food/Fruit>, <Directory: /Food/Fruit/Red>, <Directory: /Food/Fruit/Red/Cherry>, <Directory: /Food/Fruit/Yellow>, <Directory: /Food/Fruit/Yellow/Banana>, <Directory: /Food/Meat>, <Directory: /Food/Meat/Pork>, <Directory: /Food/Meat/Beef>]
    >>> 
    >>> transaction.commit()
    >>> Directory.objects.get_queryset_descendants(dirs, include_self=True, aggregate=True)
    Finished in 0.000027
    Q Object: (AND: (u'lft__gt', 1), (u'tree_id', 1), (u'rght__lt', 18))
    [<Directory: /Food/Fruit>, <Directory: /Food/Fruit/Red>, <Directory: /Food/Fruit/Red/Cherry>, <Directory: /Food/Fruit/Yellow>, <Directory: /Food/Fruit/Yellow/Banana>, <Directory: /Food/Meat>, <Directory: /Food/Meat/Pork>, <Directory: /Food/Meat/Beef>]
    >>> 
    >>> 
    >>> ### get_queryset_ancestors ###
    >>> 
    >>> transaction.commit()
    >>> Directory.objects.get_queryset_ancestors(dirs)
    Finished in 0.000042
    Q Object: (OR: (AND: (u'rght__gt', 11), (u'lft__lt', 2), (u'tree_id', 1)), (AND: (u'rght__gt', 17), (u'lft__lt', 12), (u'tree_id', 1)))
    [<Directory: /Food>]
    >>> 
    >>> transaction.commit()
    >>> Directory.objects.get_queryset_ancestors(dirs, aggregate=True)
    Finished in 0.000027
    Q Object: (AND: (u'rght__gt', 11), (u'lft__lt', 12), (u'tree_id', 1))
    [<Directory: /Food>]
    >>> 
    >>> transaction.commit()
    >>> Directory.objects.get_queryset_ancestors(dirs, include_self=True)
    Finished in 0.000045
    Q Object: (OR: (AND: (u'rght__gt', 10), (u'lft__lt', 3), (u'tree_id', 1)), (AND: (u'rght__gt', 16), (u'lft__lt', 13), (u'tree_id', 1)))
    [<Directory: /Food>, <Directory: /Food/Fruit>, <Directory: /Food/Meat>]
    >>> 
    >>> transaction.commit()
    >>> Directory.objects.get_queryset_ancestors(dirs, include_self=True, aggregate=True)
    Finished in 0.000091
    Q Object: (AND: (u'rght__gt', 10), (u'lft__lt', 13), (u'tree_id', 1))
    [<Directory: /Food>, <Directory: /Food/Fruit>, <Directory: /Food/Meat>]
    >>> 
    

    Summary

    Please tear this to shreds. Seriously. I haven't been able to find any bugs with this implementation but that doesn't mean that they aren't there. Tests pass though so ... yay. :-)

  • Defer update of mptt fields (left/right/tree/level) until a batch of changes have been made

    Defer update of mptt fields (left/right/tree/level) until a batch of changes have been made

    Originally GC 46

    I'm using django-mptt to manage the hierarchy of an ecommerce site with a lot of products.

    We regularly sync large batches of products from another systems to the web database and these syncs can include a lot of changes to the category tree.

    What would be good is if there was a way to add/update/delete the tree without modifying the mptt fields then to do all the mptt changes once at the end.

    Would the easiest way to just use the default manager to add new nodes, then to rebuild the tree using the custom method? It would be nice if there was a standard way of doing this.

  • [has patch] add_related_count() can accept related querysets, not related model.

    [has patch] add_related_count() can accept related querysets, not related model.

    Originally GC 38

    This makes it much more usable. I'll show why.

    Old fashion:

    Model.tree.add_related_count(Model.objects.all(), AnotherModel, 'rel_col', 'cnt', cumulative=True) makes this query:

    SELECT (SELECT COUNT(*) FROM another_model WHERE ( rel_col_id IN ( SELECT m2.id FROM model m2 WHERE m2.mptt_tree_id = model.mptt_tree_id AND m2.mptt_left BETWEEN model.mptt_left AND model.mptt_right ) )) AS cnt, col1, colN FROM model

    Good, but we cant change add more difficult subquery. My patch allows to use related querysets instead of related model (which being determined automatically).

    Model.tree.add_related_count(Model.objects.all(), AnotherModel.objects.all (), 'rel_col', 'cnt', cumulative=True) makes exactly the same query. But this:

    Model.tree.add_related_count(Model.objects.all(), AnotherModel.objects.filter(field=1).filter(field=2), 'rel_col', 'cnt', cumulative=True) makes this:

    SELECT (SELECT COUNT(*) FROM another_model WHERE field = 1 AND field = 2 AND ( field = 2 AND field = rel_col_id IN ( SELECT m2.id FROM model m2 WHERE m2.mptt_tree_id = model.mptt_tree_id AND m2.mptt_left BETWEEN model.mptt_left AND model.mptt_right ) )) AS cnt, col1, colN FROM model

    In my case: I have categories, tasks and users. With my patch I'm able to fill task counts for specified user in all categories :). Even more! Using non-documented "having" ability in django orm queries, I can select all categories which have >0 tasks for specified user. E.g.:

    qs = AnotherModel.objects.filter(field=1).filter(field=2), 'rel_col', 'cnt', cumulative=True) qs.query.having.append('%s > %s' % (connection.ops.quote_name('cnt'), 0))

    Which will have a result:

    SELECT (SELECT COUNT(*) FROM another_model WHERE field = 1 AND field = 2 AND ( field = 2 AND field = rel_col_id IN ( SELECT m2.id FROM model m2 WHERE m2.mptt_tree_id = model.mptt_tree_id AND m2.mptt_left BETWEEN model.mptt_left AND model.mptt_right ) )) AS cnt, col1, colN FROM model HAVING cnt > 0

    All of these examples are for cumulative queries. But also apply to non- cumulative queries as well. Woohooo! :)

  • Admin tree management

    Admin tree management

    Originally GC 33

    Attached is a patch that will allow a tree to be managed in the admin interface using the jQuery NestedSortableWidget (http://code.google.com/p/nestedsortables/wiki/NestedSortableWidgetDocumentation). I have shamelessly lifted some code from the Tusk CMS, credit goes to them for the idea (http://code.google.com/p/tusk-cms/). It's a little messy and hacky at the moment, but it should work.

    Some sample usage in admin.py:

    from django.contrib import admin from mptt.admin import MpttModelAdmin

    class CategoryAdmin(MpttModelAdmin): list_display = ('title',)

    admin.site.register(Category, CategoryAdmin)

    The files in mptt_media.tar.gz need to be put in your MEDIA_URL directory.

  • Make MPTTModel.tree the default manager

    Make MPTTModel.tree the default manager

    As far as I can tell there's no reason why the TreeManager is not the default manager. This confuses me and probably everyone else.

    For 0.5, I think .objects should refer to the same manager as .tree . It can be overridden if a subclass wants a different default manager.

  • Release a version compatible with Django 1.9

    Release a version compatible with Django 1.9

    The current master seems to work fine on 1.9 but the last released version does not. Seeing that Django 1.9 has been officially released, it would be nice to have a compatible release of django-mptt.

  • Django 1.8 support

    Django 1.8 support

    I am alpha testing Django 1.8

    Meta API is formalised in 1.8 and as a result, method get_fields_with_model is deprecated.

    That method is used here https://github.com/django-mptt/django-mptt/blob/6a41fd4384f44ff1d66c6cded4dcd37a8767b9a4/mptt/managers.py#L76

    Because of this, project which is on 1.7 is not able to to run on 1.8. Please refer to migration docs https://docs.djangoproject.com/en/1.8/ref/models/meta/#migrating-old-meta-api

  • Unique constraint failure when moving nodes

    Unique constraint failure when moving nodes

    Hi !

    I have a MPTT tree with an unique constraint on the parent and a "code" field. With such a setup, I get an IntegrityError when I reparent a child if there's already a child with the code, even if I change the code so that they don't conflict !

    This means I need to change the code, save, then reparent, and save again. This also means I'll get headaches if the new code is conflicting in the initial parent.

    I'm surprised to find a bug with a setup that seems extremely common (wanting an unique property per parent). Did I miss something ?

    Anyway, thanks a lot for the tool !

    Here's the model I tested with :

    class TestModel( MPTTModel ):
        class Meta:
            unique_together = (('parent','code',),)
        parent = TreeForeignKey('self', null=True)
        code = models.CharField(max_length=10)
    

    And here's the steps to reproduce

    from MyApp.models import TestModel
    a = TestModel.objects.create(code='a',parent=None)
    b = TestModel.objects.create(code='b',parent=None)
    a1 = TestModel.objects.create(code='1',parent=a)
    b1 = TestModel.objects.create(code='1',parent=b)
    
    b1.code = '2'
    b1.parent = a
    b1.save() # THIS THROWS IntegrityError: UNIQUE constraint failed
    
  • Removing mppt.register and putting metaclass is not funny ;/

    Removing mppt.register and putting metaclass is not funny ;/

    There is a lot of projects (including my code) using own metaclasses and now I could not use mppt whit my models. mptt.register was the best way to integrate models with mptt.

    For example: Now there is no way to use mppt and django-transmeta. Sux!

    I'm back to 0.3 version.

  • Update mptt_change_list.html to include {% change_list_object_tools %}

    Update mptt_change_list.html to include {% change_list_object_tools %}

    It seems that the admin template admin/mptt-change-list.html currently does not have the {% change_list_object_tools %} tag present, which causes the Add button to not be rendered. This change ensures that the Add button is rendered even when the django admin has some kind of skin added to it (eg. jazzmin).

    https://stackoverflow.com/questions/68722249/django-jazzmin-add-button-disappeared-after-adding-mptt-admin/69230357#69230357

  • Problem occured when integrating with django-parler

    Problem occured when integrating with django-parler

    The alternative answer was givin here, but you can improve that in the new version:

    https://stackoverflow.com/questions/68722249/django-jazzmin-add-button-disappeared-after-adding-mptt-admin/

  • How do I use order_by?

    How do I use order_by?

    Good afternoon. I want to sort the tree, but I understand that from some level the elements start to be displayed in the wrong level

    obj = Model.objects.filter(level__lte=10).annotate(
        children_count=Count('children'),
        descendants_count=Floor((F('rght') - F('lft') - 1) / 2),
        comments_count=Count('comments'),
    ).order_by('tree_id', 'level', 'children_count', '-slug')
    

    Tell me, can you somehow solve the sorting problem?

  • TypeError: unsupported operand type(s) for +=: 'set' and 'tuple

    TypeError: unsupported operand type(s) for +=: 'set' and 'tuple

    When upgrading from Django-mptt 0.8.6 to 0.11.0 or later version, Django migrations fail with unsupported type error: TypeError: unsupported operand type(s) for +=: 'set' and 'tuple' The error is traced to: https://github.com/django-mptt/django-mptt/blob/7a6a54c6d2572a45ea63bd639c25507108fff3e6/mptt/models.py#L377

    The code is expecting a tuple so when a set is passed, it throws this error. Is it possible to make the code more robust to handle both sets and tuples?

    We came across this error when upgrading from Django 1.11 to 2.2.24 which required mptt upgrade from 0.86 to 0.11 due to template loader errors. This error did not occur before upgrading.

    Failing migrations stack trace:

    (20211018-eregs-env) macadmins-mbp-5:fec-eregs pkasireddy$ python manage.py migrate
    System check identified some issues:
    
    WARNINGS:
    regcore.Diff: (models.W042) Auto-created primary key used when not defining a primary key type, by default 'django.db.models.AutoField'.
    	HINT: Configure the DEFAULT_AUTO_FIELD setting or the AppConfig.default_auto_field attribute to point to a subclass of AutoField, e.g. 'django.db.models.BigAutoField'.
    regcore.Layer: (models.W042) Auto-created primary key used when not defining a primary key type, by default 'django.db.models.AutoField'.
    	HINT: Configure the DEFAULT_AUTO_FIELD setting or the AppConfig.default_auto_field attribute to point to a subclass of AutoField, e.g. 'django.db.models.BigAutoField'.
    regcore.NoticeCFRPart: (models.W042) Auto-created primary key used when not defining a primary key type, by default 'django.db.models.AutoField'.
    	HINT: Configure the DEFAULT_AUTO_FIELD setting or the AppConfig.default_auto_field attribute to point to a subclass of AutoField, e.g. 'django.db.models.BigAutoField'.
    Operations to perform:
     Apply all migrations: auth, contenttypes, regcore, regulations, sessions
    Running migrations:
     Applying contenttypes.0001_initial... OK
     Applying contenttypes.0002_remove_content_type_name... OK
     Applying auth.0001_initial... OK
     Applying auth.0002_alter_permission_name_max_length... OK
     Applying auth.0003_alter_user_email_max_length... OK
     Applying auth.0004_alter_user_username_opts... OK
     Applying auth.0005_alter_user_last_login_null... OK
     Applying auth.0006_require_contenttypes_0002... OK
     Applying auth.0007_alter_validators_add_error_messages... OK
     Applying auth.0008_alter_user_username_max_length... OK
     Applying auth.0009_alter_user_last_name_max_length... OK
     Applying auth.0010_alter_group_name_max_length... OK
     Applying auth.0011_update_proxy_permissions... OK
     Applying auth.0012_alter_user_first_name_max_length... OK
     Applying regcore.0001_initial... OK
     Applying regcore.0002_mptt_add_fields... OK
     Applying regcore.0003_mptt_copy_children...Traceback (most recent call last):
     File "manage.py", line 10, in <module>
      execute_from_command_line(sys.argv)
     File "/Users/pkasireddy/.pyenv/versions/20211018-eregs-env/lib/python3.7/site-packages/django/core/management/__init__.py", line 419, in execute_from_command_line
      utility.execute()
     File "/Users/pkasireddy/.pyenv/versions/20211018-eregs-env/lib/python3.7/site-packages/django/core/management/__init__.py", line 413, in execute
      self.fetch_command(subcommand).run_from_argv(self.argv)
     File "/Users/pkasireddy/.pyenv/versions/20211018-eregs-env/lib/python3.7/site-packages/django/core/management/base.py", line 354, in run_from_argv
      self.execute(*args, **cmd_options)
     File "/Users/pkasireddy/.pyenv/versions/20211018-eregs-env/lib/python3.7/site-packages/django/core/management/base.py", line 398, in execute
      output = self.handle(*args, **options)
     File "/Users/pkasireddy/.pyenv/versions/20211018-eregs-env/lib/python3.7/site-packages/django/core/management/base.py", line 89, in wrapped
      res = handle_func(*args, **kwargs)
     File "/Users/pkasireddy/.pyenv/versions/20211018-eregs-env/lib/python3.7/site-packages/django/core/management/commands/migrate.py", line 246, in handle
      fake_initial=fake_initial,
     File "/Users/pkasireddy/.pyenv/versions/20211018-eregs-env/lib/python3.7/site-packages/django/db/migrations/executor.py", line 117, in migrate
      state = self._migrate_all_forwards(state, plan, full_plan, fake=fake, fake_initial=fake_initial)
     File "/Users/pkasireddy/.pyenv/versions/20211018-eregs-env/lib/python3.7/site-packages/django/db/migrations/executor.py", line 147, in _migrate_all_forwards
      state = self.apply_migration(state, migration, fake=fake, fake_initial=fake_initial)
     File "/Users/pkasireddy/.pyenv/versions/20211018-eregs-env/lib/python3.7/site-packages/django/db/migrations/executor.py", line 227, in apply_migration
      state = migration.apply(state, schema_editor)
     File "/Users/pkasireddy/.pyenv/versions/20211018-eregs-env/lib/python3.7/site-packages/django/db/migrations/migration.py", line 126, in apply
      operation.database_forwards(self.app_label, schema_editor, old_state, project_state)
     File "/Users/pkasireddy/.pyenv/versions/20211018-eregs-env/lib/python3.7/site-packages/django/db/migrations/operations/special.py", line 190, in database_forwards
      self.code(from_state.apps, schema_editor)
     File "/Users/pkasireddy/.pyenv/versions/3.7.10/envs/20211018-eregs-env/src/regcore/regcore/migrations/0003_mptt_copy_children.py", line 17, in rebuild
      mptt.register(Regulation)
     File "/Users/pkasireddy/.pyenv/versions/20211018-eregs-env/lib/python3.7/site-packages/mptt/__init__.py", line 13, in register
      return MPTTModelBase.register(*args, **kwargs)
     File "/Users/pkasireddy/.pyenv/versions/20211018-eregs-env/lib/python3.7/site-packages/mptt/models.py", line 352, in register
      cls._meta.index_together += (index_together,)
    TypeError: unsupported operand type(s) for +=: 'set' and 'tuple
    
    
    
    
  • Ancestors and descendants through proxy model returns as parent model objects instead proxy model objects.

    Ancestors and descendants through proxy model returns as parent model objects instead proxy model objects.

    class Car(MPTTModel):
        pass
    
    class BMW(Car):
        class Meta:
            proxy = True
    
        def bmw_method(self):        
            pass
    
    class Audi(Car):
        class Meta:
            proxy = True
    
        def audi_method(self):        
            pass
    
    bmw = BMW.objects.get(pk=1)
    bmw_descendants = bmw.get_descendants()
    

    bmw_descendants queryset of Car model objects, I think it should return BMW model objects.

    • custom manager with default name objects on each proxy model not working.
    • default_manager_name with custom manager with custom name on each proxy model not working.
Related tags
Django-fast-export - Utilities for quickly streaming CSV responses to the client

django-fast-export Utilities for quickly streaming CSV responses to the client T

Aug 24, 2022
Django URL Shortener is a Django app to to include URL Shortening feature in your Django Project

Django URL Shortener Django URL Shortener is a Django app to to include URL Shortening feature in your Django Project Install this package to your Dja

Nov 18, 2021
Utilities to make function-based views cleaner, more efficient, and better tasting.

django-fbv Utilities to make Django function-based views cleaner, more efficient, and better tasting. ?? ?? Complete documentation: https://django-fbv

Nov 24, 2022
Meta package to combine turbo-django and stimulus-django

Hotwire + Django This repository aims to help you integrate Hotwire with Django ?? Inspiration might be taken from @hotwired/hotwire-rails. We are sti

Aug 9, 2022
django-reversion is an extension to the Django web framework that provides version control for model instances.

django-reversion django-reversion is an extension to the Django web framework that provides version control for model instances. Requirements Python 3

Dec 1, 2022
Django-environ allows you to utilize 12factor inspired environment variables to configure your Django application.
Django-environ allows you to utilize 12factor inspired environment variables to configure your Django application.

Django-environ django-environ allows you to use Twelve-factor methodology to configure your Django application with environment variables. import envi

Dec 5, 2022
Rosetta is a Django application that eases the translation process of your Django projects
Rosetta is a Django application that eases the translation process of your Django projects

Rosetta Rosetta is a Django application that facilitates the translation process of your Django projects. Because it doesn't export any models, Rosett

Nov 30, 2022
Cookiecutter Django is a framework for jumpstarting production-ready Django projects quickly.
Cookiecutter Django is a framework for jumpstarting production-ready Django projects quickly.

Cookiecutter Django Powered by Cookiecutter, Cookiecutter Django is a framework for jumpstarting production-ready Django projects quickly. Documentati

Dec 1, 2022
Django project starter on steroids: quickly create a Django app AND generate source code for data models + REST/GraphQL APIs (the generated code is auto-linted and has 100% test coverage).

Create Django App ?? We're a Django project starter on steroids! One-line command to create a Django app with all the dependencies auto-installed AND

Oct 19, 2022
django-quill-editor makes Quill.js easy to use on Django Forms and admin sites
django-quill-editor makes Quill.js easy to use on Django Forms and admin sites

django-quill-editor django-quill-editor makes Quill.js easy to use on Django Forms and admin sites No configuration required for static files! The ent

Nov 9, 2022
A Django chatbot that is capable of doing math and searching Chinese poet online. Developed with django, channels, celery and redis.

Django Channels Websocket Chatbot A Django chatbot that is capable of doing math and searching Chinese poet online. Developed with django, channels, c

Oct 28, 2022
A handy tool for generating Django-based backend projects without coding. On the other hand, it is a code generator of the Django framework.
A handy tool for generating Django-based backend projects without coding. On the other hand, it is a code generator of the Django framework.

Django Sage Painless The django-sage-painless is a valuable package based on Django Web Framework & Django Rest Framework for high-level and rapid web

Sep 15, 2022
A beginner django project and also my first Django project which involves shortening of a longer URL into a short one using a unique id.

Django-URL-Shortener A beginner django project and also my first Django project which involves shortening of a longer URL into a short one using a uni

Aug 8, 2021
Dockerizing Django with Postgres, Gunicorn, Nginx and Certbot. A fully Django starter project.

Dockerizing Django with Postgres, Gunicorn, Nginx and Certbot ?? Features A Django stater project with fully basic requirements for a production-ready

Jun 27, 2022
pytest-django allows you to test your Django project/applications with the pytest testing tool.

pytest-django allows you to test your Django project/applications with the pytest testing tool.

Nov 30, 2022
APIs for a Chat app. Written with Django Rest framework and Django channels.
APIs for a Chat app. Written with Django Rest framework and Django channels.

ChatAPI APIs for a Chat app. Written with Django Rest framework and Django channels. The documentation for the http end points can be found here This

Sep 9, 2022
django-dashing is a customisable, modular dashboard application framework for Django to visualize interesting data about your project. Inspired in the dashboard framework Dashing
django-dashing is a customisable, modular dashboard application framework for Django to visualize interesting data about your project. Inspired in the dashboard framework Dashing

django-dashing django-dashing is a customisable, modular dashboard application framework for Django to visualize interesting data about your project.

Nov 30, 2022
Django-MySQL extends Django's built-in MySQL and MariaDB support their specific features not available on other databases.
Django-MySQL extends Django's built-in MySQL and MariaDB support their specific features not available on other databases.

Django-MySQL The dolphin-pony - proof that cute + cute = double cute. Django-MySQL extends Django's built-in MySQL and MariaDB support their specific

Dec 2, 2022
Django-Audiofield is a simple app that allows Audio files upload, management and conversion to different audio format (mp3, wav & ogg), which also makes it easy to play audio files into your Django application.
Django-Audiofield is a simple app that allows Audio files upload, management and conversion to different audio format (mp3, wav & ogg), which also makes it easy to play audio files into your Django application.

Django-Audiofield Description: Django Audio Management Tools Maintainer: Areski Contributors: list of contributors Django-Audiofield is a simple app t

Nov 10, 2022