fill some gaps in as_operations
This commit is contained in:
parent
2c6d8a0504
commit
7a22718e46
1 changed files with 42 additions and 22 deletions
|
@ -114,7 +114,7 @@ class OperationSituation(BaseSchema):
|
||||||
missing_objects: dict[ModelName, set[ObjectID]] = {}
|
missing_objects: dict[ModelName, set[ObjectID]] = {}
|
||||||
|
|
||||||
# unique values relevant for these operations that are currently not free
|
# unique values relevant for these operations that are currently not free
|
||||||
values_to_clear: dict[ModelName, dict[FieldName: set]] = {}
|
occupied_unique_values: dict[ModelName, dict[FieldName: set]] = {}
|
||||||
|
|
||||||
# references to objects that need to be removed for in this run
|
# references to objects that need to be removed for in this run
|
||||||
obj_references: dict[ModelName, dict[ObjectID, set[FoundObjectReference]]] = {}
|
obj_references: dict[ModelName, dict[ObjectID, set[FoundObjectReference]]] = {}
|
||||||
|
@ -124,11 +124,14 @@ class OperationSituation(BaseSchema):
|
||||||
return dependency.obj.id not in self.missing_objects.get(dependency.obj.model, set())
|
return dependency.obj.id not in self.missing_objects.get(dependency.obj.model, set())
|
||||||
|
|
||||||
if isinstance(dependency, OperationDependencyNoProtectedReference):
|
if isinstance(dependency, OperationDependencyNoProtectedReference):
|
||||||
return dependency.obj.id not in self.obj_references.get(dependency.obj.model, set())
|
return not any(
|
||||||
|
(reference.on_delete == "PROTECT") for reference in
|
||||||
|
self.obj_references.get(dependency.obj.model, {}).get(dependency.obj.id, ())
|
||||||
|
)
|
||||||
|
|
||||||
if isinstance(dependency, OperationDependencyUniqueValue):
|
if isinstance(dependency, OperationDependencyUniqueValue):
|
||||||
return dependency.value not in self.values_to_clear.get(dependency.obj.model,
|
return dependency.value not in self.occupied_unique_values.get(dependency.obj.model,
|
||||||
{}).get(dependency.field, set())
|
{}).get(dependency.field, set())
|
||||||
|
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
|
@ -220,6 +223,10 @@ class ChangedObjectCollection(BaseSchema):
|
||||||
model = apps.get_model("mapdata", model_name)
|
model = apps.get_model("mapdata", model_name)
|
||||||
|
|
||||||
for changed_obj in changed_objects.values():
|
for changed_obj in changed_objects.values():
|
||||||
|
base_dependencies: set[OperationDependency] = (
|
||||||
|
set() if changed_obj.created else {OperationDependencyObjectExists(obj=changed_obj.obj)}
|
||||||
|
)
|
||||||
|
|
||||||
if changed_obj.deleted:
|
if changed_obj.deleted:
|
||||||
if changed_obj.created:
|
if changed_obj.created:
|
||||||
continue
|
continue
|
||||||
|
@ -227,14 +234,15 @@ class ChangedObjectCollection(BaseSchema):
|
||||||
SingleOperationWithDependencies(
|
SingleOperationWithDependencies(
|
||||||
uid=(changed_obj.obj, "delete"),
|
uid=(changed_obj.obj, "delete"),
|
||||||
operation=DeleteObjectOperation(obj=changed_obj.obj),
|
operation=DeleteObjectOperation(obj=changed_obj.obj),
|
||||||
dependencies={OperationDependencyNoProtectedReference(obj=changed_obj.obj)}
|
dependencies=(
|
||||||
|
base_dependencies | {OperationDependencyNoProtectedReference(obj=changed_obj.obj)}
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
initial_fields = dict()
|
initial_fields = dict()
|
||||||
obj_sub_operations: list[OperationWithDependencies] = []
|
obj_sub_operations: list[OperationWithDependencies] = []
|
||||||
base_dependencies: set[OperationDependency] = {OperationDependencyObjectExists(obj=changed_obj.obj)}
|
|
||||||
for name, value in changed_obj.fields.items():
|
for name, value in changed_obj.fields.items():
|
||||||
if value is None:
|
if value is None:
|
||||||
initial_fields[name] = None
|
initial_fields[name] = None
|
||||||
|
@ -289,39 +297,53 @@ class ChangedObjectCollection(BaseSchema):
|
||||||
from pprint import pprint
|
from pprint import pprint
|
||||||
pprint(operations_with_dependencies)
|
pprint(operations_with_dependencies)
|
||||||
|
|
||||||
start_situation = OperationSituation(
|
start_situation = OperationSituation(remaining_operations_with_dependencies=operations_with_dependencies)
|
||||||
remaining_operations_with_dependencies = operations_with_dependencies,
|
|
||||||
)
|
|
||||||
|
|
||||||
# categorize operations to collect data for simulation/solving and problem detection
|
referenced_objects: dict[ModelName, set[ObjectID]] = {} # objects that need to exist before
|
||||||
missing_objects: dict[ModelName, set[ObjectID]] = {} # objects that need to exist before
|
deleted_existing_objects: dict[ModelName, set[ObjectID]] = {} # objects that need to exist before
|
||||||
|
unique_values_needed: dict[ModelName, dict[FieldName: set]] = {}
|
||||||
for operation in operations_with_dependencies:
|
for operation in operations_with_dependencies:
|
||||||
for dependency in operation.dependencies:
|
for dependency in operation.dependencies:
|
||||||
if isinstance(dependency, OperationDependencyObjectExists):
|
if isinstance(dependency, OperationDependencyObjectExists):
|
||||||
missing_objects.setdefault(dependency.obj.model, set()).add(dependency.obj.id)
|
referenced_objects.setdefault(dependency.obj.model, set()).add(dependency.obj.id)
|
||||||
elif isinstance(dependency, OperationDependencyUniqueValue):
|
elif isinstance(dependency, OperationDependencyUniqueValue):
|
||||||
start_situation.values_to_clear.setdefault(
|
unique_values_needed.setdefault(
|
||||||
dependency.obj.model, {}
|
dependency.obj.model, {}
|
||||||
).setdefault(dependency.field, set()).add(dependency.value)
|
).setdefault(dependency.field, set()).add(dependency.value)
|
||||||
# todo: check for duplicate unique values
|
elif isinstance(dependency, OperationDependencyNoProtectedReference):
|
||||||
|
deleted_existing_objects.setdefault(dependency.obj.model, set()).add(dependency.obj.id)
|
||||||
|
|
||||||
# let's find which objects that need to exist before actually exist
|
# let's find which objects that need to exist before actually exist
|
||||||
for model, ids in missing_objects.items():
|
for model, ids in referenced_objects.items():
|
||||||
model_cls = apps.get_model('mapdata', model)
|
model_cls = apps.get_model('mapdata', model)
|
||||||
ids_found = set(model_cls.objects.filter(pk__in=ids).values_list('pk', flat=True))
|
ids_found = set(model_cls.objects.filter(pk__in=ids).values_list('pk', flat=True))
|
||||||
start_situation.missing_objects = {id_ for id_ in ids if id_ not in ids_found}
|
start_situation.missing_objects[model] = {id_ for id_ in ids if id_ not in ids_found}
|
||||||
|
|
||||||
# let's find which protected references objects we want to delete have
|
# lets find which unique values are actually occupied right now
|
||||||
|
for model, fields in unique_values_needed.items():
|
||||||
|
model_cls = apps.get_model('mapdata', model)
|
||||||
|
q = Q()
|
||||||
|
for field_name, values in fields.items():
|
||||||
|
q |= Q(**{f'{field_name}__in': values})
|
||||||
|
field_names = tuple(fields.keys())
|
||||||
|
occupied_values = dict(zip(field_names, zip(*model_cls.objects.filter(q).values_list(*field_names))))
|
||||||
|
start_situation.occupied_unique_values[model] = {
|
||||||
|
field_name: (values & set(occupied_values.get(field_name, ())))
|
||||||
|
for field_name, values in fields.items()
|
||||||
|
}
|
||||||
|
|
||||||
|
# let's find which protected references to objects we want to delete have
|
||||||
potential_fields: dict[ModelName, dict[FieldName, dict[ModelName, set[ObjectID]]]] = {}
|
potential_fields: dict[ModelName, dict[FieldName, dict[ModelName, set[ObjectID]]]] = {}
|
||||||
for model, ids in missing_objects.items():
|
for model, ids in deleted_existing_objects.items():
|
||||||
# todo: this shouldn't be using missing_objects, should it?
|
# don't check this for objects that don't exist anymore
|
||||||
|
ids -= start_situation.missing_objects.get(model, set())
|
||||||
for field in apps.get_model('mapdata', model)._meta.get_fields():
|
for field in apps.get_model('mapdata', model)._meta.get_fields():
|
||||||
if isinstance(field, (ManyToOneRel, OneToOneRel)) or field.model._meta.app_label != "mapdata":
|
if isinstance(field, (ManyToOneRel, OneToOneRel)) or field.model._meta.app_label != "mapdata":
|
||||||
continue
|
continue
|
||||||
potential_fields.setdefault(field.related_model._meta.model_name,
|
potential_fields.setdefault(field.related_model._meta.model_name,
|
||||||
{}).setdefault(field.field.attname, {})[model] = ids
|
{}).setdefault(field.field.attname, {})[model] = ids
|
||||||
|
|
||||||
# collect all references
|
# collect all references to objects we want to delete
|
||||||
for model, fields in potential_fields.items():
|
for model, fields in potential_fields.items():
|
||||||
model_cls = apps.get_model('mapdata', model)
|
model_cls = apps.get_model('mapdata', model)
|
||||||
q = Q()
|
q = Q()
|
||||||
|
@ -340,8 +362,6 @@ class ChangedObjectCollection(BaseSchema):
|
||||||
on_delete=model_cls._meta.get_field(field).on_delete.__name__)
|
on_delete=model_cls._meta.get_field(field).on_delete.__name__)
|
||||||
)
|
)
|
||||||
|
|
||||||
# todo: do the same with valuea_to_clear
|
|
||||||
|
|
||||||
return start_situation
|
return start_situation
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue