still more as_operations implementation
This commit is contained in:
parent
b5472c307b
commit
ea7aaf4294
2 changed files with 36 additions and 16 deletions
|
@ -1,4 +1,5 @@
|
||||||
import bisect
|
import bisect
|
||||||
|
import json
|
||||||
import operator
|
import operator
|
||||||
import random
|
import random
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
|
@ -6,6 +7,7 @@ from itertools import chain
|
||||||
from typing import Type, Any, Union, Self, TypeVar, Generic
|
from typing import Type, Any, Union, Self, TypeVar, Generic
|
||||||
|
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
|
from django.core import serializers
|
||||||
from django.db.models import Model, Q, CharField, SlugField, DecimalField
|
from django.db.models import Model, Q, CharField, SlugField, DecimalField
|
||||||
from django.db.models.fields import IntegerField, SmallIntegerField, PositiveIntegerField, PositiveSmallIntegerField
|
from django.db.models.fields import IntegerField, SmallIntegerField, PositiveIntegerField, PositiveSmallIntegerField
|
||||||
from django.db.models.fields.reverse_related import ManyToOneRel, OneToOneRel
|
from django.db.models.fields.reverse_related import ManyToOneRel, OneToOneRel
|
||||||
|
@ -120,7 +122,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
|
||||||
occupied_unique_values: dict[ModelName, dict[FieldName, set]] = {}
|
occupied_unique_values: dict[ModelName, dict[FieldName, dict[Any, ObjectID]]] = {}
|
||||||
|
|
||||||
# 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]]] = {}
|
||||||
|
@ -325,18 +327,18 @@ class ChangedObjectCollection(BaseSchema):
|
||||||
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[model] = {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}
|
||||||
|
|
||||||
# lets find which unique values are actually occupied right now
|
# let's find which unique values are actually occupied right now
|
||||||
for model, fields in unique_values_needed.items():
|
for model, fields in unique_values_needed.items():
|
||||||
model_cls = apps.get_model('mapdata', model)
|
model_cls = apps.get_model('mapdata', model)
|
||||||
q = Q()
|
q = Q()
|
||||||
for field_name, values in fields.items():
|
for field_name, values in fields.items():
|
||||||
q |= Q(**{f'{field_name}__in': values})
|
q |= Q(**{f'{field_name}__in': values})
|
||||||
field_names = tuple(fields.keys())
|
start_situation.occupied_unique_values[model] = {}
|
||||||
occupied_values = dict(zip(field_names, zip(*model_cls.objects.filter(q).values_list(*field_names))))
|
for result in model_cls.objects.filter(q).values("id", *fields.keys()):
|
||||||
start_situation.occupied_unique_values[model] = {
|
pk = result.pop("id")
|
||||||
field_name: (values & set(occupied_values.get(field_name, ())))
|
for field_name, value in result.items():
|
||||||
for field_name, values in fields.items()
|
if value in fields[field_name]:
|
||||||
}
|
start_situation.occupied_unique_values[model].setdefault(field_name, {})[value] = pk
|
||||||
|
|
||||||
# let's find which protected references to objects we want to delete have
|
# 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]]]] = {}
|
||||||
|
@ -375,7 +377,12 @@ class ChangedObjectCollection(BaseSchema):
|
||||||
current_objects = {}
|
current_objects = {}
|
||||||
for model_name, changed_objects in self.objects.items():
|
for model_name, changed_objects in self.objects.items():
|
||||||
model = apps.get_model("mapdata", model_name)
|
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())}
|
current_objects[model_name] = {
|
||||||
|
obj["pk"]: obj["fields"]
|
||||||
|
for obj in json.loads(
|
||||||
|
serializers.serialize("json", model.objects.filter(pk__in=changed_objects.keys()))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
start_situation = self.create_start_operation_situation()
|
start_situation = self.create_start_operation_situation()
|
||||||
|
|
||||||
|
@ -413,8 +420,8 @@ class ChangedObjectCollection(BaseSchema):
|
||||||
else:
|
else:
|
||||||
new_remaining_operations.append(sub_op)
|
new_remaining_operations.append(sub_op)
|
||||||
|
|
||||||
if isinstance(new_operation, (CreateObjectOperation, UpdateObjectOperation)):
|
|
||||||
model_cls = apps.get_model('mapdata', new_operation.obj.model)
|
model_cls = apps.get_model('mapdata', new_operation.obj.model)
|
||||||
|
if isinstance(new_operation, (CreateObjectOperation, UpdateObjectOperation)):
|
||||||
for field_name, value in tuple(new_operation.fields.items()):
|
for field_name, value in tuple(new_operation.fields.items()):
|
||||||
if value is DummyValue:
|
if value is DummyValue:
|
||||||
field = model_cls._meta.get_field(field_name)
|
field = model_cls._meta.get_field(field_name)
|
||||||
|
@ -481,8 +488,23 @@ class ChangedObjectCollection(BaseSchema):
|
||||||
new_situation.missing_objects.get(new_operation.obj.model, set()).discard(new_operation.obj.id)
|
new_situation.missing_objects.get(new_operation.obj.model, set()).discard(new_operation.obj.id)
|
||||||
|
|
||||||
if isinstance(new_operation, DeleteObjectOperation):
|
if isinstance(new_operation, DeleteObjectOperation):
|
||||||
# if an object was created it's no longer missing
|
# if an object was deleted it will now be missing
|
||||||
new_situation.missing_objects.get(new_operation.obj.model, set()).discard(new_operation.obj.id)
|
new_situation.missing_objects.get(new_operation.obj.model, set()).add(new_operation.obj.id)
|
||||||
|
|
||||||
|
# all unique values it occupied will no longer be occupied
|
||||||
|
occupied_unique_values = new_situation.occupied_unique_values.get(new_operation.obj.model, {})
|
||||||
|
for field_name, values in tuple(occupied_unique_values.items()):
|
||||||
|
occupied_unique_values[field_name] = {val: pk for val, pk in values.items()
|
||||||
|
if pk != new_operation.obj.model}
|
||||||
|
|
||||||
|
# all references that came from it, will no longer exist
|
||||||
|
for model_name, references in tuple(new_situation.obj_references):
|
||||||
|
new_situation.obj_references[model_name] = {
|
||||||
|
pk: ref for pk, ref in references.items()
|
||||||
|
if ref.obj != new_operation.obj
|
||||||
|
}
|
||||||
|
|
||||||
|
# todo: cascading…?
|
||||||
|
|
||||||
# todo: ...to this
|
# todo: ...to this
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import copy
|
|
||||||
import json
|
import json
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
|
@ -9,10 +8,9 @@ from django.db import transaction
|
||||||
from django.db.models import Model
|
from django.db.models import Model
|
||||||
from django.db.models.fields.related import ManyToManyField
|
from django.db.models.fields.related import ManyToManyField
|
||||||
|
|
||||||
from c3nav.editor.changes import ChangedObjectCollection
|
from c3nav.editor.operations import CreateObjectOperation, \
|
||||||
from c3nav.editor.operations import DatabaseOperation, CreateObjectOperation, \
|
|
||||||
UpdateObjectOperation, DeleteObjectOperation, ClearManyToManyOperation, UpdateManyToManyOperation, \
|
UpdateObjectOperation, DeleteObjectOperation, ClearManyToManyOperation, UpdateManyToManyOperation, \
|
||||||
DatabaseOperationCollection, FieldValuesDict, ObjectReference, PreviousObjectCollection
|
DatabaseOperationCollection, FieldValuesDict, ObjectReference
|
||||||
from c3nav.mapdata.fields import I18nField
|
from c3nav.mapdata.fields import I18nField
|
||||||
from c3nav.mapdata.models import LocationSlug
|
from c3nav.mapdata.models import LocationSlug
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue