Skip to content

fix: store form_data as dict during viz type migration#36680

Merged
Vitor-Avila merged 1 commit into
masterfrom
fix/form_data_fix
Dec 16, 2025
Merged

fix: store form_data as dict during viz type migration#36680
Vitor-Avila merged 1 commit into
masterfrom
fix/form_data_fix

Conversation

@Vitor-Avila
Copy link
Copy Markdown
Contributor

@Vitor-Avila Vitor-Avila commented Dec 16, 2025

User description

SUMMARY

This is a follow up to #24703. During the viz type migration, we were storing the form_data as a string, which would then cause issues when loading a dashboard.

This PR fixes the issue for new viz type migrations, and also has a DB migration for existing charts in this state.

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

Before

String.form.data.bug.before.mov

**After

String.form.data.bug.after.mov

TESTING INSTRUCTIONS

Unit test added. For manual testing:

  1. Create a legacy chart.
  2. Add it to a dashboard.
  3. Export the chart.
  4. Import it.
  5. Access the dashboard, and confirm you don't get any errors.

ADDITIONAL INFORMATION

  • Has associated issue:
  • Required feature flags:
  • Changes UI
  • Includes DB Migration (follow approval process in SIP-59)
    • Migration is atomic, supports rollback & is backwards-compatible
    • Confirm DB migration upgrade and downgrade tested
    • Runtime estimates and downtime expectations provided
  • Introduces new feature or API
  • Removes existing feature or API

CodeAnt-AI Description

fix: store form_data as dict during viz type migration

What Changed

  • When migrating chart definitions, the query_context's form_data is now stored as a JSON object (dict) instead of a JSON-encoded string, preventing type mismatches when dashboards load.
  • A database migration scans charts of several migrated viz types and converts any query_context.form_data that was saved as a string back into a JSON object so existing dashboards no longer error.
  • Added a unit test that verifies query_context.form_data stays a dict and the migrated viz_type is applied.

Impact

✅ Fewer dashboard load errors after importing legacy charts
✅ Fixed corrupted imported charts for affected visualization types
✅ Consistent query_context.form_data type across migrations

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

@codeant-ai-for-open-source
Copy link
Copy Markdown
Contributor

CodeAnt AI is reviewing your PR.

@github-actions github-actions Bot added the risk:db-migration PRs that require a DB migration label Dec 16, 2025
@dosubot dosubot Bot added the change:backend Requires changing the backend label Dec 16, 2025
@codeant-ai-for-open-source codeant-ai-for-open-source Bot added the size:L This PR changes 100-499 lines, ignoring generated files label Dec 16, 2025
@codeant-ai-for-open-source
Copy link
Copy Markdown
Contributor

Nitpicks 🔍

🔒 No security issues identified
⚡ Recommended areas for review

  • Silent No-op on Invalid query_context
    If output["query_context"] is invalid JSON, the code falls back to an empty dict and will not add form_data even if migration happened. Confirm that this is intentional and acceptable for charts missing or corrupt query_context.

  • Session cleanup not guaranteed
    session.close() and the final session.commit() are called after the loop, but if an unexpected error escapes the loop (e.g., from paginated_update), the session may not be closed. Use a try/finally around session usage to guarantee cleanup and consider rolling back on errors.

  • Empty-string form_data skipped
    The code only attempts to fix form_data when it is truthy and a string. If form_data is an empty string (""), the current check will skip it, leaving broken state in the DB. Empty strings were likely introduced by the bug and should be handled (attempt parse or normalize to an empty dict) rather than silently ignored.

  • Broad exception swallowing
    A bare except Exception hides underlying errors and doesn't log stack traces. This can make debugging migration failures difficult. It also doesn't perform a rollback on unexpected session errors. Prefer logging the exception details and ensuring session state is safe.

  • Insufficient test coverage
    The new test only verifies the case when query_context["form_data"] is already a dict. It does not exercise the prior-bug scenario where form_data was stored as a JSON string inside query_context (the problem this PR aims to fix). Add a test that starts with query_context["form_data"] as a JSON string to ensure the migration always converts it to a dict.

):
try:
query_context = json.loads(slc.query_context)
form_data = query_context.get("form_data")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The code assumes that the decoded query_context is always a dict and immediately calls .get, but if the column contains valid non-object JSON (e.g. a string or list), json.loads will succeed and .get will raise an AttributeError, causing the migration to skip that slice via the broad except and leave it in a broken state; adding an explicit type check for a dict before accessing .get avoids this runtime type error and ensures only well-formed contexts are processed. [type error]

Severity Level: Minor ⚠️

Suggested change
form_data = query_context.get("form_data")
if not isinstance(query_context, dict):
logger.warning(
"Unexpected query_context format for slice %s, skipping", slc.id
)
continue
Why it matters? ⭐

The current code assumes json.loads(...) returns a dict and immediately calls .get. If query_context is valid JSON but not an object (e.g. a list or string), calling .get will raise an AttributeError which is then swallowed by the broad except, skipping the row silently. Adding an explicit isinstance(query_context, dict) check is a small, correct defensive fix that prevents that runtime type error and makes the migration behavior explicit.

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset/migrations/versions/2025-12-16_12-00_f5b5f88d8526_fix_form_data_string_in_query_context.py
**Line:** 84:84
**Comment:**
	*Type Error: The code assumes that the decoded `query_context` is always a dict and immediately calls `.get`, but if the column contains valid non-object JSON (e.g. a string or list), `json.loads` will succeed and `.get` will raise an AttributeError, causing the migration to skip that slice via the broad `except` and leave it in a broken state; adding an explicit type check for a dict before accessing `.get` avoids this runtime type error and ensures only well-formed contexts are processed.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

# Check if form_data is a non-empty string (the bug)
if form_data and isinstance(form_data, str):
try:
query_context["form_data"] = json.loads(form_data)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: After parsing form_data from a JSON string, the code directly assigns the result back into query_context["form_data"] without verifying it is a dict, so if the stored string represents a non-object JSON value (e.g. a list or scalar), later consumers expecting a dict will see an invalid type and may fail when constructing a QueryContext; adding a type check to only persist dict-like parsed values prevents this latent runtime type error. [type error]

Severity Level: Minor ⚠️

Suggested change
query_context["form_data"] = json.loads(form_data)
parsed_form_data = json.loads(form_data)
if not isinstance(parsed_form_data, dict):
logger.warning(
"Unexpected form_data type for slice %s, skipping", slc.id
)
continue
query_context["form_data"] = parsed_form_data
Why it matters? ⭐

The suggestion correctly prevents persisting parsed form_data that isn't a mapping (dict). If the stored string decodes to a list/scalar, downstream code expecting a dict will later fail; skipping such cases (or at least not writing invalid types back) is safer. This is a real correctness improvement for data integrity in the migration.

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset/migrations/versions/2025-12-16_12-00_f5b5f88d8526_fix_form_data_string_in_query_context.py
**Line:** 89:89
**Comment:**
	*Type Error: After parsing `form_data` from a JSON string, the code directly assigns the result back into `query_context["form_data"]` without verifying it is a dict, so if the stored string represents a non-object JSON value (e.g. a list or scalar), later consumers expecting a dict will see an invalid type and may fail when constructing a `QueryContext`; adding a type check to only persist dict-like parsed values prevents this latent runtime type error.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.

@codeant-ai-for-open-source
Copy link
Copy Markdown
Contributor

CodeAnt AI finished reviewing your PR.

Copy link
Copy Markdown
Contributor

@bito-code-review bito-code-review Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Agent Run #6b2fe0

Actionable Suggestions - 1
  • superset/migrations/versions/2025-12-16_12-00_f5b5f88d8526_fix_form_data_string_in_query_context.py - 1
    • Blind exception catch with unused noqa · Line 99-99
Review Details
  • Files reviewed - 3 · Commit Range: 2f34292..2f34292
    • superset/commands/chart/importers/v1/utils.py
    • superset/migrations/versions/2025-12-16_12-00_f5b5f88d8526_fix_form_data_string_in_query_context.py
    • tests/unit_tests/charts/commands/importers/v1/utils_test.py
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Default Agent You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

logger.warning(
"Could not parse query_context for slice %s, skipping", slc.id
)
except Exception: # noqa: S110
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blind exception catch with unused noqa

Avoid catching blind Exception on line 99. Replace with specific exception types (e.g., ValueError, TypeError). The noqa: S110 directive is unused and should be removed.

Code suggestion
Check the AI-generated fix before applying
Suggested change
except Exception: # noqa: S110
except (ValueError, TypeError, KeyError):

Code Review Run #6b2fe0


Should Bito avoid suggestions like this for future reviews? (Manage Rules)

  • Yes, avoid them

Copy link
Copy Markdown
Member

@betodealmeida betodealmeida left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@Vitor-Avila Vitor-Avila merged commit d14f502 into master Dec 16, 2025
84 of 90 checks passed
@Vitor-Avila Vitor-Avila deleted the fix/form_data_fix branch December 16, 2025 21:32
aminghadersohi pushed a commit to aminghadersohi/superset that referenced this pull request Jan 17, 2026
aminghadersohi pushed a commit to aminghadersohi/superset that referenced this pull request Jan 24, 2026
qfcwell pushed a commit to qfcwell/superset that referenced this pull request May 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

change:backend Requires changing the backend risk:db-migration PRs that require a DB migration size/L size:L This PR changes 100-499 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants