Skip to content

[FIX] Drop DRF auto UniqueTogetherValidator on upsert/catch serializers#2104

Open
chandrasekharan-zipstack wants to merge 1 commit into
mainfrom
fix/drf-validators-bucket1
Open

[FIX] Drop DRF auto UniqueTogetherValidator on upsert/catch serializers#2104
chandrasekharan-zipstack wants to merge 1 commit into
mainfrom
fix/drf-validators-bucket1

Conversation

@chandrasekharan-zipstack

Copy link
Copy Markdown
Contributor

What

DRF 3.15+ auto-generates a UniqueTogetherValidator from each model's Meta.constraints UniqueConstraint. For serializers whose view already owns uniqueness — either an upsert (update_or_create) or an IntegrityError → DuplicateData catch — that validator fires at is_valid() and returns a non_field_errors: must make a unique set 400 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 (custom validate_name already enforces uniqueness)

Why now / safety

  • No-op on the currently-pinned DRF 3.14 (it only auto-validates legacy unique_together, which these models don't use). So this changes nothing at runtime today.
  • Required prep for the DRF re-bump to 3.17.x (stacked PR follows). Landing it first keeps these endpoints safe the moment 3.15+ is reintroduced.
  • Uniqueness is still enforced — by each view's existing upsert / IntegrityError catch and the DB constraint itself.

Part of the staged DRF uniqueness fix. Cloud-side counterparts (pluggable_apps) go in a separate unstract-cloud PR.

🤖 Generated with Claude Code

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
@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: dd7bc1f3-fe62-4cbd-a8de-d623094c5284

📥 Commits

Reviewing files that changed from the base of the PR and between 7aba21f and 76bf71e.

📒 Files selected for processing (8)
  • backend/adapter_processor_v2/serializers.py
  • backend/api_v2/serializers.py
  • backend/connector_v2/serializers.py
  • backend/notification_v2/serializers.py
  • backend/pipeline_v2/serializers/crud.py
  • backend/prompt_studio/prompt_profile_manager_v2/serializers.py
  • backend/prompt_studio/prompt_studio_v2/serializers.py
  • backend/workflow_manager/workflow_v2/serializers.py

Summary by CodeRabbit

Release Notes

  • Bug Fixes
    • Improved error handling across multiple endpoints to display more user-friendly validation messages instead of generic errors, particularly for duplicate entry scenarios.

Walkthrough

Eight DRF serializer Meta classes across six Django apps (adapter_processor_v2, api_v2, connector_v2, notification_v2, pipeline_v2, prompt_studio, workflow_manager) now explicitly set validators = [], disabling DRF's auto-generated uniqueness validators so that uniqueness violations reach view-level handling instead of returning a 400 before IntegrityErrorMixin can act.

Changes

Disable DRF auto-validators across serializers

Layer / File(s) Summary
Set validators = [] in all affected serializer Meta classes
backend/adapter_processor_v2/serializers.py, backend/api_v2/serializers.py, backend/connector_v2/serializers.py, backend/notification_v2/serializers.py, backend/pipeline_v2/serializers/crud.py, backend/prompt_studio/prompt_profile_manager_v2/serializers.py, backend/prompt_studio/prompt_studio_v2/serializers.py, backend/workflow_manager/workflow_v2/serializers.py
BaseAdapterSerializer, APIDeploymentSerializer, ConnectorInstanceSerializer, NotificationSerializer, PipelineSerializer, ProfileManagerSerializer, ToolStudioPromptSerializer, and WorkflowSerializer each add validators = [] to their Meta class with inline comments explaining that DRF's default uniqueness validator is suppressed so re-save and PUT requests reach view-level IntegrityErrorMixin handling instead of receiving a DRF-generated 400.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~3 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: disabling DRF's auto-generated UniqueTogetherValidator across multiple serializers for upsert and error-catching scenarios.
Description check ✅ Passed The PR description is comprehensive, covering What (the problem and solution), Why (DRF 3.15+ auto-validation issue, timing for 3.17.x bump), and How (disabling validators on 8 serializers). However, the description template requires several sections that are either missing or not filled: "Can this PR break existing features", "Database Migrations", "Env Config", and other optional sections are not addressed.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/drf-validators-bucket1

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sonarqubecloud

Copy link
Copy Markdown

@github-actions

Copy link
Copy Markdown
Contributor

Unstract test results

Per-group results

Status Group Tier Passed Failed Errors Skipped Duration (s)
unit-connectors unit 64 12 0 3 16.8
unit-core unit 0 0 4 0 1.2
unit-platform-service unit 9 0 1 0 1.3
unit-prompt-service unit 15 0 0 0 20.1
unit-rig unit 53 0 0 0 3.3
unit-runner unit 11 0 0 0 3.1
unit-sdk1 unit 390 0 0 0 20.6
unit-tool-registry unit 0 0 1 0 1.3
unit-workers unit 0 0 0 0 17.5
TOTAL 542 12 6 3 85.2

Critical paths

⚠️ Critical paths not yet covered

  • auth-login — User can log in and obtain a session cookie. (entry: POST /api/v1/auth/login; declared coverage: no groups declared)
  • adapter-register-llm — Register and validate an LLM adapter. (entry: POST /api/v1/adapter/; declared coverage: no groups declared)
  • workflow-create-execute — Create a workflow, configure source+destination, execute, poll, fetch result. (entry: POST /api/v1/workflow/{id}/execute/; declared coverage: e2e-workflow)
  • api-deployment-run — Deploy a workflow as an API, POST a document, receive structured JSON. (entry: POST /deployment/api/{org}/{name}/; declared coverage: e2e-api-deployment)
  • prompt-studio-fetch-response — Prompt Studio: create project, add prompt, run single-pass, get response. (entry: POST /api/v1/prompt-studio/prompt-studio-tool/{id}/fetch_response/; declared coverage: e2e-prompt-studio)
  • pipeline-etl-execute — Run an ETL pipeline from source connector to destination. (entry: POST /api/v1/pipeline/{id}/execute/; declared coverage: no groups declared)
  • usage-token-tracking — Per-execution token usage is recorded and retrievable. (entry: GET /api/v1/usage/get_token_usage/; declared coverage: no groups declared)
  • workflow-execution-fan-out — Multi-file workflow execution fans out to file-processing workers and rejoins. (entry: internal: backend → rabbitmq → workers/file_processing; declared coverage: no groups declared)
  • callback-result-delivery — Async results are posted back via the callback worker. (entry: internal: workers/callback → backend /internal endpoints; declared coverage: no groups declared)
✅ Covered critical paths
  • tool-sandbox-exec — covered by unit-runner

@greptile-apps

greptile-apps Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds validators = [] to the Meta class of 8 DRF serializers whose views already own uniqueness enforcement (via IntegrityErrorMixin, explicit IntegrityError catches, or a custom validate_name). The change is a no-op against the currently-pinned DRF 3.14 but is required prep to prevent a staging regression once DRF 3.15+ is reintroduced — that version auto-generates a UniqueTogetherValidator from Meta.constraints UniqueConstraint entries, which fires at is_valid() and short-circuits the view's own duplicate handling with a generic 400.

  • 8 serializers patched: AdapterInstanceSerializer, APIDeploymentSerializer, ConnectorInstanceSerializer, PipelineSerializer, WorkflowSerializer, ToolStudioPromptSerializer, ProfileManagerSerializer, and NotificationSerializer — each has a well-commented rationale pointing to its existing uniqueness owner.
  • NotificationSerializer is the only one without IntegrityErrorMixin; its validate_name field-level validator correctly replicates both (name, api) and (name, pipeline) DB constraints with proper instance exclusion on update.
  • CustomToolSerializer (prompt_studio_core_v2) uses IntegrityErrorMixin with a matching UniqueConstraint on (tool_name, organization) but was not included in this PR — it will hit the same regression on the DRF bump.

Confidence Score: 4/5

Safe to merge as-is against DRF 3.14; the one missing serializer (CustomToolSerializer) does not affect the current deployment but will need the same fix before the DRF bump lands.

All 8 targeted serializers are correctly patched and each has a verified fallback uniqueness owner. CustomToolSerializer in prompt_studio_core_v2 — which also uses IntegrityErrorMixin and carries a UniqueConstraint on (tool_name, organization) — was not included. When DRF 3.15+ is enabled, the auto-validator on that serializer will bypass the mixin's friendly error mapping in the same way the PR fixes for the others. The fix is trivial but should be applied before or alongside the DRF re-bump.

backend/prompt_studio/prompt_studio_core_v2/serializers.py — CustomToolSerializer needs validators = [] before the DRF upgrade lands.

Important Files Changed

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
Loading
%%{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
Loading

Reviews (1): Last reviewed commit: "[FIX] Drop DRF auto UniqueTogetherValida..." | Re-trigger Greptile

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant