diff --git a/src/c3nav/editor/models/changeset.py b/src/c3nav/editor/models/changeset.py index ef70528e..3e2bebd2 100644 --- a/src/c3nav/editor/models/changeset.py +++ b/src/c3nav/editor/models/changeset.py @@ -54,7 +54,6 @@ class ChangeSet(models.Model): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.changed_objects = None self.created_objects = {} self.updated_existing = {} @@ -234,7 +233,7 @@ class ChangeSet(models.Model): return self.can_edit(request) and self.state == 'unproposed' def can_propose(self, request): - return self.can_edit(request) and not self.proposed and self.changed_objects_count + return self.can_edit(request) and not self.proposed and self.changes.operations def can_unpropose(self, request): return self.author_id == request.user.pk and self.state in ('proposed', 'reproposed') @@ -417,19 +416,7 @@ class ChangeSet(models.Model): """ Get the number of changed objects. """ - self.fill_changes_cache() - count = 0 - changed_locationslug_pks = set() - for model, objects in self.changed_objects.items(): - if issubclass(model, LocationSlug): - if model == LocationRedirect: - continue - changed_locationslug_pks.update(objects.keys()) - count += sum(1 for obj in objects.values() if not obj.is_created or not obj.deleted) - - count += len(set(obj.obj.target_id - for obj in self.changed_objects.get(LocationRedirect, {}).values()) - changed_locationslug_pks) - return count + return len(self.changes.changed_objects) def get_changed_objects_by_model(self, model): if isinstance(model, str): @@ -446,7 +433,6 @@ class ChangeSet(models.Model): if self.direct_editing: return _('Direct editing active') return _('No objects changed') - return 'something was changed' # todo: make this nice again return (ngettext_lazy('%(num)d object changed', '%(num)d objects changed', 'num') % {'num': self.changed_objects_count}) diff --git a/src/c3nav/editor/operations.py b/src/c3nav/editor/operations.py index 9773353f..f25edf93 100644 --- a/src/c3nav/editor/operations.py +++ b/src/c3nav/editor/operations.py @@ -111,6 +111,21 @@ DatabaseOperation = Annotated[ ] +class ChangedManyToMany(BaseSchema): + cleared: bool = False + added: list[str] = [] + removed: list[str] = [] + + +class ChangedObject(BaseSchema): + obj: ObjectReference + repr: str + created: bool = False + deleted: bool = False + fields: FieldValuesDict = {} + m2m_changes: dict[str, ChangedManyToMany] = {} + + class CollectedChanges(BaseSchema): prev_reprs: dict[str, dict[int, str]] = {} prev_values: dict[str, dict[int, FieldValuesDict]] = {} @@ -128,6 +143,38 @@ class CollectedChanges(BaseSchema): return CollectedChangesPrefetch(changes=self, instances=instances) + @property + def changed_objects(self) -> list[ChangedObject]: + objects = {} + for operation in self.operations: + changed_object = objects.get(operation.obj, None) + if changed_object is None: + changed_object = ChangedObject(obj=operation.obj, + repr=self.prev_reprs[operation.obj.model][operation.obj.id]) + objects[operation.obj] = changed_object + if isinstance(operation, CreateObjectOperation): + changed_object.created = True + changed_object.fields.update(operation.fields) + elif isinstance(operation, UpdateObjectOperation): + changed_object.fields.update(operation.fields) + elif isinstance(operation, DeleteObjectOperation): + changed_object.deleted = False + else: + changed_m2m = changed_object.m2m_changes.get(operation.field, None) + if changed_m2m is None: + changed_m2m = ChangedManyToMany() + changed_object.m2m_changes[operation.field] = changed_m2m + if isinstance(operation, ClearManyToManyOperation): + changed_m2m.cleared = True + changed_m2m.added = [] + changed_m2m.removed = [] + else: + changed_m2m.added = sorted((set(changed_m2m.added) | operation.add_values) + - operation.remove_values) + changed_m2m.removed = sorted((set(changed_m2m.removed) - operation.add_values) + | operation.remove_values) + return list(objects.values()) + @dataclass class CollectedChangesPrefetch: