Skip to content

Commit

Permalink
Allow fields to be excluded from the model_from_serializable_data() p…
Browse files Browse the repository at this point in the history
…rocess
  • Loading branch information
ababic committed Oct 27, 2024
1 parent cba12ca commit b4dab2c
Showing 1 changed file with 21 additions and 3 deletions.
24 changes: 21 additions & 3 deletions modelcluster/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,18 @@ def get_serializable_data_for_fields(model, exclude_fields=None):
return obj


def model_from_serializable_data(model, data, check_fks=True, strict_fks=False):
def model_from_serializable_data(model, data, check_fks=True, strict_fks=False, exclude_fields=None):
"""
Return an instance of the given model, built from the serialised data
(`data`).
:param check_fks: Optional. If set to False, disables checking of ForeignKey values.
:param strict_fks: Optional. If set to True, enables strict foreign key checks.
:param exclude_fields: Optional. An iterable of field names to exclude from the process.
"""

pk_field = model._meta.pk
exclude = set(exclude_fields or ())
kwargs = {}

# If model is a child via multitable inheritance, we need to set ptr_id fields all the way up
Expand All @@ -71,6 +81,9 @@ def model_from_serializable_data(model, data, check_fks=True, strict_fks=False):
kwargs[pk_field.attname] = data['pk']

for field_name, field_value in data.items():
if field_name in exclude:
continue

try:
field = model._meta.get_field(field_name)
except FieldDoesNotExist:
Expand All @@ -80,11 +93,11 @@ def model_from_serializable_data(model, data, check_fks=True, strict_fks=False):
if isinstance(field, ForeignObjectRel):
continue

if field.remote_field and isinstance(field.remote_field, models.ManyToManyRel):
if field.remote_field and isinstance(field.remote_field, models.ManyToManyRel) and field_name not in exclude:
related_objects = field.remote_field.model._default_manager.filter(pk__in=field_value)
kwargs[field.attname] = list(related_objects)

elif field.remote_field and isinstance(field.remote_field, models.ManyToOneRel):
elif field.remote_field and isinstance(field.remote_field, models.ManyToOneRel) and field_name not in exclude:
if field_value is None:
kwargs[field.attname] = None
else:
Expand All @@ -107,6 +120,11 @@ def model_from_serializable_data(model, data, check_fks=True, strict_fks=False):

else:
raise Exception("can't currently handle on_delete types other than CASCADE, SET_NULL and DO_NOTHING")
elif field_name in exclude:
# In order to not break custom `__init__()` functionality that depends on
# field values being available, use Django's DEFERRED mechanism, allowing
# the canonical field value to be loaded from the database on access.
kwargs[field.name] = models.DEFERRED
else:
value = field.to_python(field_value)

Expand Down

0 comments on commit b4dab2c

Please sign in to comment.