[FIX] Bump DRF version to 3.17 to address vulnerability and drop DRF auto UniqueTogetherValidator on upsert/catch serializers#2104
[FIX] Bump DRF version to 3.17 to address vulnerability and drop DRF auto UniqueTogetherValidator on upsert/catch serializers#2104chandrasekharan-zipstack merged 3 commits intomainZipstack/unstract:mainfrom fix/drf-validators-bucket1Zipstack/unstract:fix/drf-validators-bucket1Copy head branch name to clipboard
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
WalkthroughMultiple DRF serializer ChangesDisable DRF auto-validators across serializers
Dependency file updates
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 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 |
|
| Filename | Overview |
|---|---|
| backend/adapter_processor_v2/serializers.py | Adds validators = [] to BaseAdapterSerializer.Meta, which all child serializers inherit. Correct — the view handles duplicates via IntegrityError → DuplicateAdapterNameError. |
| backend/api_v2/serializers.py | Adds validators = [] to APIDeploymentSerializer.Meta. IntegrityErrorMixin maps DB constraint violations to friendly field errors; disabling the DRF auto-validator is correct. |
| backend/notification_v2/serializers.py | Adds validators = [] to NotificationSerializer.Meta. Uniqueness is enforced solely by the application-level validate_name field validator; unlike the other seven serializers, there is no IntegrityErrorMixin catch, so a concurrent duplicate write bypasses validate_name and reaches the DB unguarded. |
| backend/workflow_manager/workflow_v2/serializers.py | Adds validators = [] to WorkflowSerializer.Meta. IntegrityErrorMixin maps unique_workflow_name constraint to a friendly message; correct. |
| backend/pyproject.toml | Bumps djangorestframework from 3.14.0 to 3.17.1. The version pin is exact, consistent with the rest of the dependency list. |
| pyproject.toml | Removes the hook-check-django-migrations optional group and its pinned DRF 3.14 + drf-yasg 1.21.7 transitive set. No remaining references to this group in the repository. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["Client request\n(POST / PUT / PATCH)"] --> B["DRF serializer.is_valid()"]
B --> C{"validators = [] ?"}
C -- "DRF 3.15+ without fix\n(auto UniqueTogetherValidator)" --> D["UniqueTogetherValidator fires\nHTTP 400 on re-save\nbefore view runs"]
C -- "This PR: validators = []" --> E["Field-level validate_*\n+ validate() run"]
E --> F{"Serializer has\nIntegrityErrorMixin?"}
F -- "Yes\n(API, Workflow, Pipeline,\nConnector, Adapter,\nPrompt, ProfileMgr)" --> G["save() wrapped\nIntegrityError → friendly 400"]
F -- "No\n(Notification)" --> H["validate_name checks\nuniqueness at app level"]
H --> I{"Concurrent write\nraces past validate_name?"}
I -- "No (normal path)" --> J["DB write succeeds ✓"]
I -- "Yes (race)" --> K["DB UniqueConstraint fires\nUnhandled IntegrityError → 500"]
G --> J
%%{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\n(POST / PUT / PATCH)"] --> B["DRF serializer.is_valid()"]
B --> C{"validators = [] ?"}
C -- "DRF 3.15+ without fix\n(auto UniqueTogetherValidator)" --> D["UniqueTogetherValidator fires\nHTTP 400 on re-save\nbefore view runs"]
C -- "This PR: validators = []" --> E["Field-level validate_*\n+ validate() run"]
E --> F{"Serializer has\nIntegrityErrorMixin?"}
F -- "Yes\n(API, Workflow, Pipeline,\nConnector, Adapter,\nPrompt, ProfileMgr)" --> G["save() wrapped\nIntegrityError → friendly 400"]
F -- "No\n(Notification)" --> H["validate_name checks\nuniqueness at app level"]
H --> I{"Concurrent write\nraces past validate_name?"}
I -- "No (normal path)" --> J["DB write succeeds ✓"]
I -- "Yes (race)" --> K["DB UniqueConstraint fires\nUnhandled IntegrityError → 500"]
G --> J
Reviews (3): Last reviewed commit: "Merge branch 'main' into fix/drf-validat..." | Re-trigger Greptile
* [FIX] Re-bump djangorestframework 3.14.0 -> 3.17.1 Re-attempts the DRF upgrade reverted in #2098. 3.17.1 is chosen because it: - carries PR encode/django-rest-framework#9766 (3.17.0), so the auto UniqueTogetherValidator honors a UniqueConstraint's violation_error_message for friendly duplicate messages (used by cloud-side create-only serializers), - includes the CVE-2024-21520 (XSS) fix first shipped in 3.15.2. Safe to layer on top of the Meta.validators=[] changes in the parent commit, which neutralize the 3.15+ auto-validator for every upsert / IntegrityError-catch serializer. Django 4.2.30 + Python 3.12 satisfy DRF 3.17's floors. MUST be build + test + staging validated before merge (this bump caused the rc.343 regression). drf-yasg 1.21.7 / drf-standardized-errors compatibility with DRF 3.17 to be verified in CI. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G8hAHc4HUo42zY1g9LAjKu * [FIX] Remove unused hook-check-django-migrations dep group Group had no consumer (migration check runs in backend env); its pinned drf-yasg==1.21.7 paired with DRF 3.17.1 in the root lock was an incompatible skew. Drop the group and relock. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G8hAHc4HUo42zY1g9LAjKu --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Unstract test resultsPer-group results
Critical paths
|
|
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