fixes to make the last commit work (asside from applying changes)

This commit is contained in:
Laura Klünder 2024-09-26 17:08:41 +02:00
parent 174866c2fd
commit f2f0b9fdae
7 changed files with 32 additions and 18 deletions

View file

@ -4,7 +4,8 @@ from django.apps import apps
from c3nav.api.schema import BaseSchema from c3nav.api.schema import BaseSchema
from c3nav.editor.operations import DatabaseOperationCollection, CreateObjectOperation, UpdateObjectOperation, \ from c3nav.editor.operations import DatabaseOperationCollection, CreateObjectOperation, UpdateObjectOperation, \
DeleteObjectOperation, ClearManyToManyOperation, FieldValuesDict, ObjectReference, PreviousObjectCollection DeleteObjectOperation, ClearManyToManyOperation, FieldValuesDict, ObjectReference, PreviousObjectCollection, \
DatabaseOperation
from c3nav.mapdata.fields import I18nField from c3nav.mapdata.fields import I18nField
@ -33,14 +34,18 @@ class ChangedObjectCollection(BaseSchema):
objects: dict[str, dict[int, ChangedObject]] = {} objects: dict[str, dict[int, ChangedObject]] = {}
def __iter__(self): def __iter__(self):
yield from chain(*(objects.keys() for model, objects in self.objects.items())) yield from chain(*(objects.values() for model, objects in self.objects.items()))
def __len__(self):
return sum(len(v) for v in self.objects.values())
def add_operations(self, operations: DatabaseOperationCollection): def add_operations(self, operations: DatabaseOperationCollection):
""" """
Add the given operations, creating/updating changed objects to represent the resulting state. Add the given operations, creating/updating changed objects to represent the resulting state.
""" """
# todo: merge prev # todo: if something is being changed back, remove it from thingy?
for operation in operations.operations: self.prev.add_other(operations.prev)
for operation in operations:
changed_object = self.objects.setdefault(operation.obj.model, {}).get(operation.obj.id, None) changed_object = self.objects.setdefault(operation.obj.model, {}).get(operation.obj.id, None)
if changed_object is None: if changed_object is None:
changed_object = ChangedObject(obj=operation.obj, changed_object = ChangedObject(obj=operation.obj,

View file

@ -43,7 +43,8 @@ class ChangeSet(models.Model):
related_name='assigned_changesets', verbose_name=_('assigned to')) related_name='assigned_changesets', verbose_name=_('assigned to'))
map_update = models.OneToOneField(MapUpdate, null=True, related_name='changeset', map_update = models.OneToOneField(MapUpdate, null=True, related_name='changeset',
verbose_name=_('map update'), on_delete=models.PROTECT) verbose_name=_('map update'), on_delete=models.PROTECT)
changes: ChangedObjectCollection = SchemaField(schema=ChangedObjectCollection, default=ChangedObjectCollection) changes: ChangedObjectCollection = SchemaField(schema=ChangedObjectCollection,
default=lambda: ChangedObjectCollection)
class Meta: class Meta:
verbose_name = _('Change Set') verbose_name = _('Change Set')
@ -164,7 +165,7 @@ class ChangeSet(models.Model):
return self.can_edit(request) and self.state == 'unproposed' return self.can_edit(request) and self.state == 'unproposed'
def can_propose(self, request): def can_propose(self, request):
return self.can_edit(request) and not self.proposed and self.changes.operations return self.can_edit(request) and not self.proposed and self.changes
def can_unpropose(self, request): def can_unpropose(self, request):
return self.author_id == request.user.pk and self.state in ('proposed', 'reproposed') return self.author_id == request.user.pk and self.state in ('proposed', 'reproposed')
@ -347,8 +348,7 @@ class ChangeSet(models.Model):
""" """
Get the number of changed objects. Get the number of changed objects.
""" """
return len([changed_object for changed_object in self.changes.changed_objects return len(self.changes)
if changed_object.obj.model != "locationredirect"])
def get_changed_objects_by_model(self, model): def get_changed_objects_by_model(self, model):
if isinstance(model, str): if isinstance(model, str):

View file

@ -1,7 +1,7 @@
import datetime import datetime
import json import json
from dataclasses import dataclass from dataclasses import dataclass
from typing import Annotated, Literal, Union, TypeAlias, Any from typing import Annotated, Literal, Union, TypeAlias, Any, Self
from uuid import UUID, uuid4 from uuid import UUID, uuid4
from django.apps import apps from django.apps import apps
@ -70,6 +70,10 @@ class PreviousObjectCollection(BaseSchema):
titles=titles, titles=titles,
) )
def add_other(self, other: Self):
for key in set(self.objects.keys()) | set(other.objects.keys()):
self.objects[key] = {**other.objects.get(key, {}), **self.objects.get(key, {})}
class BaseOperation(BaseSchema): class BaseOperation(BaseSchema):
obj: ObjectReference obj: ObjectReference
@ -194,10 +198,16 @@ class DatabaseOperationCollection(BaseSchema):
Iterable as a list of DatabaseOperation instances. Iterable as a list of DatabaseOperation instances.
""" """
prev: PreviousObjectCollection = PreviousObjectCollection() prev: PreviousObjectCollection = PreviousObjectCollection()
operations: list[DatabaseOperation] = [] _operations: list[DatabaseOperation] = []
def __iter__(self): def __iter__(self):
yield from self.operations yield from self._operations
def __len__(self):
return len(self._operations)
def append(self, item: DatabaseOperation):
self._operations.append(item)
def prefetch(self) -> "PrefetchedDatabaseOperationCollection": def prefetch(self) -> "PrefetchedDatabaseOperationCollection":
return PrefetchedDatabaseOperationCollection(operations=self, instances=self.prev.get_instances()) return PrefetchedDatabaseOperationCollection(operations=self, instances=self.prev.get_instances())
@ -210,7 +220,7 @@ class PrefetchedDatabaseOperationCollection:
def apply(self): def apply(self):
# todo: what if unique constraint error occurs? # todo: what if unique constraint error occurs?
for operation in self.operations.operations: for operation in self.operations:
if isinstance(operation, CreateObjectOperation): if isinstance(operation, CreateObjectOperation):
self.instances[operation.obj] = operation.apply_create() self.instances[operation.obj] = operation.apply_create()
else: else:

View file

@ -34,8 +34,7 @@ class DatabaseOverlayManager:
""" """
This class handles the currently active database overlay and will apply and/or intercept changes. This class handles the currently active database overlay and will apply and/or intercept changes.
""" """
prev: PreviousObjectCollection = PreviousObjectCollection() operations: DatabaseOperationCollection = field(default_factory=DatabaseOperationCollection)
operations: list[DatabaseOperation] = field(default_factory=list)
pre_change_values: dict[ObjectReference, FieldValuesDict] = field(default_factory=dict, init=False, repr=False) pre_change_values: dict[ObjectReference, FieldValuesDict] = field(default_factory=dict, init=False, repr=False)
@classmethod @classmethod
@ -54,7 +53,7 @@ class DatabaseOverlayManager:
operations = DatabaseOperationCollection() operations = DatabaseOperationCollection()
try: try:
with transaction.atomic(): with transaction.atomic():
manager = DatabaseOverlayManager(prev=copy.deepcopy(operations.prev)) manager = DatabaseOverlayManager(operations=DatabaseOperationCollection(prev=operations.prev))
operations.prefetch().apply() operations.prefetch().apply()
overlay_state.manager = manager overlay_state.manager = manager
yield manager yield manager

View file

@ -50,7 +50,7 @@ def accesses_mapdata(func):
changed_geometries.reset() changed_geometries.reset()
with DatabaseOverlayManager.enable(operations=None, commit=writable_method) as manager: with DatabaseOverlayManager.enable(operations=None, commit=writable_method) as manager:
result = func(request, *args, **kwargs) result = func(request, *args, **kwargs)
if manager.new_operations: if manager.operations:
if writable_method: if writable_method:
MapUpdate.objects.create(user=request.user, type='direct_edit') MapUpdate.objects.create(user=request.user, type='direct_edit')
else: else:

View file

@ -218,7 +218,7 @@ def changeset_detail(request, pk):
else: else:
title = next(iter(changed_object.titles.values())) title = next(iter(changed_object.titles.values()))
prev_values = changeset.operations.prev.get(changed_object.obj).values prev_values = changeset.changes.prev.get(changed_object.obj).values
edit_url = None edit_url = None
if not changed_object.deleted: if not changed_object.deleted:

View file

@ -128,7 +128,7 @@ def space_detail(request, level, pk):
def get_changeset_exceeded(request): def get_changeset_exceeded(request):
return request.user_permissions.max_changeset_changes <= len(request.changeset.operations.operations) return request.user_permissions.max_changeset_changes <= len(request.changeset.changes)
@etag(editor_etag_func) @etag(editor_etag_func)