[FIX] Drop DRF auto UniqueTogetherValidator on upsert/catch serializers#2104
[FIX] Drop DRF auto UniqueTogetherValidator on upsert/catch serializers#2104chandrasekharan-zipstack wants to merge 1 commit into
Conversation
DRF 3.15+ auto-generates a UniqueTogetherValidator from each model's Meta.constraints UniqueConstraint. For serializers whose view already owns uniqueness (upsert via update_or_create, or IntegrityError->Duplicate catch), that validator fires at is_valid() and 400s on a legitimate re-save before the view can run, short-circuiting the view's duplicate handling. Set Meta.validators = [] on those serializers so the view/DB own uniqueness. No-op on the currently-pinned DRF 3.14 (which only auto-validates legacy unique_together); required once DRF is re-bumped to 3.17.x. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G8hAHc4HUo42zY1g9LAjKu
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (8)
Summary by CodeRabbitRelease Notes
WalkthroughEight DRF serializer ChangesDisable DRF auto-validators across serializers
Estimated code review effort🎯 1 (Trivial) | ⏱️ ~3 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Unstract test resultsPer-group results
Critical paths
|
|
| Filename | Overview |
|---|---|
| backend/adapter_processor_v2/serializers.py | Adds validators = [] to BaseAdapterSerializer; view catches IntegrityError → DuplicateAdapterNameError |
| backend/api_v2/serializers.py | Adds validators = [] to APIDeploymentSerializer; IntegrityErrorMixin already owns uniqueness enforcement |
| backend/connector_v2/serializers.py | Adds validators = [] to ConnectorInstanceSerializer; view catches IntegrityError → DuplicateData |
| backend/notification_v2/serializers.py | Adds validators = [] to NotificationSerializer; custom validate_name correctly checks both (name, api) and (name, pipeline) constraints with instance exclusion |
| backend/pipeline_v2/serializers/crud.py | Adds validators = [] to PipelineSerializer; IntegrityErrorMixin with unique_error_message_map handles both unique_pipeline_name and unique_pipeline_entity constraints |
| backend/prompt_studio/prompt_profile_manager_v2/serializers.py | Adds validators = [] to ProfileManagerSerializer; view owns uniqueness via IntegrityError catch on create |
| backend/prompt_studio/prompt_studio_v2/serializers.py | Adds validators = [] to ToolStudioPromptSerializer; view owns uniqueness via IntegrityError catch, also fixes Mode-B PUT/PATCH update |
| backend/workflow_manager/workflow_v2/serializers.py | Adds validators = [] to WorkflowSerializer; IntegrityErrorMixin with unique_error_message_map provides friendly error on unique_workflow_name violation |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Client Request - POST/PUT/PATCH] --> B[DRF Serializer is_valid]
subgraph "DRF 3.14 (current)"
B --> C14[Field validators run]
C14 --> D14[Cross-field validate runs]
D14 --> E14[No UniqueTogetherValidator auto-generated]
E14 --> F14[serializer.save called]
F14 --> G14[DB insert/update]
G14 -->|IntegrityError| H14[IntegrityErrorMixin / view catch]
H14 --> I14[Friendly 400 ValidationError]
G14 -->|Success| J14[200/201 response]
end
subgraph "DRF 3.15+ WITHOUT this PR"
B --> C15[Field validators run]
C15 --> D15[Auto UniqueTogetherValidator fires]
D15 -->|Duplicate| E15[non_field_errors: must make a unique set]
E15 --> F15[400 BEFORE view logic runs]
D15 -->|OK| G15[serializer.save called]
end
subgraph "DRF 3.15+ WITH this PR - validators = []"
B --> C_fix[Field validators run]
C_fix --> D_fix[No auto UniqueTogetherValidator]
D_fix --> E_fix[Cross-field validate runs]
E_fix --> F_fix[serializer.save called]
F_fix --> G_fix[DB insert/update]
G_fix -->|IntegrityError| H_fix[IntegrityErrorMixin / view catch]
H_fix --> I_fix[Friendly 400 ValidationError]
G_fix -->|Success| J_fix[200/201 response]
end
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
A[Client Request - POST/PUT/PATCH] --> B[DRF Serializer is_valid]
subgraph "DRF 3.14 (current)"
B --> C14[Field validators run]
C14 --> D14[Cross-field validate runs]
D14 --> E14[No UniqueTogetherValidator auto-generated]
E14 --> F14[serializer.save called]
F14 --> G14[DB insert/update]
G14 -->|IntegrityError| H14[IntegrityErrorMixin / view catch]
H14 --> I14[Friendly 400 ValidationError]
G14 -->|Success| J14[200/201 response]
end
subgraph "DRF 3.15+ WITHOUT this PR"
B --> C15[Field validators run]
C15 --> D15[Auto UniqueTogetherValidator fires]
D15 -->|Duplicate| E15[non_field_errors: must make a unique set]
E15 --> F15[400 BEFORE view logic runs]
D15 -->|OK| G15[serializer.save called]
end
subgraph "DRF 3.15+ WITH this PR - validators = []"
B --> C_fix[Field validators run]
C_fix --> D_fix[No auto UniqueTogetherValidator]
D_fix --> E_fix[Cross-field validate runs]
E_fix --> F_fix[serializer.save called]
F_fix --> G_fix[DB insert/update]
G_fix -->|IntegrityError| H_fix[IntegrityErrorMixin / view catch]
H_fix --> I_fix[Friendly 400 ValidationError]
G_fix -->|Success| J_fix[200/201 response]
end
Reviews (1): Last reviewed commit: "[FIX] Drop DRF auto UniqueTogetherValida..." | Re-trigger Greptile



What
DRF 3.15+ auto-generates a
UniqueTogetherValidatorfrom each model'sMeta.constraintsUniqueConstraint. For serializers whose view already owns uniqueness — either an upsert (update_or_create) or anIntegrityError → DuplicateDatacatch — that validator fires atis_valid()and returns anon_field_errors: must make a unique set400 on a legitimate re-save, before the view runs. This short-circuits the view's duplicate handling (was a rc.343 staging regression once DRF 3.15.2 shipped).This sets
Meta.validators = []on those 8 serializers so the view/DB own uniqueness:adapter_processor_v2.AdapterInstanceSerializer(catch →DuplicateAdapterNameError)api_v2.APIDeploymentSerializer(IntegrityErrorMixin)connector_v2.ConnectorInstanceSerializer(catch →DuplicateData)pipeline_v2.PipelineSerializer(IntegrityErrorMixin+ view catch)workflow_v2.WorkflowSerializer(IntegrityErrorMixin)prompt_studio_v2.ToolStudioPromptSerializer(create catches; also fixes Mode-B on PUT/PATCH update)prompt_profile_manager_v2.ProfileManagerSerializer(create catches; fixes update)notification_v2.NotificationSerializer(customvalidate_namealready enforces uniqueness)Why now / safety
unique_together, which these models don't use). So this changes nothing at runtime today.IntegrityErrorcatch and the DB constraint itself.Part of the staged DRF uniqueness fix. Cloud-side counterparts (
pluggable_apps) go in a separateunstract-cloudPR.🤖 Generated with Claude Code