Skip to content

Commit

Permalink
Avoid validating foreign key checks if the key never changed
Browse files Browse the repository at this point in the history
This leverages our custom full_clean implementation (#141) in
combination with the LoadedValues mixin.  This allows us to know when
a value did not change and avoid validating the foreign key just
before save, because the foreign key was fine as it was stored, so it
cannot suddenly become invalid.

For complex models with many relations or which are saved a lot, this
makes a big difference, as the reduced number of queries can make
things quite a bit more efficient.
  • Loading branch information
Peter Bex committed Sep 1, 2020
1 parent b5ec096 commit dd489f6
Show file tree
Hide file tree
Showing 2 changed files with 15 additions and 6 deletions.
13 changes: 11 additions & 2 deletions binder/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,7 @@ def save(self, *args, **kwargs):
return super().save(*args, **kwargs)


def full_clean(self, *args, **kwargs):
def full_clean(self, exclude=None, *args, **kwargs):
# Determine if the field needs an extra nullability check.
# Expects the field object (not the field name)
def field_needs_nullability_check(field):
Expand All @@ -487,10 +487,19 @@ def field_needs_nullability_check(field):
return False


# Gather unchanged fields if LoadedValues mixin available, to
# avoid querying uniqueness constraints for unchanged
# relations (an useful performance optimization).
if hasattr(self, 'field_changed'):
exclude = set(exclude) if exclude else set()
for f in self.binder_concrete_fields_as_dict(skip_deferred_fields=True):
if not self.field_changed(f):
exclude.add(f)

validation_errors = defaultdict(list)

try:
res = super().full_clean(*args, **kwargs)
res = super().full_clean(exclude=exclude, *args, **kwargs)
except ValidationError as ve:
if hasattr(ve, 'error_dict'):
for key, value in ve.error_dict.items():
Expand Down
8 changes: 4 additions & 4 deletions binder/plugins/loaded_values.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@ def field_changed(self, *fields):
return True

for field in fields:
field_def = self._meta.get_field(field)

try:
current_value = getattr(self, field)
current_value = getattr(self, field_def.attname)
except ObjectDoesNotExist:
current_value = None

if isinstance(current_value, Model):
current_value = current_value.pk
elif isinstance(current_value, FieldFile):
if isinstance(current_value, FieldFile):
current_value = current_value.name
if current_value is None:
current_value = ''
Expand Down

0 comments on commit dd489f6

Please sign in to comment.