update m2m tracking code in new changeset implementation

This commit is contained in:
Laura Klünder 2024-08-24 17:58:05 +02:00
parent 9f95f663d4
commit 68346f821f

View file

@ -55,16 +55,11 @@ class DeleteObjectChange(BaseChange):
type: Literal["delete"] = "delete" type: Literal["delete"] = "delete"
class AddManyToManyChange(BaseSchema): class UpdateManyToManyChange(BaseSchema):
type: Literal["m2m_add"] = "m2m_add" type: Literal["m2m_add"] = "m2m_update"
field: str field: str
values: list[int] add_values: set[int] = set()
remove_values: set[int] = set()
class RemoveManyToManyChange(BaseSchema):
type: Literal["m2m_remove"] = "m2m_remove"
field: str
values: list[int]
class ClearManyToManyChange(BaseSchema): class ClearManyToManyChange(BaseSchema):
@ -77,8 +72,7 @@ ChangeSetChange = Annotated[
CreateObjectChange, CreateObjectChange,
UpdateObjectChange, UpdateObjectChange,
DeleteObjectChange, DeleteObjectChange,
AddManyToManyChange, UpdateManyToManyChange,
RemoveManyToManyChange,
ClearManyToManyChange, ClearManyToManyChange,
], ],
Discriminator("type"), Discriminator("type"),
@ -116,13 +110,24 @@ def enable_changeset_overlay(changeset):
@dataclass @dataclass
class ChangesetOverlayManager: class ChangesetOverlayManager:
changes: ChangeSetChanges changes: ChangeSetChanges
new_changes: bool = False new_changes: list[ChangeSetChange] = field(default_factory=list)
pre_change_values: dict[ObjectReference, FieldValuesDict] = field(default_factory=dict) pre_change_values: dict[ObjectReference, FieldValuesDict] = field(default_factory=dict)
def get_model_field_values(self, instance: Model) -> FieldValuesDict: @staticmethod
def get_model_field_values(instance: Model) -> FieldValuesDict:
return json.loads(serializers.serialize("json", [instance]))[0]["fields"] return json.loads(serializers.serialize("json", [instance]))[0]["fields"]
def handle_pre_change_instance(self, sender: Type[Model], instance: Model, **kwargs): def get_ref_and_pre_change_values(self, instance: Model) -> ObjectReference:
ref = ObjectReference.from_instance(instance)
pre_change_values = self.pre_change_values.pop(ref, None)
if pre_change_values:
self.changes.prev_values[ref] = pre_change_values
self.changes.prev_reprs[ref] = str(instance)
return ref
def handle_pre_change_instance(self, instance: Model, **kwargs):
if instance.pk is None: if instance.pk is None:
return return
ref = ObjectReference.from_instance(instance) ref = ObjectReference.from_instance(instance)
@ -131,40 +136,23 @@ class ChangesetOverlayManager:
instance._meta.model.objects.get(pk=instance.pk) instance._meta.model.objects.get(pk=instance.pk)
) )
def handle_post_save(self, sender: Type[Model], instance: Model, created: bool, def handle_post_save(self, instance: Model, created: bool, update_fields: set | None, **kwargs):
update_fields: set | None, **kwargs):
field_values = self.get_model_field_values(instance) field_values = self.get_model_field_values(instance)
ref = ObjectReference.from_instance(instance) ref = self.get_ref_and_pre_change_values(instance)
if created: if created:
self.changes.changes.append(CreateObjectChange(obj=ref, fields=field_values)) self.new_changes.append(CreateObjectChange(obj=ref, fields=field_values))
from pprint import pprint
pprint(self.changes)
return return
if update_fields: if update_fields:
field_values = {name: value for name, value in field_values.items() if name in update_fields} field_values = {name: value for name, value in field_values.items() if name in update_fields}
pre_change_values = self.pre_change_values.pop(ref, None) self.new_changes.append(UpdateObjectChange(obj=ref, fields=field_values))
if pre_change_values:
self.changes.prev_values[ref] = pre_change_values
self.changes.prev_reprs[ref] = str(instance)
self.changes.changes.append(UpdateObjectChange(obj=ref, fields=field_values))
from pprint import pprint
pprint(self.changes)
def handle_post_delete(self, sender: Type[Model], instance: Model, **kwargs): def handle_post_delete(self, instance: Model, **kwargs):
ref = ObjectReference.from_instance(instance) ref = self.get_ref_and_pre_change_values(instance)
pre_change_values = self.pre_change_values.pop(ref, None) self.new_changes.append(DeleteObjectChange(obj=ref))
if pre_change_values:
self.changes.prev_values[ref] = pre_change_values
self.changes.prev_reprs[ref] = str(instance)
self.changes.changes.append(DeleteObjectChange(
obj=ref,
))
from pprint import pprint
pprint(self.changes)
def handle_m2m_changed(self, sender: Type[Model], instance: Model, action: str, model: Type[Model], def handle_m2m_changed(self, sender: Type[Model], instance: Model, action: str, model: Type[Model],
pk_set: set | None, reverse: bool, **kwargs): pk_set: set | None, reverse: bool, **kwargs):
@ -176,38 +164,33 @@ class ChangesetOverlayManager:
for field in instance._meta.get_fields(): for field in instance._meta.get_fields():
if isinstance(field, ManyToManyField): if isinstance(field, ManyToManyField):
# todo: actually identify field!!
raise NotImplementedError
break break
else: else:
raise ValueError raise ValueError
ref = ObjectReference.from_instance(instance) ref = self.get_ref_and_pre_change_values(instance)
pre_change_values = self.pre_change_values.pop(ref, None)
if pre_change_values:
self.changes.prev_values[ref] = pre_change_values
match(action): if action == "post_clear":
case "post_add": self.new_changes.append(ClearManyToManyChange(obj=ref, field=field.name))
self.changes.changes.append(AddManyToManyChange( return
obj=ref,
field=field.name,
values=list(pk_set),
))
case "post_remove": if self.new_changes:
self.changes.changes.append(RemoveManyToManyChange( last_change = self.new_changes[-1]
obj=ref, if isinstance(last_change, UpdateManyToManyChange) and last_change == ref and last_change == field.name:
field=field.name, if action == "post_add":
values=list(pk_set), last_change.add_values.update(pk_set)
)) last_change.remove_values.difference_update(pk_set)
else:
last_change.add_values.difference_update(pk_set)
last_change.remove_values.update(pk_set)
return
case "post_clear": if action == "post_add":
self.changes.changes.append(ClearManyToManyChange( self.new_changes.append(UpdateManyToManyChange(obj=ref, field=field.name, add_values=list(pk_set)))
obj=ref, else:
field=field.name, self.new_changes.append(UpdateManyToManyChange(obj=ref, field=field.name, remove_values=list(pk_set)))
))
from pprint import pprint
pprint(self.changes)
def handle_pre_change_instance(sender: Type[Model], **kwargs): def handle_pre_change_instance(sender: Type[Model], **kwargs):