From 44671a12dc9cd233906ccf1550c3d8558f169658 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20Kl=C3=BCnder?= Date: Thu, 5 Dec 2024 23:38:12 +0100 Subject: [PATCH] do create_multiple correctly --- src/c3nav/editor/changes.py | 15 +++++-- src/c3nav/editor/operations.py | 39 +++++++------------ .../editor/templates/editor/changeset.html | 3 ++ src/c3nav/editor/views/changes.py | 1 + src/c3nav/mapdata/models/geometry/base.py | 7 ++++ src/c3nav/mapdata/models/geometry/level.py | 5 ++- src/c3nav/mapdata/models/geometry/space.py | 29 +++++++++----- src/c3nav/mapdata/models/locations.py | 22 +++++++++-- src/c3nav/mapdata/models/theme.py | 10 ++++- src/c3nav/mapdata/utils/cache/changes.py | 8 ---- 10 files changed, 86 insertions(+), 53 deletions(-) diff --git a/src/c3nav/editor/changes.py b/src/c3nav/editor/changes.py index f3335c02..bebf449b 100644 --- a/src/c3nav/editor/changes.py +++ b/src/c3nav/editor/changes.py @@ -655,10 +655,19 @@ class ChangedObjectCollection(BaseSchema): # construct new situation new_situation = situation.model_copy(deep=True) - if isinstance(new_operation, CreateObjectOperation) and new_situation.operations: + model = apps.get_model('mapdata', new_operation.obj.model) + for parent in model._meta.get_parent_list(): + if parent._meta.concrete_model is not model._meta.concrete_model: + is_multi_inheritance = True + break + else: + is_multi_inheritance = False + + if (isinstance(new_operation, CreateObjectOperation) + and new_situation.operations and not is_multi_inheritance): last_operation = new_situation.operations[-1] - if (isinstance(last_operation, CreateObjectOperation) and - last_operation.obj.model == new_operation.obj.model): + if (isinstance(last_operation, CreateObjectOperation) + and last_operation.obj.model == new_operation.obj.model): new_situation.operations[-1] = CreateMultipleObjectsOperation( objects=[last_operation, new_operation], ) diff --git a/src/c3nav/editor/operations.py b/src/c3nav/editor/operations.py index e9dadc67..98dbef0b 100644 --- a/src/c3nav/editor/operations.py +++ b/src/c3nav/editor/operations.py @@ -87,33 +87,19 @@ class CreateObjectOperation(BaseOperation): fields: FieldValuesDict def get_data(self): - model = apps.get_model('mapdata', self.obj.model) - data = [] - if issubclass(model, LocationSlug): - data.append({ - "model": f"mapdata.locationslug", - "pk": self.obj.id, - "fields": { - "slug": self.fields.get("slug", None) - }, - }) - values = {key: val for key, val in self.fields.items() if key != "slug"} - else: - values = self.fields - data.append({ + return [{ "model": f"mapdata.{self.obj.model}", "pk": self.obj.id, - "fields": values, - }) - return data + "fields": self.fields, + }] def apply_create(self) -> dict[ObjectReference, Model]: data = self.get_data() - instances = list(serializers.deserialize("json", json.dumps(data))) - for instance in instances: + instances = [item.object for item in serializers.deserialize("json", json.dumps(data))] + for instance in instances[-1:]: # .object. to make sure our own .save() function is called! - instance.object.save() - return {self.obj: instances[-1].object} + instance.save() + return {self.obj: instances[-1]} class CreateMultipleObjectsOperation(BaseSchema): @@ -126,11 +112,12 @@ class CreateMultipleObjectsOperation(BaseSchema): for obj in self.objects: data.extend(obj.get_data()) indexes[obj.obj] = len(data)-1 - instances = list(serializers.deserialize("json", json.dumps(data))) - # todo: actually do a create_multiple!, let's not forget about register_changed_geometries etc - for instance in instances: - # .object. to make sure our own .save() function is called! - instance.object.save() + instances = [item.object for item in serializers.deserialize("json", json.dumps(data))] + if hasattr(instances[-1], "pre_save_changed_geometries"): + for instance in instances: + instance.pre_save_changed_geometries() + model = apps.get_model('mapdata', self.objects[0].obj.model) + model.objects.bulk_create(instances) return {ref: instances[i] for ref, i in indexes.items()} diff --git a/src/c3nav/editor/templates/editor/changeset.html b/src/c3nav/editor/templates/editor/changeset.html index 2dcf18da..fe28208c 100644 --- a/src/c3nav/editor/templates/editor/changeset.html +++ b/src/c3nav/editor/templates/editor/changeset.html @@ -81,6 +81,9 @@ {% if changeset.description %}

{{ changeset.description }}

{% endif %} +{% if changed_objects %} +

{% blocktranslate count counter=operations|length %}({{ counter }} operation in total){% plural %}({{ counter }} operations in total){% endblocktranslate %}

+{% endif %} {% for obj in changed_objects %} diff --git a/src/c3nav/editor/views/changes.py b/src/c3nav/editor/views/changes.py index cb5d936a..3c54789f 100644 --- a/src/c3nav/editor/views/changes.py +++ b/src/c3nav/editor/views/changes.py @@ -233,6 +233,7 @@ def changeset_detail(request, pk): 'can_unreject': changeset.can_unreject(request), 'can_apply': changeset.can_apply(request), 'active': active, + 'operations': changeset.as_operations, } cache_key = '%s:%s:%s:view_data' % (changeset.cache_key_by_changes, diff --git a/src/c3nav/mapdata/models/geometry/base.py b/src/c3nav/mapdata/models/geometry/base.py index dec00205..8426de9c 100644 --- a/src/c3nav/mapdata/models/geometry/base.py +++ b/src/c3nav/mapdata/models/geometry/base.py @@ -133,3 +133,10 @@ class GeometryMixin(SerializableMixin): if self._meta.get_field('geometry').geomtype in ('polygon', 'multipolygon'): difference = unary_union(assert_multipolygon(difference)) return difference + + def pre_delete_changed_geometries(self): + self.register_delete() + + def delete(self, *args, **kwargs): + self.pre_delete_changed_geometries() + super().delete(*args, **kwargs) diff --git a/src/c3nav/mapdata/models/geometry/level.py b/src/c3nav/mapdata/models/geometry/level.py index abc34623..02229b6e 100644 --- a/src/c3nav/mapdata/models/geometry/level.py +++ b/src/c3nav/mapdata/models/geometry/level.py @@ -92,8 +92,11 @@ class LevelGeometryMixin(GeometryMixin): Level.q_for_request(request, prefix=prefix+'level__', allow_none=allow_none) ) - def save(self, *args, **kwargs): + def pre_save_changed_geometries(self): self.register_change() + + def save(self, *args, **kwargs): + self.pre_save_changed_geometries() super().save(*args, **kwargs) diff --git a/src/c3nav/mapdata/models/geometry/space.py b/src/c3nav/mapdata/models/geometry/space.py index 02eb64db..3e44aaa5 100644 --- a/src/c3nav/mapdata/models/geometry/space.py +++ b/src/c3nav/mapdata/models/geometry/space.py @@ -98,8 +98,11 @@ class SpaceGeometryMixin(GeometryMixin): space = self.space changed_geometries.register(space.level_id, space.geometry.intersection(unwrap_geom(self.geometry))) - def save(self, *args, **kwargs): + def pre_save_changed_geometries(self): self.register_change() + + def save(self, *args, **kwargs): + self.pre_save_changed_geometries() super().save(*args, **kwargs) @@ -182,21 +185,27 @@ class ObstacleGroup(TitledMixin, models.Model): super().__init__(*args, **kwargs) self._orig = {"color": self.color} - def save(self, *args, **kwargs): - if not self._state.adding and any(getattr(self, attname) != value for attname, value in self._orig.items()): - self.register_changed_geometries() - super().save(*args, **kwargs) - - def delete(self, *args, **kwargs): - self.register_changed_geometries() - super().delete(*args, **kwargs) - def register_changed_geometries(self): for obj in self.obstacles.select_related('space'): obj.register_change(force=True) for obj in self.lineobstacles.select_related('space'): obj.register_change(force=True) + def pre_save_changed_geometries(self): + if not self._state.adding and any(getattr(self, attname) != value for attname, value in self._orig.items()): + self.register_changed_geometries() + + def save(self, *args, **kwargs): + self.pre_save_changed_geometries() + super().save(*args, **kwargs) + + def pre_delete_changed_geometries(self): + self.register_changed_geometries() + + def delete(self, *args, **kwargs): + self.pre_delete_changed_geometries() + super().delete(*args, **kwargs) + class Obstacle(SpaceGeometryMixin, models.Model): """ diff --git a/src/c3nav/mapdata/models/locations.py b/src/c3nav/mapdata/models/locations.py index ee9426ea..4e506ad6 100644 --- a/src/c3nav/mapdata/models/locations.py +++ b/src/c3nav/mapdata/models/locations.py @@ -289,11 +289,21 @@ class LocationGroupCategory(SerializableMixin, models.Model): for group in query: group.register_changed_geometries(do_query=False) - def save(self, *args, **kwargs): + def pre_save_changed_geometries(self): if not self._state.adding and any(getattr(self, attname) != value for attname, value in self._orig.items()): self.register_changed_geometries() + + def save(self, *args, **kwargs): + self.pre_save_changed_geometries() super().save(*args, **kwargs) + def pre_delete_changed_geometries(self): + self.register_changed_geometries() + + def delete(self, *args, **kwargs): + self.pre_delete_changed_geometries() + super().delete(*args, **kwargs) + class LocationGroupManager(models.Manager): def get_queryset(self): @@ -395,13 +405,19 @@ class LocationGroup(Location, models.Model): def order(self): return (1, self.category.priority, self.priority) - def save(self, *args, **kwargs): + def pre_save_changed_geometries(self): if not self._state.adding and any(getattr(self, attname) != value for attname, value in self._orig.items()): self.register_changed_geometries() + + def save(self, *args, **kwargs): + self.pre_save_changed_geometries() super().save(*args, **kwargs) - def delete(self, *args, **kwargs): + def pre_delete_changed_geometries(self): self.register_changed_geometries() + + def delete(self, *args, **kwargs): + self.pre_delete_changed_geometries() super().delete(*args, **kwargs) diff --git a/src/c3nav/mapdata/models/theme.py b/src/c3nav/mapdata/models/theme.py index 8cdf1182..88718b59 100644 --- a/src/c3nav/mapdata/models/theme.py +++ b/src/c3nav/mapdata/models/theme.py @@ -95,12 +95,18 @@ class ThemeLocationGroupBackgroundColor(models.Model): fill_color = models.CharField(max_length=32, null=True, blank=True) border_color = models.CharField(max_length=32, null=True, blank=True) - def save(self, *args, **kwargs): + def pre_save_changed_geometries(self): self.location_group.register_changed_geometries() + + def save(self, *args, **kwargs): + self.pre_save_changed_geometries() super().save(*args, **kwargs) - def delete(self, *args, **kwargs): + def pre_delete_changed_geometries(self): self.location_group.register_changed_geometries() + + def delete(self, *args, **kwargs): + self.pre_delete_changed_geometries() super().delete(*args, **kwargs) diff --git a/src/c3nav/mapdata/utils/cache/changes.py b/src/c3nav/mapdata/utils/cache/changes.py index ebcafdee..eb05acd2 100644 --- a/src/c3nav/mapdata/utils/cache/changes.py +++ b/src/c3nav/mapdata/utils/cache/changes.py @@ -75,10 +75,6 @@ class GeometryChangeTracker: changed_geometries = GeometryChangeTracker() # todo: no longer needed if we use the overlay stuff -def geometry_deleted(sender, instance, **kwargs): - instance.register_delete() - - def locationgroup_changed(sender, instance, action, reverse, model, pk_set, using, **kwargs): if action not in ('post_add', 'post_remove', 'post_clear'): return @@ -97,10 +93,6 @@ def locationgroup_changed(sender, instance, action, reverse, model, pk_set, usin def register_signals(): - from c3nav.mapdata.models.geometry.base import GeometryMixin - for model in get_submodels(GeometryMixin): - post_delete.connect(geometry_deleted, sender=model) - from c3nav.mapdata.models.locations import SpecificLocation for model in get_submodels(SpecificLocation): m2m_changed.connect(locationgroup_changed, sender=model.groups.through)