Run source/file conflict checks before super().validate() / full_clean()

super().validate() calls full_clean() on the model instance, which raises
a unique-constraint error for (file_root, file_path) when file_path is
empty (e.g. data_source-only requests). Move the conflict guards above the
super() call so they produce clear, actionable error messages before
full_clean() has a chance to surface confusing database-level errors.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Arthur
2026-03-30 15:41:31 -07:00
parent 83fb0a6379
commit a128b7de55

View File

@@ -37,15 +37,11 @@ class ScriptModuleSerializer(ValidatedModelSerializer):
def validate(self, data):
upload_file = data.pop('upload_file', None)
# ScriptModule.save() sets file_root; inject it here so full_clean() succeeds
data['file_root'] = ManagedFileRootPathChoices.SCRIPTS
data = super().validate(data)
data.pop('file_root', None)
if upload_file is not None:
data['upload_file'] = upload_file
# For multipart requests, nested serializer fields (data_source, data_file) are
# silently dropped by DRF's HTML parser, so also check initial_data for raw values.
# These checks must run before super().validate() calls full_clean(), which would
# otherwise surface confusing unique-constraint errors for empty file_path values.
has_data_file = data.get('data_file') or self.initial_data.get('data_file')
has_data_source = data.get('data_source') or self.initial_data.get('data_source')
@@ -66,6 +62,13 @@ class ScriptModuleSerializer(ValidatedModelSerializer):
_("Must upload a file or select a data file to sync")
)
# ScriptModule.save() sets file_root; inject it here so full_clean() succeeds
data['file_root'] = ManagedFileRootPathChoices.SCRIPTS
data = super().validate(data)
data.pop('file_root', None)
if upload_file is not None:
data['upload_file'] = upload_file
return data
def _save_upload(self, upload_file, validated_data):