Skip to content

Commit f567d53

Browse files
Collect nodes using controlled lists #10554
1 parent 347d7af commit f567d53

File tree

4 files changed

+80
-7
lines changed

4 files changed

+80
-7
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Generated by Django 4.2.10 on 2024-02-23 08:33
2+
3+
from django.db import migrations, models
4+
import django.db.models.fields.json
5+
import django.db.models.functions.comparison
6+
7+
8+
class Migration(migrations.Migration):
9+
dependencies = [
10+
("models", "10552_add_reference_datatype"),
11+
]
12+
13+
operations = [
14+
migrations.AddIndex(
15+
model_name="node",
16+
index=models.Index(
17+
django.db.models.functions.comparison.Cast(
18+
django.db.models.fields.json.KeyTextTransform("controlledList", "config"),
19+
output_field=models.UUIDField(null=True)
20+
),
21+
name="lists_reffed_by_node_idx",
22+
),
23+
),
24+
]

arches/app/models/models.py

+8
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
from arches.app.utils import import_class_from_string
2525
from django.contrib.gis.db import models
2626
from django.db.models import Deferrable, JSONField
27+
from django.db.models.fields.json import KT
28+
from django.db.models.functions import Cast
2729
from django.core.cache import caches
2830
from django.core.exceptions import ValidationError
2931
from django.core.mail import EmailMultiAlternatives
@@ -725,6 +727,12 @@ class Meta:
725727
models.UniqueConstraint(fields=["name", "nodegroup"], name="unique_nodename_nodegroup"),
726728
models.UniqueConstraint(fields=["alias", "graph"], name="unique_alias_graph"),
727729
]
730+
indexes = [
731+
models.Index(
732+
Cast(KT("config__controlledList"), output_field=models.UUIDField(null=True)),
733+
name="lists_reffed_by_node_idx",
734+
)
735+
]
728736

729737

730738
@receiver(post_save, sender=Node)

arches/app/views/controlled_lists.py

+29-7
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
from collections import defaultdict
22
from datetime import datetime
33

4+
from django.contrib.postgres.expressions import ArraySubquery
45
from django.core.exceptions import ValidationError
56
from django.db import IntegrityError, transaction
6-
from django.db.models import Max
7+
from django.db.models import Max, OuterRef, UUIDField
8+
from django.db.models.fields.json import KT
9+
from django.db.models.functions import Cast
710
from django.views.generic import View
811
from django.utils.decorators import method_decorator
912
from django.utils.translation import get_language, gettext as _
@@ -13,19 +16,20 @@
1316
ControlledListItem,
1417
ControlledListItemLabel,
1518
Language,
19+
Node,
1620
)
1721
from arches.app.utils.betterJSONSerializer import JSONDeserializer
1822
from arches.app.utils.decorators import group_required
1923
from arches.app.utils.response import JSONErrorResponse, JSONResponse
2024
from arches.app.utils.string_utils import str_to_bool
2125

2226

23-
def serialize(obj, depth_map=None, flat=False):
27+
def serialize(obj, depth_map=None, flat=False, nodes=False):
2428
if depth_map is None:
2529
depth_map = defaultdict(int)
2630
match obj:
2731
case ControlledList():
28-
return {
32+
data = {
2933
"id": str(obj.id),
3034
"name": obj.name,
3135
"dynamic": obj.dynamic,
@@ -38,6 +42,9 @@ def serialize(obj, depth_map=None, flat=False):
3842
key=lambda d: d["sortorder"],
3943
),
4044
}
45+
if nodes:
46+
data["nodes"] = [str(uid) for uid in obj.node_ids]
47+
return data
4148
case ControlledListItem():
4249
if obj.parent_id:
4350
depth_map[obj.id] = depth_map[obj.parent_id] + 1
@@ -134,12 +141,27 @@ def handle_item(itemDict):
134141
class ControlledListsView(View):
135142
def get(self, request):
136143
"""Returns either a flat representation (?flat=true) or a tree (default)."""
144+
lists = (
145+
ControlledList.objects.all()
146+
.annotate(
147+
node_ids=ArraySubquery(
148+
Node.objects.annotate(
149+
as_uuid=Cast(
150+
KT("config__controlledList"), output_field=UUIDField()
151+
)
152+
)
153+
.filter(as_uuid=OuterRef("id"))
154+
.values("pk")
155+
)
156+
)
157+
# check node perms?
158+
.order_by("name")
159+
.prefetch_related(*prefetch_terms(request))
160+
)
137161
data = {
138162
"controlled_lists": [
139-
serialize(obj, flat=str_to_bool(request.GET.get("flat", "false")))
140-
for obj in ControlledList.objects.all()
141-
.order_by("name")
142-
.prefetch_related(*prefetch_terms(request))
163+
serialize(obj, nodes=True, flat=str_to_bool(request.GET.get("flat", "false")))
164+
for obj in lists
143165
],
144166
}
145167

tests/views/controlled_lists_tests.py

+19
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
ControlledListItemLabel,
1212
DValueType,
1313
Language,
14+
Node,
15+
NodeGroup,
1416
)
1517
from arches.app.views.controlled_lists import serialize
1618
from tests.base_test import ArchesTestCase
@@ -102,6 +104,20 @@ def setUpTestData(cls):
102104
]
103105
)
104106

107+
random_node_group = NodeGroup.objects.last()
108+
cls.node_using_list1 = Node(
109+
pk="a3c5b7d3-ef2c-4f8b-afd5-f8d4636b8834",
110+
name="Uses list1",
111+
datatype="reference",
112+
nodegroup=random_node_group,
113+
istopnode=False,
114+
config={
115+
"multiValue": False,
116+
"controlledList": str(cls.list1.pk),
117+
},
118+
)
119+
cls.node_using_list1.save()
120+
105121
def test_get_controlled_lists(self):
106122
self.client.force_login(self.anonymous)
107123
response = self.client.get(reverse("controlled_lists"))
@@ -129,6 +145,9 @@ def test_get_controlled_lists(self):
129145
# pprint(result)
130146

131147
first_list, second_list = result["controlled_lists"]
148+
149+
self.assertEqual(first_list["nodes"], [str(self.node_using_list1.pk)])
150+
132151
for item in first_list["items"]:
133152
self.assertEqual(item["children"], [])
134153

0 commit comments

Comments
 (0)