Skip to content

Commit 9e99357

Browse files
authored
Merge pull request #473 from winged/drop_mptt
refactor: drop django-mptt in favour of django-tree-queries
2 parents c7a95bf + 61a30e0 commit 9e99357

18 files changed

+634
-97
lines changed

emeis/core/management/commands/create_scope.py

-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
from django.core.management.base import BaseCommand
44

55
from emeis.core.models import ACL, Role, Scope, User
6-
from emeis.core.utils import rebuild_mptt_model
76

87

98
class Command(BaseCommand):
@@ -78,7 +77,6 @@ def handle(self, *args, **options):
7877
options["name"] = json.loads(options["name"])
7978

8079
new_scope = Scope.objects.create(name=options["name"], parent=parent)
81-
rebuild_mptt_model(Scope)
8280
out_info["scope_id"] = str(new_scope.pk)
8381

8482
if options["user"]:

emeis/core/migrations/0001_initial.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import django.utils.timezone
77
import localized_fields.fields.char_field
88
import localized_fields.fields.text_field
9-
import mptt.fields
109
from django.conf import settings
1110
from django.db import migrations, models
1211

@@ -224,7 +223,7 @@ class Migration(migrations.Migration):
224223
),
225224
(
226225
"parent",
227-
mptt.fields.TreeForeignKey(
226+
models.ForeignKey(
228227
blank=True,
229228
null=True,
230229
on_delete=django.db.models.deletion.SET_NULL,

emeis/core/migrations/0009_alter_scope_parent.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
# Generated by Django 3.2.12 on 2022-04-21 07:24
22

3-
from django.db import migrations
43
import django.db.models.deletion
5-
import mptt.fields
4+
from django.db import migrations, models
65

76

87
class Migration(migrations.Migration):
@@ -14,7 +13,7 @@ class Migration(migrations.Migration):
1413
migrations.AlterField(
1514
model_name="scope",
1615
name="parent",
17-
field=mptt.fields.TreeForeignKey(
16+
field=models.ForeignKey(
1817
blank=True,
1918
null=True,
2019
on_delete=django.db.models.deletion.CASCADE,

emeis/core/migrations/0010_scope_full_name.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Generated by Django 3.2.15 on 2022-09-23 09:13
22

3-
from django.db import migrations
43
import localized_fields.fields.char_field
4+
from django.db import migrations
55

66
from emeis.core import models
77

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Generated by Django 3.2.25 on 2024-06-03 14:17
2+
3+
import django.db.models.deletion
4+
from django.db import migrations, models
5+
6+
7+
class Migration(migrations.Migration):
8+
dependencies = [
9+
("emeis_core", "0010_scope_full_name"),
10+
]
11+
12+
operations = [
13+
migrations.AlterModelOptions(
14+
name="scope",
15+
options={"ordering": ["full_name"]},
16+
),
17+
migrations.RemoveField(
18+
model_name="scope",
19+
name="level",
20+
),
21+
migrations.RemoveField(
22+
model_name="scope",
23+
name="lft",
24+
),
25+
migrations.RemoveField(
26+
model_name="scope",
27+
name="rght",
28+
),
29+
migrations.RemoveField(
30+
model_name="scope",
31+
name="tree_id",
32+
),
33+
migrations.AlterField(
34+
model_name="scope",
35+
name="parent",
36+
field=models.ForeignKey(
37+
blank=True,
38+
null=True,
39+
on_delete=django.db.models.deletion.CASCADE,
40+
related_name="children",
41+
to="emeis_core.scope",
42+
verbose_name="parent",
43+
),
44+
),
45+
]

emeis/core/models.py

+11-10
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from django.utils import timezone, translation
1111
from django.utils.translation import gettext_lazy as _
1212
from localized_fields.fields import LocalizedCharField, LocalizedTextField
13-
from mptt.models import MPTTModel, TreeForeignKey
13+
from tree_queries.models import TreeNode, TreeQuerySet
1414

1515

1616
def make_uuid():
@@ -158,7 +158,7 @@ def is_authenticated(self):
158158
return True
159159

160160

161-
class Scope(MPTTModel, UUIDModel):
161+
class Scope(TreeNode, UUIDModel):
162162
name = LocalizedCharField(_("scope name"), blank=False, null=False, required=False)
163163

164164
full_name = LocalizedCharField(
@@ -168,20 +168,21 @@ class Scope(MPTTModel, UUIDModel):
168168
description = LocalizedTextField(
169169
_("scope description"), null=True, blank=True, required=False
170170
)
171-
parent = TreeForeignKey(
172-
"self",
173-
on_delete=models.CASCADE,
174-
null=True,
175-
blank=True,
176-
related_name="children",
177-
)
178171
is_active = models.BooleanField(default=True)
179172

173+
objects = TreeQuerySet.as_manager(with_tree_fields=True)
174+
175+
def save(self, *args, **kwargs):
176+
# django-tree-queries does validation in TreeNode.clean(), which is not
177+
# called by DRF (only by django forms), so we have to do this here
178+
self.clean()
179+
return super().save(*args, **kwargs)
180+
180181
def __str__(self):
181182
return f"{type(self).__name__} ({self.full_name}, pk={self.pk})"
182183

183184
class Meta:
184-
ordering = ["full_name"]
185+
ordering = ["name"]
185186

186187

187188
@receiver(pre_save, sender=Scope)

emeis/core/serializers.py

+24-1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ class Meta:
3333

3434

3535
class MyACLSerializer(BaseSerializer):
36+
scope = serializers.ResourceRelatedField(
37+
queryset=Scope.objects.all(), required=False, many=False
38+
)
39+
3640
included_serializers = {
3741
"scope": "emeis.core.serializers.ScopeSerializer",
3842
"role": "emeis.core.serializers.RoleSerializer",
@@ -73,6 +77,22 @@ class Meta:
7377

7478

7579
class ScopeSerializer(BaseSerializer):
80+
level = serializers.SerializerMethodField()
81+
82+
def get_level(self, obj):
83+
depth = getattr(obj, "tree_depth", None)
84+
if depth is not None:
85+
return depth
86+
87+
# Note: This should only happen on CREATE, never in GET (Either list,
88+
# detail, or include!) In CREATE, it's a new object that doesn't come
89+
# from a QS
90+
91+
# Sometimes, the model object may come out of a non-django-tree-queries
92+
# QS, and thus would not have the `tree_*` attributes amended. Then we
93+
# need to go the "slow path"
94+
return obj.ancestors().count()
95+
7696
class Meta:
7797
model = Scope
7898
fields = BaseSerializer.Meta.fields + (
@@ -83,7 +103,7 @@ class Meta:
83103
"full_name",
84104
"is_active",
85105
)
86-
read_only_fields = ["full_name"]
106+
read_only_fields = ["full_name", "level"]
87107

88108

89109
class PermissionSerializer(BaseSerializer):
@@ -111,6 +131,9 @@ class Meta:
111131

112132

113133
class ACLSerializer(BaseSerializer):
134+
scope = serializers.ResourceRelatedField(
135+
queryset=Scope.objects.all(), required=False, many=False
136+
)
114137
included_serializers = {
115138
"user": UserSerializer,
116139
"scope": ScopeSerializer,

0 commit comments

Comments
 (0)