From f219f1639f8c8653c1ef39f5243d5a5f5871c37c Mon Sep 17 00:00:00 2001 From: Kwong Tung Nan Date: Thu, 2 Jan 2025 10:23:02 +0800 Subject: [PATCH] Attempt to use django-tree-node to replace django-mptt --- poetry.lock | 16 ++++- pyproject.toml | 1 + tests/relay/mptt/models.py | 131 ++----------------------------------- 3 files changed, 20 insertions(+), 128 deletions(-) diff --git a/poetry.lock b/poetry.lock index 41d6406d..a74d83f6 100644 --- a/poetry.lock +++ b/poetry.lock @@ -236,6 +236,20 @@ files = [ [package.dependencies] Django = ">=2.1" +[[package]] +name = "django-tree-queries" +version = "0.19.0" +description = "Tree queries with explicit opt-in, without configurability" +optional = false +python-versions = ">=3.8" +files = [ + {file = "django_tree_queries-0.19.0-py3-none-any.whl", hash = "sha256:05b9e3158e31612528f136b4704a8d807e14edc0b4a607a45377e6132517ba2c"}, + {file = "django_tree_queries-0.19.0.tar.gz", hash = "sha256:d1325e75f96e90b86c4316a3d63498101ec05703f4e629786b561e8aaab0e4a7"}, +] + +[package.extras] +tests = ["coverage"] + [[package]] name = "django-types" version = "0.20.0" @@ -911,4 +925,4 @@ enum = ["django-choices-field"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<4.0" -content-hash = "56e83e063c18200cdc6cceb33afa4686060eea914fca7182f2ca5ff73755f4ca" +content-hash = "a4566912e06f3adf657c3a4f3fab14b4cfe546f7e445c985231b35130e82289b" diff --git a/pyproject.toml b/pyproject.toml index 8f3ba55c..8ae8db6f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,6 +37,7 @@ asgiref = ">=3.8" django-choices-field = { version = ">=2.2.2", optional = true } django-debug-toolbar = { version = ">=3.4", optional = true } strawberry-graphql = ">=0.236.0" +django-tree-queries = "^0.19.0" [tool.poetry.group.dev.dependencies] channels = { version = ">=3.0.5" } diff --git a/tests/relay/mptt/models.py b/tests/relay/mptt/models.py index f7374c29..e712fdb3 100644 --- a/tests/relay/mptt/models.py +++ b/tests/relay/mptt/models.py @@ -1,134 +1,11 @@ -import django from django.db import models -from mptt.fields import TreeForeignKey -from mptt.models import MPTTModel -from mptt.models import MPTTModelBase as _MPTTModelBase +from tree_queries.fields import TreeNodeForeignKey +from tree_queries.models import TreeNode -if django.VERSION >= (5, 1): - from django.utils.translation import gettext as _ - from mptt.managers import TreeManager - from mptt.models import MPTTOptions - from mptt.utils import _get_tree_model # noqa: PLC2701 - class MPTTModelBase(_MPTTModelBase): - @classmethod - def register(meta, cls, **kwargs): # noqa: N804 - # 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 TypeError(_("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 # noqa: B018 - 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) - - is_cls_tree_model = _get_tree_model(cls) is cls - - if is_cls_tree_model: - # 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 - ]) - - mptt_meta = cls._mptt_meta - indexed_attrs = (mptt_meta.tree_id_attr,) - field_names = ( - mptt_meta.left_attr, - mptt_meta.right_attr, - mptt_meta.tree_id_attr, - mptt_meta.level_attr, - ) - - for field_name in field_names: - if field_name not in existing_field_names: - field = models.PositiveIntegerField( - db_index=field_name in indexed_attrs, editable=False - ) - field.contribute_to_class(cls, field_name) - - # Add an unique_together on tree_id_attr and left_attr, as these are very - # commonly queried (pretty much all reads). - unique_together = ( - cls._mptt_meta.tree_id_attr, - cls._mptt_meta.left_attr, - ) - if unique_together not in cls._meta.unique_together: - cls._meta.unique_together += (unique_together,) - - # Add a tree manager, if there isn't one already - if not abstract: - # make sure we have a tree manager somewhere - tree_manager = None - # Use the default manager defined on the class if any - if cls._default_manager and isinstance( - cls._default_manager, TreeManager - ): - tree_manager = cls._default_manager - else: - for cls_manager in cls._meta.managers: - if ( - isinstance(cls_manager, TreeManager) - and cls_manager.model is cls - ): - # prefer any locally defined manager (i.e. keep going if not local) - tree_manager = cls_manager - break - - if is_cls_tree_model: - idx_together = ( - cls._mptt_meta.tree_id_attr, - cls._mptt_meta.left_attr, - ) - - if idx_together not in cls._meta.unique_together: - cls._meta.unique_together += (idx_together,) - - 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 - cls._tree_manager = tree_manager - return cls - - -else: - MPTTModelBase = _MPTTModelBase - - -class MPTTAuthor(MPTTModel, metaclass=MPTTModelBase): +class MPTTAuthor(TreeNode): name = models.CharField(max_length=100) - parent = TreeForeignKey( + parent = TreeNodeForeignKey( to="self", on_delete=models.CASCADE, null=True,