change set apply: determine operation dependencies
This commit is contained in:
parent
b300e23233
commit
c05760b767
2 changed files with 120 additions and 5 deletions
|
@ -1,12 +1,14 @@
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
from typing import Type
|
from typing import Type, Any, Optional, Annotated, Union
|
||||||
|
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
from django.db.models import Model, OneToOneField, ForeignKey
|
from django.db.models import Model, OneToOneField, ForeignKey
|
||||||
|
from pydantic.config import ConfigDict
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
|
|
||||||
|
@ -25,6 +27,52 @@ class ChangedObject(BaseSchema):
|
||||||
m2m_changes: dict[str, ChangedManyToMany] = {}
|
m2m_changes: dict[str, ChangedManyToMany] = {}
|
||||||
|
|
||||||
|
|
||||||
|
class OperationDependencyObjectExists(BaseSchema):
|
||||||
|
obj: ObjectReference
|
||||||
|
nullable: bool
|
||||||
|
|
||||||
|
|
||||||
|
class OperationDependencyUniqueValue(BaseSchema):
|
||||||
|
model_config = ConfigDict(frozen=True)
|
||||||
|
|
||||||
|
model: str
|
||||||
|
field: str
|
||||||
|
value: Any
|
||||||
|
nullable: bool
|
||||||
|
|
||||||
|
|
||||||
|
class OperationDependencyNoProtectedReference(BaseSchema):
|
||||||
|
model_config = ConfigDict(frozen=True)
|
||||||
|
|
||||||
|
obj: ObjectReference
|
||||||
|
|
||||||
|
|
||||||
|
OperationDependency = Union[
|
||||||
|
OperationDependencyObjectExists,
|
||||||
|
OperationDependencyNoProtectedReference,
|
||||||
|
OperationDependencyUniqueValue,
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class SingleOperationWithDependencies(BaseSchema):
|
||||||
|
operation: DatabaseOperation
|
||||||
|
dependencies: set[OperationDependency] = set()
|
||||||
|
|
||||||
|
|
||||||
|
class MergableOperationsWithDependencies(BaseSchema):
|
||||||
|
children: list[SingleOperationWithDependencies]
|
||||||
|
|
||||||
|
|
||||||
|
OperationWithDependencies = Union[
|
||||||
|
SingleOperationWithDependencies,
|
||||||
|
MergableOperationsWithDependencies,
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class DummyValue:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ChangedObjectCollection(BaseSchema):
|
class ChangedObjectCollection(BaseSchema):
|
||||||
"""
|
"""
|
||||||
A collection of ChangedObject instances, sorted by model and id.
|
A collection of ChangedObject instances, sorted by model and id.
|
||||||
|
@ -104,4 +152,73 @@ class ChangedObjectCollection(BaseSchema):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def as_operations(self) -> DatabaseOperationCollection:
|
def as_operations(self) -> DatabaseOperationCollection:
|
||||||
pass # todo: implement
|
current_objects = {}
|
||||||
|
for model_name, changed_objects in self.objects.items():
|
||||||
|
model = apps.get_model("mapdata", model_name)
|
||||||
|
current_objects[model_name] = {obj.pk: obj for obj in model.objects.filter(pk__in=changed_objects.keys())}
|
||||||
|
|
||||||
|
operations_with_dependencies: list[OperationWithDependencies] = []
|
||||||
|
for model_name, changed_objects in self.objects.items():
|
||||||
|
model = apps.get_model("mapdata", model_name)
|
||||||
|
|
||||||
|
for changed_obj in changed_objects.values():
|
||||||
|
if changed_obj.deleted:
|
||||||
|
if changed_obj.created:
|
||||||
|
continue
|
||||||
|
operations_with_dependencies.append(
|
||||||
|
SingleOperationWithDependencies(
|
||||||
|
operation=DeleteObjectOperation(obj=changed_obj.obj),
|
||||||
|
dependencies={OperationDependencyNoProtectedReference(obj=changed_obj.obj)}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
|
initial_fields = dict()
|
||||||
|
obj_operations: list[OperationWithDependencies] = []
|
||||||
|
for name, value in changed_obj.fields.items():
|
||||||
|
if value is None:
|
||||||
|
initial_fields[name] = None
|
||||||
|
continue
|
||||||
|
field = model._meta.get_field(name)
|
||||||
|
dependencies = set()
|
||||||
|
# todo: prev
|
||||||
|
if field.is_relation:
|
||||||
|
dependencies.add(OperationDependencyObjectExists(obj=ObjectReference(
|
||||||
|
model=field.related_model._meta.model_name,
|
||||||
|
id=value,
|
||||||
|
)))
|
||||||
|
if field.unique:
|
||||||
|
dependencies.add(OperationDependencyUniqueValue(obj=ObjectReference(
|
||||||
|
model=model._meta.model_name,
|
||||||
|
field=name,
|
||||||
|
value=value,
|
||||||
|
)))
|
||||||
|
|
||||||
|
if not dependencies:
|
||||||
|
initial_fields[name] = None
|
||||||
|
continue
|
||||||
|
|
||||||
|
initial_fields[name] = DummyValue
|
||||||
|
obj_operations.append(SingleOperationWithDependencies(
|
||||||
|
operation=UpdateObjectOperation(obj=changed_obj.obj, fields={name: value}),
|
||||||
|
dependencies=dependencies
|
||||||
|
))
|
||||||
|
|
||||||
|
obj_operations.insert(0, SingleOperationWithDependencies(
|
||||||
|
operation=(CreateObjectOperation if changed_obj.created else UpdateObjectOperation)(
|
||||||
|
obj=changed_obj.obj,
|
||||||
|
fields=initial_fields,
|
||||||
|
)
|
||||||
|
))
|
||||||
|
|
||||||
|
if len(obj_operations) == 1:
|
||||||
|
operations_with_dependencies.append(obj_operations[0])
|
||||||
|
else:
|
||||||
|
operations_with_dependencies.append(MergableOperationsWithDependencies(operations=obj_operations))
|
||||||
|
|
||||||
|
from pprint import pprint
|
||||||
|
pprint(operations_with_dependencies)
|
||||||
|
|
||||||
|
# todo: continue here
|
||||||
|
|
||||||
|
return DatabaseOperationCollection()
|
||||||
|
|
|
@ -77,8 +77,6 @@ class PreviousObjectCollection(BaseSchema):
|
||||||
|
|
||||||
class BaseOperation(BaseSchema):
|
class BaseOperation(BaseSchema):
|
||||||
obj: ObjectReference
|
obj: ObjectReference
|
||||||
uuid: UUID = Field(default_factory=uuid4)
|
|
||||||
datetime: Annotated[datetime.datetime, Field(default_factory=timezone.now)]
|
|
||||||
|
|
||||||
def apply(self, values: FieldValuesDict, instance: Model) -> Model:
|
def apply(self, values: FieldValuesDict, instance: Model) -> Model:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue