Skip to content

Commit

Permalink
Fix regression with tile saves; revisit after grouping node field merges
Browse files Browse the repository at this point in the history
  • Loading branch information
jacobtylerwalls committed Dec 3, 2024
1 parent baad4eb commit b30f781
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 13 deletions.
31 changes: 22 additions & 9 deletions arches/app/models/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import traceback
from collections import defaultdict
from itertools import zip_longest
from operator import attrgetter, itemgetter
from operator import attrgetter

from arches.app.const import ExtensionType
from arches.app.utils.module_importer import get_class_from_modulename
Expand All @@ -18,6 +18,7 @@
from arches.app.models.utils import (
add_to_update_fields,
field_names,
find_root_node_from_fetched_root_nodes,
pop_arches_model_kwargs,
)
from arches.app.utils.betterJSONSerializer import JSONSerializer
Expand Down Expand Up @@ -2068,8 +2069,8 @@ def _save_from_pythonic_model_values(self, *, user=None, index=False, **kwargs):
)

super().save(**kwargs)

for node in self._root_node.nodegroup.node_set.all():
# TODO: address performance.
for node in self.nodegroup.node_set.all():
datatype = datatype_factory.get_instance(node.datatype)
datatype.post_tile_save(self, str(node.pk))
proxy._Tile__postSave()
Expand Down Expand Up @@ -2126,7 +2127,11 @@ def _update_tile_from_pythonic_model_values(self):
errors_by_alias = defaultdict(list)
# TODO: move this somewhere else.
ResourceInstance._validate_and_patch_from_tile_values(
self, root_node=self._root_node, errors_by_node_alias=errors_by_alias
self,
root_node=find_root_node_from_fetched_root_nodes(
self._fetched_root_nodes, self.nodegroup_id
),
errors_by_node_alias=errors_by_alias,
)
if not any(self.data.values()):
raise ValidationError(_("Tile is blank."))
Expand All @@ -2151,7 +2156,8 @@ def _tile_update_is_noop(self, original_data):
# that's probably good. Determine DX here.

datatype_factory = DataTypeFactory()
for node in self._root_node.nodegroup.node_set.all():
# TODO: address performance
for node in self.nodegroup.node_set.all():
if node.datatype == "semantic":
continue
old = original_data[str(node.nodeid)]
Expand Down Expand Up @@ -2219,20 +2225,27 @@ def serialize(
def refresh_from_db(self, using=None, fields=None, from_queryset=None):
if (
from_queryset is None
and (root_nodes := getattr(self, "_fetched_root_nodes", set()))
and (fetched_nodes := getattr(self, "_fetched_nodes", set()))
and self.resourceinstance.graph.slug
):
aliases = [n.alias for n in root_nodes]
NOT_PROVIDED = object()
aliases = [n.alias for n in fetched_nodes]
from_queryset = self.__class__.as_nodegroup(
root_node_alias=self._root_node.alias,
root_node_alias=find_root_node_from_fetched_root_nodes(
self._fetched_root_nodes, self.nodegroup_id
).alias,
graph_slug=self.resourceinstance.graph.slug,
only=aliases,
)
super().refresh_from_db(using, fields, from_queryset)
# Copy over annotations.
refreshed_tile = from_queryset[0]
for field in aliases:
setattr(self, field, getattr(refreshed_tile, field))
# TODO: why is this if needed?
if (
annotation := getattr(refreshed_tile, field, NOT_PROVIDED)
) is not NOT_PROVIDED:
setattr(self, field, annotation)
else:
super().refresh_from_db(using, fields, from_queryset)

Expand Down
11 changes: 7 additions & 4 deletions arches/app/models/querysets.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,19 +80,22 @@ def _prefetch_related_objects(self):
Discard annotations that do not pertain to this nodegroup.
"""
from arches.app.datatypes.datatypes import DataTypeFactory
from arches.app.models.models import TileModel
from arches.app.models.models import Node, TileModel

super()._prefetch_related_objects()

datatype_factory = DataTypeFactory()
NOT_PROVIDED = object()
for tile in self._result_cache:
tile._fetched_nodes = self._fetched_nodes
tile._fetched_root_nodes = set()
for node in self._fetched_nodes:
if node.nodegroup_id == tile.nodegroup_id:
if node.pk == tile.nodegroup_id:
tile._root_node = node
tile._fetched_root_nodes.add(node)
# Replace with new v8 root/grouping node lookup.
if Node(pk=tile.nodegroup_id) not in tile._fetched_root_nodes:
tile._fetched_root_nodes.add(
Node.objects.get(pk=tile.nodegroup_id)
)
tile_val = getattr(tile, node.alias, NOT_PROVIDED)
if tile_val is not NOT_PROVIDED:
datatype_instance = datatype_factory.get_instance(node.datatype)
Expand Down
6 changes: 6 additions & 0 deletions arches/app/models/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@ def find_root_node(prefetched_siblings, nodegroup_id):
return sibling_node


def find_root_node_from_fetched_root_nodes(fetched_root_nodes, nodegroup_id):
for candidate in fetched_root_nodes:
if candidate.pk == nodegroup_id:
return candidate


def get_values_query(*, nodegroup, base_lookup, lhs=None, outer_ref) -> BaseExpression:
"""Return a tile values query expression for use in a
ResourceInstanceQuerySet or TileQuerySet.
Expand Down

0 comments on commit b30f781

Please sign in to comment.