Merge pull request #410 from mouse-reeve/atomicity

Change transaction atomic to avoid cascading errors
This commit is contained in:
Mouse Reeve 2020-12-20 11:24:37 -08:00 committed by GitHub
commit fdb9b71030
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -63,7 +63,6 @@ class ActivityObject:
setattr(self, field.name, value) setattr(self, field.name, value)
@transaction.atomic
def to_model(self, model, instance=None, save=True): def to_model(self, model, instance=None, save=True):
''' convert from an activity to a model instance ''' ''' convert from an activity to a model instance '''
if not isinstance(self, model.activity_serializer): if not isinstance(self, model.activity_serializer):
@ -91,13 +90,14 @@ class ActivityObject:
if not save: if not save:
return instance return instance
# we can't set many to many and reverse fields on an unsaved object with transaction.atomic():
instance.save() # we can't set many to many and reverse fields on an unsaved object
instance.save()
# add many to many fields, which have to be set post-save # add many to many fields, which have to be set post-save
for field in instance.many_to_many_fields: for field in instance.many_to_many_fields:
# mention books/users, for example # mention books/users, for example
field.set_field_from_activity(instance, self) field.set_field_from_activity(instance, self)
# reversed relationships in the models # reversed relationships in the models
for (model_field_name, activity_field_name) in \ for (model_field_name, activity_field_name) in \
@ -106,20 +106,23 @@ class ActivityObject:
values = getattr(self, activity_field_name) values = getattr(self, activity_field_name)
if values is None or values is MISSING: if values is None or values is MISSING:
continue continue
model_field = getattr(model, model_field_name)
try: try:
# this is for one to many # this is for one to many
related_model = getattr(model, model_field_name).field.model related_model = model_field.field.model
related_field_name = model_field.field.name
except AttributeError: except AttributeError:
# it's a one to one or foreign key # it's a one to one or foreign key
related_model = getattr(model, model_field_name)\ related_model = model_field.related.related_model
.related.related_model related_field_name = model_field.related.related_name
values = [values] values = [values]
for item in values: for item in values:
set_related_field.delay( set_related_field.delay(
related_model.__name__, related_model.__name__,
instance.__class__.__name__, instance.__class__.__name__,
instance.__class__.__name__.lower(), related_field_name,
instance.remote_id, instance.remote_id,
item item
) )
@ -145,23 +148,25 @@ def set_related_field(
require_ready=True require_ready=True
) )
if isinstance(data, str): with transaction.atomic():
item = resolve_remote_id(model, data, save=False) if isinstance(data, str):
else: item = resolve_remote_id(model, data, save=False)
# look for a match based on all the available data else:
item = model.find_existing(data) # look for a match based on all the available data
if not item: item = model.find_existing(data)
# create a new model instance if not item:
item = model.activity_serializer(**data) # create a new model instance
item = item.to_model(model, save=False) item = model.activity_serializer(**data)
# this must exist because it's the object that triggered this function item = item.to_model(model, save=False)
instance = origin_model.find_existing_by_remote_id(related_remote_id) # this must exist because it's the object that triggered this function
if not instance: instance = origin_model.find_existing_by_remote_id(related_remote_id)
raise ValueError('Invalid related remote id: %s' % related_remote_id) if not instance:
raise ValueError(
'Invalid related remote id: %s' % related_remote_id)
# edition.parent_work = instance, for example # edition.parent_work = instance, for example
setattr(item, related_field_name, instance) setattr(item, related_field_name, instance)
item.save() item.save()
def resolve_remote_id(model, remote_id, refresh=False, save=True): def resolve_remote_id(model, remote_id, refresh=False, save=True):