Extracted deserialize fk/m2m functions from Deserializer.

In preparation for handling forward references (refs #26291).
This commit is contained in:
Peter Inglesby 2018-03-20 16:07:39 +00:00 committed by Tim Graham
parent a0c03c62a8
commit 73f7d1755f
2 changed files with 55 additions and 40 deletions

View File

@ -28,6 +28,13 @@ class DeserializationError(Exception):
return cls("%s: (%s:pk=%s) field_value was '%s'" % (original_exc, model, fk, field_value)) return cls("%s: (%s:pk=%s) field_value was '%s'" % (original_exc, model, fk, field_value))
class M2MDeserializationError(Exception):
"""Something bad happened during deserialization of a ManyToManyField."""
def __init__(self, original_exc, pk):
self.original_exc = original_exc
self.pk = pk
class ProgressBar: class ProgressBar:
progress_width = 75 progress_width = 75
@ -235,3 +242,42 @@ def build_instance(Model, data, db):
except Model.DoesNotExist: except Model.DoesNotExist:
pass pass
return obj return obj
def deserialize_m2m_values(field, field_value, using):
model = field.remote_field.model
if hasattr(model._default_manager, 'get_by_natural_key'):
def m2m_convert(value):
if hasattr(value, '__iter__') and not isinstance(value, str):
return model._default_manager.db_manager(using).get_by_natural_key(*value).pk
else:
return model._meta.pk.to_python(value)
else:
def m2m_convert(v):
return model._meta.pk.to_python(v)
try:
values = []
for pk in field_value:
values.append(m2m_convert(pk))
return values
except Exception as e:
raise M2MDeserializationError(e, pk)
def deserialize_fk_value(field, field_value, using):
if field_value is None:
return None
model = field.remote_field.model
default_manager = model._default_manager
field_name = field.remote_field.field_name
if (hasattr(default_manager, 'get_by_natural_key') and
hasattr(field_value, '__iter__') and not isinstance(field_value, str)):
obj = default_manager.db_manager(using).get_by_natural_key(*field_value)
value = getattr(obj, field_name)
# If this is a natural foreign key to an object that has a FK/O2O as
# the foreign key, use the FK value.
if model._meta.pk.remote_field:
value = value.pk
return value
return model._meta.get_field(field_name).to_python(field_value)

View File

@ -117,49 +117,18 @@ def Deserializer(object_list, *, using=DEFAULT_DB_ALIAS, ignorenonexistent=False
# Handle M2M relations # Handle M2M relations
if field.remote_field and isinstance(field.remote_field, models.ManyToManyRel): if field.remote_field and isinstance(field.remote_field, models.ManyToManyRel):
model = field.remote_field.model
if hasattr(model._default_manager, 'get_by_natural_key'):
def m2m_convert(value):
if hasattr(value, '__iter__') and not isinstance(value, str):
return model._default_manager.db_manager(using).get_by_natural_key(*value).pk
else:
return model._meta.pk.to_python(value)
else:
def m2m_convert(v):
return model._meta.pk.to_python(v)
try: try:
m2m_data[field.name] = [] values = base.deserialize_m2m_values(field, field_value, using)
for pk in field_value: except base.M2MDeserializationError as e:
m2m_data[field.name].append(m2m_convert(pk)) raise base.DeserializationError.WithData(e.original_exc, d['model'], d.get('pk'), e.pk)
except Exception as e: m2m_data[field.name] = values
raise base.DeserializationError.WithData(e, d['model'], d.get('pk'), pk)
# Handle FK fields # Handle FK fields
elif field.remote_field and isinstance(field.remote_field, models.ManyToOneRel): elif field.remote_field and isinstance(field.remote_field, models.ManyToOneRel):
model = field.remote_field.model
if field_value is not None:
try: try:
default_manager = model._default_manager value = base.deserialize_fk_value(field, field_value, using)
field_name = field.remote_field.field_name
if hasattr(default_manager, 'get_by_natural_key'):
if hasattr(field_value, '__iter__') and not isinstance(field_value, str):
obj = default_manager.db_manager(using).get_by_natural_key(*field_value)
value = getattr(obj, field.remote_field.field_name)
# If this is a natural foreign key to an object that
# has a FK/O2O as the foreign key, use the FK value
if model._meta.pk.remote_field:
value = value.pk
else:
value = model._meta.get_field(field_name).to_python(field_value)
data[field.attname] = value
else:
data[field.attname] = model._meta.get_field(field_name).to_python(field_value)
except Exception as e: except Exception as e:
raise base.DeserializationError.WithData(e, d['model'], d.get('pk'), field_value) raise base.DeserializationError.WithData(e, d['model'], d.get('pk'), field_value)
else: data[field.attname] = value
data[field.attname] = None
# Handle all other fields # Handle all other fields
else: else:
try: try: