🐛 Fix alias support for Pydantic v2 - #1577

Merged
tiangolo merged 18 commits into
fastapi:mainfrom
ravishan16:issue-1536-alias-pydantic-v2
Dec 23, 2025
Merged

🐛 Fix alias support for Pydantic v2#1577
tiangolo merged 18 commits into
fastapi:mainfrom
ravishan16:issue-1536-alias-pydantic-v2

Conversation

@ravishan16

@ravishan16ravishan16 commented Sep 25, 2025

Copy link
Copy Markdown
Contributor

Description

This PR fixes the Field(alias="...") functionality that was broken in Pydantic v2 while maintaining full backward compatibility with Pydantic v1.

Problem

In Pydantic v2, the alias parameter in SQLModel's Field function was not working correctly for validation and serialization. The issue was that Pydantic v2 requires explicit validation_alias and serialization_alias parameters, but SQLModel wasn't automatically propagating the alias value to these parameters.

Solution

Changes Made:

  1. Enhanced Field function:

    • Added validation_alias and serialization_alias parameters
    • Implemented version-specific parameter handling
  2. Automatic alias propagation:

    • Modified post_init_field_info in _compat.py to automatically set validation_alias and serialization_alias from alias when not explicitly provided (Pydantic v2 only) UPD YuriiMotov: updated it to just using alias value for validation_alias and serialization_alias if they are not specified directly
  3. Backward compatibility:

    • Filter out v2-only parameters from FieldInfo in Pydantic v1
    • Raise clear RuntimeError when v2 features are used with v1
    • Conditional test execution based on Pydantic version
  4. Comprehensive testing:

    • Added extensive test coverage for both Pydantic v1 and v2
    • Tests automatically skip v2-specific features when running with v1
    • Covers all alias scenarios including edge cases

Breaking Changes
None. This is purely a bug fix with backward compatibility maintained.

Closes
Fixes#1536

@ravishan16
ravishan16 marked this pull request as ready for review September 25, 2025 23:02
@svlandegsvlandeg added the bug Something isn't working label Sep 26, 2025
@svlandegsvlandeg self-assigned this Sep 26, 2025

@YuriiMotovYuriiMotov left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The idea looks good to me!

A couple of more things:

  • I think it would be nice to use alias as a default value for DB column. It can then be overridden by sa_column=Column("column_name") - decided to handle it in separate PR later
  • We should probably test how it works with alias generators specified in model's config (is it supported? - UPD: YES) (done)

PS: @svlandeg, sorry, I only noticed you self-assigned this PR when I was about to press "Submit review".. Hope you don't mind I submitted this review)

Comment threadsqlmodel/_compat.py Outdated
Comment threadsqlmodel/main.py Outdated
Comment threadtests/test_aliases.py
Comment threadtests/test_aliases.py Outdated
Comment threadtests/test_aliases.py Outdated
Comment threadtests/test_aliases.py
Comment threadsqlmodel/main.py
*,
default_factory: Optional[NoArgAnyCallable] = None,
alias: Optional[str] = None,
validation_alias: Optional[str] = None,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Pydantic's validation_alias type annotation is wider (validation_alias: str | AliasPath | AliasChoices | None), but I think it's fine if we only support str for now and extend it later

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I don't have a strong preference on this.

@svlandeg

Copy link
Copy Markdown
Member

Hope you don't mind I submitted this review

Of course not! I tentatively assign PRs when I want to review them in the near future, just to keep track of them, but sometimes other tasks come in between 🙈 Feel free to review whatever you want!

@ravishan16

Copy link
Copy Markdown
ContributorAuthor

@YuriiMotov@svlandeg I’ve addressed all the comments from the review. Let me know if there’s anything else you’d like me to focus on.

@svlandegsvlandeg removed their assignment Oct 6, 2025
YuriiMotov

This comment was marked as resolved.

@YuriiMotovYuriiMotov mentioned this pull request Oct 8, 2025
1 task
@ravishan16

This comment was marked as resolved.

@ravishan16

This comment was marked as resolved.

@YuriiMotov

Copy link
Copy Markdown
Member

I simplified code a bit - moved alias propagation logic to Field.__init__ so that it would be all in one place.
Also, got rid of deprecation warnings in test.

@YuriiMotovYuriiMotov left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM!

Implementation is not breaking. It also handles the workaround with

Field(schema_extra={"validation_alias": "field_alias"})

that people currently use.

The only use case it can break is if users use

Field(schema_extra={"validation_alias": "field_alias"})

and want to use this model with both, Pydantic V1 and V2.
It will now raise

RuntimeError("validation_alias is not supported in Pydantic v1")

for Pydantic V1.
But it's easily fixable - just remove validation_alias (now alias works with both versions, so that workaround is not needed).

To Sebastian:

Some tests are parameterized to be run with both, SQLModel and Pydantic models, to show that they behave the same way. If you don't like it, it's easy to remove it.

@ravishan16, thank you!

@YuriiMotovYuriiMotov changed the title 🐛 Fix Field alias support for Pydantic v2 with backward compatibility🐛 Fix alias support for Pydantic v2Oct 9, 2025
@xdewx

Copy link
Copy Markdown

Could you please let me know when this PR will be merged and released?

@tiangolotiangolo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Awesome, thank you @ravishan16! 🚀

And thanks @YuriiMotov! 🍰

This will be available in SQLModel 0.0.29, released in the next hours. 🎉

@tiangolo
tiangolo enabled auto-merge (squash) December 23, 2025 20:57
@tiangolo
tiangolo merged commit 5b1d8e6 into fastapi:mainDec 23, 2025
24 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugSomething isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Field(alias="") doesn't work with Pydantic V2

5 participants

@ravishan16@svlandeg@YuriiMotov@xdewx@tiangolo
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

🐛 Fix alias support for Pydantic v2 - #1577

Merged
tiangolo merged 18 commits into
fastapi:mainfrom
ravishan16:issue-1536-alias-pydantic-v2
Dec 23, 2025
Merged

🐛 Fix alias support for Pydantic v2#1577
tiangolo merged 18 commits into
fastapi:mainfrom
ravishan16:issue-1536-alias-pydantic-v2

Conversation

@ravishan16

@ravishan16ravishan16 commented Sep 25, 2025

Copy link
Copy Markdown
Contributor

Description

This PR fixes the Field(alias="...") functionality that was broken in Pydantic v2 while maintaining full backward compatibility with Pydantic v1.

Problem

In Pydantic v2, the alias parameter in SQLModel's Field function was not working correctly for validation and serialization. The issue was that Pydantic v2 requires explicit validation_alias and serialization_alias parameters, but SQLModel wasn't automatically propagating the alias value to these parameters.

Solution

Changes Made:

  1. Enhanced Field function:

    • Added validation_alias and serialization_alias parameters
    • Implemented version-specific parameter handling
  2. Automatic alias propagation:

    • Modified post_init_field_info in _compat.py to automatically set validation_alias and serialization_alias from alias when not explicitly provided (Pydantic v2 only) UPD YuriiMotov: updated it to just using alias value for validation_alias and serialization_alias if they are not specified directly
  3. Backward compatibility:

    • Filter out v2-only parameters from FieldInfo in Pydantic v1
    • Raise clear RuntimeError when v2 features are used with v1
    • Conditional test execution based on Pydantic version
  4. Comprehensive testing:

    • Added extensive test coverage for both Pydantic v1 and v2
    • Tests automatically skip v2-specific features when running with v1
    • Covers all alias scenarios including edge cases

Breaking Changes
None. This is purely a bug fix with backward compatibility maintained.

Closes
Fixes#1536

@ravishan16
ravishan16 marked this pull request as ready for review September 25, 2025 23:02
@svlandegsvlandeg added the bug Something isn't working label Sep 26, 2025
@svlandegsvlandeg self-assigned this Sep 26, 2025

@YuriiMotovYuriiMotov left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The idea looks good to me!

A couple of more things:

  • I think it would be nice to use alias as a default value for DB column. It can then be overridden by sa_column=Column("column_name") - decided to handle it in separate PR later
  • We should probably test how it works with alias generators specified in model's config (is it supported? - UPD: YES) (done)

PS: @svlandeg, sorry, I only noticed you self-assigned this PR when I was about to press "Submit review".. Hope you don't mind I submitted this review)

Comment threadsqlmodel/_compat.py Outdated
Comment threadsqlmodel/main.py Outdated
Comment threadtests/test_aliases.py
Comment threadtests/test_aliases.py Outdated
Comment threadtests/test_aliases.py Outdated
Comment threadtests/test_aliases.py
Comment threadsqlmodel/main.py
*,
default_factory: Optional[NoArgAnyCallable] = None,
alias: Optional[str] = None,
validation_alias: Optional[str] = None,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Pydantic's validation_alias type annotation is wider (validation_alias: str | AliasPath | AliasChoices | None), but I think it's fine if we only support str for now and extend it later

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I don't have a strong preference on this.

@svlandeg

Copy link
Copy Markdown
Member

Hope you don't mind I submitted this review

Of course not! I tentatively assign PRs when I want to review them in the near future, just to keep track of them, but sometimes other tasks come in between 🙈 Feel free to review whatever you want!

@ravishan16

Copy link
Copy Markdown
ContributorAuthor

@YuriiMotov@svlandeg I’ve addressed all the comments from the review. Let me know if there’s anything else you’d like me to focus on.

@svlandegsvlandeg removed their assignment Oct 6, 2025
YuriiMotov

This comment was marked as resolved.

@YuriiMotovYuriiMotov mentioned this pull request Oct 8, 2025
1 task
@ravishan16

This comment was marked as resolved.

@ravishan16

This comment was marked as resolved.

@YuriiMotov

Copy link
Copy Markdown
Member

I simplified code a bit - moved alias propagation logic to Field.__init__ so that it would be all in one place.
Also, got rid of deprecation warnings in test.

@YuriiMotovYuriiMotov left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM!

Implementation is not breaking. It also handles the workaround with

Field(schema_extra={"validation_alias": "field_alias"})

that people currently use.

The only use case it can break is if users use

Field(schema_extra={"validation_alias": "field_alias"})

and want to use this model with both, Pydantic V1 and V2.
It will now raise

RuntimeError("validation_alias is not supported in Pydantic v1")

for Pydantic V1.
But it's easily fixable - just remove validation_alias (now alias works with both versions, so that workaround is not needed).

To Sebastian:

Some tests are parameterized to be run with both, SQLModel and Pydantic models, to show that they behave the same way. If you don't like it, it's easy to remove it.

@ravishan16, thank you!

@YuriiMotovYuriiMotov changed the title 🐛 Fix Field alias support for Pydantic v2 with backward compatibility🐛 Fix alias support for Pydantic v2Oct 9, 2025
@xdewx

Copy link
Copy Markdown

Could you please let me know when this PR will be merged and released?

@tiangolotiangolo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Awesome, thank you @ravishan16! 🚀

And thanks @YuriiMotov! 🍰

This will be available in SQLModel 0.0.29, released in the next hours. 🎉

@tiangolo
tiangolo enabled auto-merge (squash) December 23, 2025 20:57
@tiangolo
tiangolo merged commit 5b1d8e6 into fastapi:mainDec 23, 2025
24 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugSomething isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Field(alias="") doesn't work with Pydantic V2

5 participants

@ravishan16@svlandeg@YuriiMotov@xdewx@tiangolo
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

🐛 Fix alias support for Pydantic v2 - #1577

Merged
tiangolo merged 18 commits into
fastapi:mainfrom
ravishan16:issue-1536-alias-pydantic-v2
Dec 23, 2025
Merged

🐛 Fix alias support for Pydantic v2#1577
tiangolo merged 18 commits into
fastapi:mainfrom
ravishan16:issue-1536-alias-pydantic-v2

Conversation

@ravishan16

@ravishan16ravishan16 commented Sep 25, 2025

Copy link
Copy Markdown
Contributor

Description

This PR fixes the Field(alias="...") functionality that was broken in Pydantic v2 while maintaining full backward compatibility with Pydantic v1.

Problem

In Pydantic v2, the alias parameter in SQLModel's Field function was not working correctly for validation and serialization. The issue was that Pydantic v2 requires explicit validation_alias and serialization_alias parameters, but SQLModel wasn't automatically propagating the alias value to these parameters.

Solution

Changes Made:

  1. Enhanced Field function:

    • Added validation_alias and serialization_alias parameters
    • Implemented version-specific parameter handling
  2. Automatic alias propagation:

    • Modified post_init_field_info in _compat.py to automatically set validation_alias and serialization_alias from alias when not explicitly provided (Pydantic v2 only) UPD YuriiMotov: updated it to just using alias value for validation_alias and serialization_alias if they are not specified directly
  3. Backward compatibility:

    • Filter out v2-only parameters from FieldInfo in Pydantic v1
    • Raise clear RuntimeError when v2 features are used with v1
    • Conditional test execution based on Pydantic version
  4. Comprehensive testing:

    • Added extensive test coverage for both Pydantic v1 and v2
    • Tests automatically skip v2-specific features when running with v1
    • Covers all alias scenarios including edge cases

Breaking Changes
None. This is purely a bug fix with backward compatibility maintained.

Closes
Fixes#1536

@ravishan16
ravishan16 marked this pull request as ready for review September 25, 2025 23:02
@svlandegsvlandeg added the bug Something isn't working label Sep 26, 2025
@svlandegsvlandeg self-assigned this Sep 26, 2025

@YuriiMotovYuriiMotov left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The idea looks good to me!

A couple of more things:

  • I think it would be nice to use alias as a default value for DB column. It can then be overridden by sa_column=Column("column_name") - decided to handle it in separate PR later
  • We should probably test how it works with alias generators specified in model's config (is it supported? - UPD: YES) (done)

PS: @svlandeg, sorry, I only noticed you self-assigned this PR when I was about to press "Submit review".. Hope you don't mind I submitted this review)

Comment threadsqlmodel/_compat.py Outdated
Comment threadsqlmodel/main.py Outdated
Comment threadtests/test_aliases.py
Comment threadtests/test_aliases.py Outdated
Comment threadtests/test_aliases.py Outdated
Comment threadtests/test_aliases.py
Comment threadsqlmodel/main.py
*,
default_factory: Optional[NoArgAnyCallable] = None,
alias: Optional[str] = None,
validation_alias: Optional[str] = None,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Pydantic's validation_alias type annotation is wider (validation_alias: str | AliasPath | AliasChoices | None), but I think it's fine if we only support str for now and extend it later

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I don't have a strong preference on this.

@svlandeg

Copy link
Copy Markdown
Member

Hope you don't mind I submitted this review

Of course not! I tentatively assign PRs when I want to review them in the near future, just to keep track of them, but sometimes other tasks come in between 🙈 Feel free to review whatever you want!

@ravishan16

Copy link
Copy Markdown
ContributorAuthor

@YuriiMotov@svlandeg I’ve addressed all the comments from the review. Let me know if there’s anything else you’d like me to focus on.

@svlandegsvlandeg removed their assignment Oct 6, 2025
YuriiMotov

This comment was marked as resolved.

@YuriiMotovYuriiMotov mentioned this pull request Oct 8, 2025
1 task
@ravishan16

This comment was marked as resolved.

@ravishan16

This comment was marked as resolved.

@YuriiMotov

Copy link
Copy Markdown
Member

I simplified code a bit - moved alias propagation logic to Field.__init__ so that it would be all in one place.
Also, got rid of deprecation warnings in test.

@YuriiMotovYuriiMotov left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM!

Implementation is not breaking. It also handles the workaround with

Field(schema_extra={"validation_alias": "field_alias"})

that people currently use.

The only use case it can break is if users use

Field(schema_extra={"validation_alias": "field_alias"})

and want to use this model with both, Pydantic V1 and V2.
It will now raise

RuntimeError("validation_alias is not supported in Pydantic v1")

for Pydantic V1.
But it's easily fixable - just remove validation_alias (now alias works with both versions, so that workaround is not needed).

To Sebastian:

Some tests are parameterized to be run with both, SQLModel and Pydantic models, to show that they behave the same way. If you don't like it, it's easy to remove it.

@ravishan16, thank you!

@YuriiMotovYuriiMotov changed the title 🐛 Fix Field alias support for Pydantic v2 with backward compatibility🐛 Fix alias support for Pydantic v2Oct 9, 2025
@xdewx

Copy link
Copy Markdown

Could you please let me know when this PR will be merged and released?

@tiangolotiangolo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Awesome, thank you @ravishan16! 🚀

And thanks @YuriiMotov! 🍰

This will be available in SQLModel 0.0.29, released in the next hours. 🎉

@tiangolo
tiangolo enabled auto-merge (squash) December 23, 2025 20:57
@tiangolo
tiangolo merged commit 5b1d8e6 into fastapi:mainDec 23, 2025
24 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugSomething isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Field(alias="") doesn't work with Pydantic V2

5 participants

@ravishan16@svlandeg@YuriiMotov@xdewx@tiangolo
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

🐛 Fix alias support for Pydantic v2 - #1577

Merged
tiangolo merged 18 commits into
fastapi:mainfrom
ravishan16:issue-1536-alias-pydantic-v2
Dec 23, 2025
Merged

🐛 Fix alias support for Pydantic v2#1577
tiangolo merged 18 commits into
fastapi:mainfrom
ravishan16:issue-1536-alias-pydantic-v2

Conversation

@ravishan16

@ravishan16ravishan16 commented Sep 25, 2025

Copy link
Copy Markdown
Contributor

Description

This PR fixes the Field(alias="...") functionality that was broken in Pydantic v2 while maintaining full backward compatibility with Pydantic v1.

Problem

In Pydantic v2, the alias parameter in SQLModel's Field function was not working correctly for validation and serialization. The issue was that Pydantic v2 requires explicit validation_alias and serialization_alias parameters, but SQLModel wasn't automatically propagating the alias value to these parameters.

Solution

Changes Made:

  1. Enhanced Field function:

    • Added validation_alias and serialization_alias parameters
    • Implemented version-specific parameter handling
  2. Automatic alias propagation:

    • Modified post_init_field_info in _compat.py to automatically set validation_alias and serialization_alias from alias when not explicitly provided (Pydantic v2 only) UPD YuriiMotov: updated it to just using alias value for validation_alias and serialization_alias if they are not specified directly
  3. Backward compatibility:

    • Filter out v2-only parameters from FieldInfo in Pydantic v1
    • Raise clear RuntimeError when v2 features are used with v1
    • Conditional test execution based on Pydantic version
  4. Comprehensive testing:

    • Added extensive test coverage for both Pydantic v1 and v2
    • Tests automatically skip v2-specific features when running with v1
    • Covers all alias scenarios including edge cases

Breaking Changes
None. This is purely a bug fix with backward compatibility maintained.

Closes
Fixes#1536

@ravishan16
ravishan16 marked this pull request as ready for review September 25, 2025 23:02
@svlandegsvlandeg added the bug Something isn't working label Sep 26, 2025
@svlandegsvlandeg self-assigned this Sep 26, 2025

@YuriiMotovYuriiMotov left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The idea looks good to me!

A couple of more things:

  • I think it would be nice to use alias as a default value for DB column. It can then be overridden by sa_column=Column("column_name") - decided to handle it in separate PR later
  • We should probably test how it works with alias generators specified in model's config (is it supported? - UPD: YES) (done)

PS: @svlandeg, sorry, I only noticed you self-assigned this PR when I was about to press "Submit review".. Hope you don't mind I submitted this review)

Comment threadsqlmodel/_compat.py Outdated
Comment threadsqlmodel/main.py Outdated
Comment threadtests/test_aliases.py
Comment threadtests/test_aliases.py Outdated
Comment threadtests/test_aliases.py Outdated
Comment threadtests/test_aliases.py
Comment threadsqlmodel/main.py
*,
default_factory: Optional[NoArgAnyCallable] = None,
alias: Optional[str] = None,
validation_alias: Optional[str] = None,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Pydantic's validation_alias type annotation is wider (validation_alias: str | AliasPath | AliasChoices | None), but I think it's fine if we only support str for now and extend it later

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I don't have a strong preference on this.

@svlandeg

Copy link
Copy Markdown
Member

Hope you don't mind I submitted this review

Of course not! I tentatively assign PRs when I want to review them in the near future, just to keep track of them, but sometimes other tasks come in between 🙈 Feel free to review whatever you want!

@ravishan16

Copy link
Copy Markdown
ContributorAuthor

@YuriiMotov@svlandeg I’ve addressed all the comments from the review. Let me know if there’s anything else you’d like me to focus on.

@svlandegsvlandeg removed their assignment Oct 6, 2025
YuriiMotov

This comment was marked as resolved.

@YuriiMotovYuriiMotov mentioned this pull request Oct 8, 2025
1 task
@ravishan16

This comment was marked as resolved.

@ravishan16

This comment was marked as resolved.

@YuriiMotov

Copy link
Copy Markdown
Member

I simplified code a bit - moved alias propagation logic to Field.__init__ so that it would be all in one place.
Also, got rid of deprecation warnings in test.

@YuriiMotovYuriiMotov left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM!

Implementation is not breaking. It also handles the workaround with

Field(schema_extra={"validation_alias": "field_alias"})

that people currently use.

The only use case it can break is if users use

Field(schema_extra={"validation_alias": "field_alias"})

and want to use this model with both, Pydantic V1 and V2.
It will now raise

RuntimeError("validation_alias is not supported in Pydantic v1")

for Pydantic V1.
But it's easily fixable - just remove validation_alias (now alias works with both versions, so that workaround is not needed).

To Sebastian:

Some tests are parameterized to be run with both, SQLModel and Pydantic models, to show that they behave the same way. If you don't like it, it's easy to remove it.

@ravishan16, thank you!

@YuriiMotovYuriiMotov changed the title 🐛 Fix Field alias support for Pydantic v2 with backward compatibility🐛 Fix alias support for Pydantic v2Oct 9, 2025
@xdewx

Copy link
Copy Markdown

Could you please let me know when this PR will be merged and released?

@tiangolotiangolo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Awesome, thank you @ravishan16! 🚀

And thanks @YuriiMotov! 🍰

This will be available in SQLModel 0.0.29, released in the next hours. 🎉

@tiangolo
tiangolo enabled auto-merge (squash) December 23, 2025 20:57
@tiangolo
tiangolo merged commit 5b1d8e6 into fastapi:mainDec 23, 2025
24 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugSomething isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Field(alias="") doesn't work with Pydantic V2

5 participants

@ravishan16@svlandeg@YuriiMotov@xdewx@tiangolo
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

🐛 Fix alias support for Pydantic v2 - #1577

Merged
tiangolo merged 18 commits into
fastapi:mainfrom
ravishan16:issue-1536-alias-pydantic-v2
Dec 23, 2025
Merged

🐛 Fix alias support for Pydantic v2#1577
tiangolo merged 18 commits into
fastapi:mainfrom
ravishan16:issue-1536-alias-pydantic-v2

Conversation

@ravishan16

@ravishan16ravishan16 commented Sep 25, 2025

Copy link
Copy Markdown
Contributor

Description

This PR fixes the Field(alias="...") functionality that was broken in Pydantic v2 while maintaining full backward compatibility with Pydantic v1.

Problem

In Pydantic v2, the alias parameter in SQLModel's Field function was not working correctly for validation and serialization. The issue was that Pydantic v2 requires explicit validation_alias and serialization_alias parameters, but SQLModel wasn't automatically propagating the alias value to these parameters.

Solution

Changes Made:

  1. Enhanced Field function:

    • Added validation_alias and serialization_alias parameters
    • Implemented version-specific parameter handling
  2. Automatic alias propagation:

    • Modified post_init_field_info in _compat.py to automatically set validation_alias and serialization_alias from alias when not explicitly provided (Pydantic v2 only) UPD YuriiMotov: updated it to just using alias value for validation_alias and serialization_alias if they are not specified directly
  3. Backward compatibility:

    • Filter out v2-only parameters from FieldInfo in Pydantic v1
    • Raise clear RuntimeError when v2 features are used with v1
    • Conditional test execution based on Pydantic version
  4. Comprehensive testing:

    • Added extensive test coverage for both Pydantic v1 and v2
    • Tests automatically skip v2-specific features when running with v1
    • Covers all alias scenarios including edge cases

Breaking Changes
None. This is purely a bug fix with backward compatibility maintained.

Closes
Fixes#1536

@ravishan16
ravishan16 marked this pull request as ready for review September 25, 2025 23:02
@svlandegsvlandeg added the bug Something isn't working label Sep 26, 2025
@svlandegsvlandeg self-assigned this Sep 26, 2025

@YuriiMotovYuriiMotov left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The idea looks good to me!

A couple of more things:

  • I think it would be nice to use alias as a default value for DB column. It can then be overridden by sa_column=Column("column_name") - decided to handle it in separate PR later
  • We should probably test how it works with alias generators specified in model's config (is it supported? - UPD: YES) (done)

PS: @svlandeg, sorry, I only noticed you self-assigned this PR when I was about to press "Submit review".. Hope you don't mind I submitted this review)

Comment threadsqlmodel/_compat.py Outdated
Comment threadsqlmodel/main.py Outdated
Comment threadtests/test_aliases.py
Comment threadtests/test_aliases.py Outdated
Comment threadtests/test_aliases.py Outdated
Comment threadtests/test_aliases.py
Comment threadsqlmodel/main.py
*,
default_factory: Optional[NoArgAnyCallable] = None,
alias: Optional[str] = None,
validation_alias: Optional[str] = None,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Pydantic's validation_alias type annotation is wider (validation_alias: str | AliasPath | AliasChoices | None), but I think it's fine if we only support str for now and extend it later

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I don't have a strong preference on this.

@svlandeg

Copy link
Copy Markdown
Member

Hope you don't mind I submitted this review

Of course not! I tentatively assign PRs when I want to review them in the near future, just to keep track of them, but sometimes other tasks come in between 🙈 Feel free to review whatever you want!

@ravishan16

Copy link
Copy Markdown
ContributorAuthor

@YuriiMotov@svlandeg I’ve addressed all the comments from the review. Let me know if there’s anything else you’d like me to focus on.

@svlandegsvlandeg removed their assignment Oct 6, 2025
YuriiMotov

This comment was marked as resolved.

@YuriiMotovYuriiMotov mentioned this pull request Oct 8, 2025
1 task
@ravishan16

This comment was marked as resolved.

@ravishan16

This comment was marked as resolved.

@YuriiMotov

Copy link
Copy Markdown
Member

I simplified code a bit - moved alias propagation logic to Field.__init__ so that it would be all in one place.
Also, got rid of deprecation warnings in test.

@YuriiMotovYuriiMotov left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM!

Implementation is not breaking. It also handles the workaround with

Field(schema_extra={"validation_alias": "field_alias"})

that people currently use.

The only use case it can break is if users use

Field(schema_extra={"validation_alias": "field_alias"})

and want to use this model with both, Pydantic V1 and V2.
It will now raise

RuntimeError("validation_alias is not supported in Pydantic v1")

for Pydantic V1.
But it's easily fixable - just remove validation_alias (now alias works with both versions, so that workaround is not needed).

To Sebastian:

Some tests are parameterized to be run with both, SQLModel and Pydantic models, to show that they behave the same way. If you don't like it, it's easy to remove it.

@ravishan16, thank you!

@YuriiMotovYuriiMotov changed the title 🐛 Fix Field alias support for Pydantic v2 with backward compatibility🐛 Fix alias support for Pydantic v2Oct 9, 2025
@xdewx

Copy link
Copy Markdown

Could you please let me know when this PR will be merged and released?

@tiangolotiangolo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Awesome, thank you @ravishan16! 🚀

And thanks @YuriiMotov! 🍰

This will be available in SQLModel 0.0.29, released in the next hours. 🎉

@tiangolo
tiangolo enabled auto-merge (squash) December 23, 2025 20:57
@tiangolo
tiangolo merged commit 5b1d8e6 into fastapi:mainDec 23, 2025
24 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugSomething isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Field(alias="") doesn't work with Pydantic V2

5 participants

@ravishan16@svlandeg@YuriiMotov@xdewx@tiangolo
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

🐛 Fix alias support for Pydantic v2 - #1577

Merged
tiangolo merged 18 commits into
fastapi:mainfrom
ravishan16:issue-1536-alias-pydantic-v2
Dec 23, 2025
Merged

🐛 Fix alias support for Pydantic v2#1577
tiangolo merged 18 commits into
fastapi:mainfrom
ravishan16:issue-1536-alias-pydantic-v2

Conversation

@ravishan16

@ravishan16ravishan16 commented Sep 25, 2025

Copy link
Copy Markdown
Contributor

Description

This PR fixes the Field(alias="...") functionality that was broken in Pydantic v2 while maintaining full backward compatibility with Pydantic v1.

Problem

In Pydantic v2, the alias parameter in SQLModel's Field function was not working correctly for validation and serialization. The issue was that Pydantic v2 requires explicit validation_alias and serialization_alias parameters, but SQLModel wasn't automatically propagating the alias value to these parameters.

Solution

Changes Made:

  1. Enhanced Field function:

    • Added validation_alias and serialization_alias parameters
    • Implemented version-specific parameter handling
  2. Automatic alias propagation:

    • Modified post_init_field_info in _compat.py to automatically set validation_alias and serialization_alias from alias when not explicitly provided (Pydantic v2 only) UPD YuriiMotov: updated it to just using alias value for validation_alias and serialization_alias if they are not specified directly
  3. Backward compatibility:

    • Filter out v2-only parameters from FieldInfo in Pydantic v1
    • Raise clear RuntimeError when v2 features are used with v1
    • Conditional test execution based on Pydantic version
  4. Comprehensive testing:

    • Added extensive test coverage for both Pydantic v1 and v2
    • Tests automatically skip v2-specific features when running with v1
    • Covers all alias scenarios including edge cases

Breaking Changes
None. This is purely a bug fix with backward compatibility maintained.

Closes
Fixes#1536

@ravishan16
ravishan16 marked this pull request as ready for review September 25, 2025 23:02
@svlandegsvlandeg added the bug Something isn't working label Sep 26, 2025
@svlandegsvlandeg self-assigned this Sep 26, 2025

@YuriiMotovYuriiMotov left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The idea looks good to me!

A couple of more things:

  • I think it would be nice to use alias as a default value for DB column. It can then be overridden by sa_column=Column("column_name") - decided to handle it in separate PR later
  • We should probably test how it works with alias generators specified in model's config (is it supported? - UPD: YES) (done)

PS: @svlandeg, sorry, I only noticed you self-assigned this PR when I was about to press "Submit review".. Hope you don't mind I submitted this review)

Comment threadsqlmodel/_compat.py Outdated
Comment threadsqlmodel/main.py Outdated
Comment threadtests/test_aliases.py
Comment threadtests/test_aliases.py Outdated
Comment threadtests/test_aliases.py Outdated
Comment threadtests/test_aliases.py
Comment threadsqlmodel/main.py
*,
default_factory: Optional[NoArgAnyCallable] = None,
alias: Optional[str] = None,
validation_alias: Optional[str] = None,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Pydantic's validation_alias type annotation is wider (validation_alias: str | AliasPath | AliasChoices | None), but I think it's fine if we only support str for now and extend it later

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I don't have a strong preference on this.

@svlandeg

Copy link
Copy Markdown
Member

Hope you don't mind I submitted this review

Of course not! I tentatively assign PRs when I want to review them in the near future, just to keep track of them, but sometimes other tasks come in between 🙈 Feel free to review whatever you want!

@ravishan16

Copy link
Copy Markdown
ContributorAuthor

@YuriiMotov@svlandeg I’ve addressed all the comments from the review. Let me know if there’s anything else you’d like me to focus on.

@svlandegsvlandeg removed their assignment Oct 6, 2025
YuriiMotov

This comment was marked as resolved.

@YuriiMotovYuriiMotov mentioned this pull request Oct 8, 2025
1 task
@ravishan16

This comment was marked as resolved.

@ravishan16

This comment was marked as resolved.

@YuriiMotov

Copy link
Copy Markdown
Member

I simplified code a bit - moved alias propagation logic to Field.__init__ so that it would be all in one place.
Also, got rid of deprecation warnings in test.

@YuriiMotovYuriiMotov left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM!

Implementation is not breaking. It also handles the workaround with

Field(schema_extra={"validation_alias": "field_alias"})

that people currently use.

The only use case it can break is if users use

Field(schema_extra={"validation_alias": "field_alias"})

and want to use this model with both, Pydantic V1 and V2.
It will now raise

RuntimeError("validation_alias is not supported in Pydantic v1")

for Pydantic V1.
But it's easily fixable - just remove validation_alias (now alias works with both versions, so that workaround is not needed).

To Sebastian:

Some tests are parameterized to be run with both, SQLModel and Pydantic models, to show that they behave the same way. If you don't like it, it's easy to remove it.

@ravishan16, thank you!

@YuriiMotovYuriiMotov changed the title 🐛 Fix Field alias support for Pydantic v2 with backward compatibility🐛 Fix alias support for Pydantic v2Oct 9, 2025
@xdewx

Copy link
Copy Markdown

Could you please let me know when this PR will be merged and released?

@tiangolotiangolo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Awesome, thank you @ravishan16! 🚀

And thanks @YuriiMotov! 🍰

This will be available in SQLModel 0.0.29, released in the next hours. 🎉

@tiangolo
tiangolo enabled auto-merge (squash) December 23, 2025 20:57
@tiangolo
tiangolo merged commit 5b1d8e6 into fastapi:mainDec 23, 2025
24 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugSomething isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Field(alias="") doesn't work with Pydantic V2

5 participants

@ravishan16@svlandeg@YuriiMotov@xdewx@tiangolo
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

🐛 Fix alias support for Pydantic v2 - #1577

Merged
tiangolo merged 18 commits into
fastapi:mainfrom
ravishan16:issue-1536-alias-pydantic-v2
Dec 23, 2025
Merged

🐛 Fix alias support for Pydantic v2#1577
tiangolo merged 18 commits into
fastapi:mainfrom
ravishan16:issue-1536-alias-pydantic-v2

Conversation

@ravishan16

@ravishan16ravishan16 commented Sep 25, 2025

Copy link
Copy Markdown
Contributor

Description

This PR fixes the Field(alias="...") functionality that was broken in Pydantic v2 while maintaining full backward compatibility with Pydantic v1.

Problem

In Pydantic v2, the alias parameter in SQLModel's Field function was not working correctly for validation and serialization. The issue was that Pydantic v2 requires explicit validation_alias and serialization_alias parameters, but SQLModel wasn't automatically propagating the alias value to these parameters.

Solution

Changes Made:

  1. Enhanced Field function:

    • Added validation_alias and serialization_alias parameters
    • Implemented version-specific parameter handling
  2. Automatic alias propagation:

    • Modified post_init_field_info in _compat.py to automatically set validation_alias and serialization_alias from alias when not explicitly provided (Pydantic v2 only) UPD YuriiMotov: updated it to just using alias value for validation_alias and serialization_alias if they are not specified directly
  3. Backward compatibility:

    • Filter out v2-only parameters from FieldInfo in Pydantic v1
    • Raise clear RuntimeError when v2 features are used with v1
    • Conditional test execution based on Pydantic version
  4. Comprehensive testing:

    • Added extensive test coverage for both Pydantic v1 and v2
    • Tests automatically skip v2-specific features when running with v1
    • Covers all alias scenarios including edge cases

Breaking Changes
None. This is purely a bug fix with backward compatibility maintained.

Closes
Fixes#1536

@ravishan16
ravishan16 marked this pull request as ready for review September 25, 2025 23:02
@svlandegsvlandeg added the bug Something isn't working label Sep 26, 2025
@svlandegsvlandeg self-assigned this Sep 26, 2025

@YuriiMotovYuriiMotov left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The idea looks good to me!

A couple of more things:

  • I think it would be nice to use alias as a default value for DB column. It can then be overridden by sa_column=Column("column_name") - decided to handle it in separate PR later
  • We should probably test how it works with alias generators specified in model's config (is it supported? - UPD: YES) (done)

PS: @svlandeg, sorry, I only noticed you self-assigned this PR when I was about to press "Submit review".. Hope you don't mind I submitted this review)

Comment threadsqlmodel/_compat.py Outdated
Comment threadsqlmodel/main.py Outdated
Comment threadtests/test_aliases.py
Comment threadtests/test_aliases.py Outdated
Comment threadtests/test_aliases.py Outdated
Comment threadtests/test_aliases.py
Comment threadsqlmodel/main.py
*,
default_factory: Optional[NoArgAnyCallable] = None,
alias: Optional[str] = None,
validation_alias: Optional[str] = None,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Pydantic's validation_alias type annotation is wider (validation_alias: str | AliasPath | AliasChoices | None), but I think it's fine if we only support str for now and extend it later

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I don't have a strong preference on this.

@svlandeg

Copy link
Copy Markdown
Member

Hope you don't mind I submitted this review

Of course not! I tentatively assign PRs when I want to review them in the near future, just to keep track of them, but sometimes other tasks come in between 🙈 Feel free to review whatever you want!

@ravishan16

Copy link
Copy Markdown
ContributorAuthor

@YuriiMotov@svlandeg I’ve addressed all the comments from the review. Let me know if there’s anything else you’d like me to focus on.

@svlandegsvlandeg removed their assignment Oct 6, 2025
YuriiMotov

This comment was marked as resolved.

@YuriiMotovYuriiMotov mentioned this pull request Oct 8, 2025
1 task
@ravishan16

This comment was marked as resolved.

@ravishan16

This comment was marked as resolved.

@YuriiMotov

Copy link
Copy Markdown
Member

I simplified code a bit - moved alias propagation logic to Field.__init__ so that it would be all in one place.
Also, got rid of deprecation warnings in test.

@YuriiMotovYuriiMotov left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM!

Implementation is not breaking. It also handles the workaround with

Field(schema_extra={"validation_alias": "field_alias"})

that people currently use.

The only use case it can break is if users use

Field(schema_extra={"validation_alias": "field_alias"})

and want to use this model with both, Pydantic V1 and V2.
It will now raise

RuntimeError("validation_alias is not supported in Pydantic v1")

for Pydantic V1.
But it's easily fixable - just remove validation_alias (now alias works with both versions, so that workaround is not needed).

To Sebastian:

Some tests are parameterized to be run with both, SQLModel and Pydantic models, to show that they behave the same way. If you don't like it, it's easy to remove it.

@ravishan16, thank you!

@YuriiMotovYuriiMotov changed the title 🐛 Fix Field alias support for Pydantic v2 with backward compatibility🐛 Fix alias support for Pydantic v2Oct 9, 2025
@xdewx

Copy link
Copy Markdown

Could you please let me know when this PR will be merged and released?

@tiangolotiangolo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Awesome, thank you @ravishan16! 🚀

And thanks @YuriiMotov! 🍰

This will be available in SQLModel 0.0.29, released in the next hours. 🎉

@tiangolo
tiangolo enabled auto-merge (squash) December 23, 2025 20:57
@tiangolo
tiangolo merged commit 5b1d8e6 into fastapi:mainDec 23, 2025
24 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugSomething isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Field(alias="") doesn't work with Pydantic V2

5 participants

@ravishan16@svlandeg@YuriiMotov@xdewx@tiangolo
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

🐛 Fix alias support for Pydantic v2 - #1577

Merged
tiangolo merged 18 commits into
fastapi:mainfrom
ravishan16:issue-1536-alias-pydantic-v2
Dec 23, 2025
Merged

🐛 Fix alias support for Pydantic v2#1577
tiangolo merged 18 commits into
fastapi:mainfrom
ravishan16:issue-1536-alias-pydantic-v2

Conversation

@ravishan16

@ravishan16ravishan16 commented Sep 25, 2025

Copy link
Copy Markdown
Contributor

Description

This PR fixes the Field(alias="...") functionality that was broken in Pydantic v2 while maintaining full backward compatibility with Pydantic v1.

Problem

In Pydantic v2, the alias parameter in SQLModel's Field function was not working correctly for validation and serialization. The issue was that Pydantic v2 requires explicit validation_alias and serialization_alias parameters, but SQLModel wasn't automatically propagating the alias value to these parameters.

Solution

Changes Made:

  1. Enhanced Field function:

    • Added validation_alias and serialization_alias parameters
    • Implemented version-specific parameter handling
  2. Automatic alias propagation:

    • Modified post_init_field_info in _compat.py to automatically set validation_alias and serialization_alias from alias when not explicitly provided (Pydantic v2 only) UPD YuriiMotov: updated it to just using alias value for validation_alias and serialization_alias if they are not specified directly
  3. Backward compatibility:

    • Filter out v2-only parameters from FieldInfo in Pydantic v1
    • Raise clear RuntimeError when v2 features are used with v1
    • Conditional test execution based on Pydantic version
  4. Comprehensive testing:

    • Added extensive test coverage for both Pydantic v1 and v2
    • Tests automatically skip v2-specific features when running with v1
    • Covers all alias scenarios including edge cases

Breaking Changes
None. This is purely a bug fix with backward compatibility maintained.

Closes
Fixes#1536

@ravishan16
ravishan16 marked this pull request as ready for review September 25, 2025 23:02
@svlandegsvlandeg added the bug Something isn't working label Sep 26, 2025
@svlandegsvlandeg self-assigned this Sep 26, 2025

@YuriiMotovYuriiMotov left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The idea looks good to me!

A couple of more things:

  • I think it would be nice to use alias as a default value for DB column. It can then be overridden by sa_column=Column("column_name") - decided to handle it in separate PR later
  • We should probably test how it works with alias generators specified in model's config (is it supported? - UPD: YES) (done)

PS: @svlandeg, sorry, I only noticed you self-assigned this PR when I was about to press "Submit review".. Hope you don't mind I submitted this review)

Comment threadsqlmodel/_compat.py Outdated
Comment threadsqlmodel/main.py Outdated
Comment threadtests/test_aliases.py
Comment threadtests/test_aliases.py Outdated
Comment threadtests/test_aliases.py Outdated
Comment threadtests/test_aliases.py
Comment threadsqlmodel/main.py
*,
default_factory: Optional[NoArgAnyCallable] = None,
alias: Optional[str] = None,
validation_alias: Optional[str] = None,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Pydantic's validation_alias type annotation is wider (validation_alias: str | AliasPath | AliasChoices | None), but I think it's fine if we only support str for now and extend it later

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I don't have a strong preference on this.

@svlandeg

Copy link
Copy Markdown
Member

Hope you don't mind I submitted this review

Of course not! I tentatively assign PRs when I want to review them in the near future, just to keep track of them, but sometimes other tasks come in between 🙈 Feel free to review whatever you want!

@ravishan16

Copy link
Copy Markdown
ContributorAuthor

@YuriiMotov@svlandeg I’ve addressed all the comments from the review. Let me know if there’s anything else you’d like me to focus on.

@svlandegsvlandeg removed their assignment Oct 6, 2025
YuriiMotov

This comment was marked as resolved.

@YuriiMotovYuriiMotov mentioned this pull request Oct 8, 2025
1 task
@ravishan16

This comment was marked as resolved.

@ravishan16

This comment was marked as resolved.

@YuriiMotov

Copy link
Copy Markdown
Member

I simplified code a bit - moved alias propagation logic to Field.__init__ so that it would be all in one place.
Also, got rid of deprecation warnings in test.

@YuriiMotovYuriiMotov left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM!

Implementation is not breaking. It also handles the workaround with

Field(schema_extra={"validation_alias": "field_alias"})

that people currently use.

The only use case it can break is if users use

Field(schema_extra={"validation_alias": "field_alias"})

and want to use this model with both, Pydantic V1 and V2.
It will now raise

RuntimeError("validation_alias is not supported in Pydantic v1")

for Pydantic V1.
But it's easily fixable - just remove validation_alias (now alias works with both versions, so that workaround is not needed).

To Sebastian:

Some tests are parameterized to be run with both, SQLModel and Pydantic models, to show that they behave the same way. If you don't like it, it's easy to remove it.

@ravishan16, thank you!

@YuriiMotovYuriiMotov changed the title 🐛 Fix Field alias support for Pydantic v2 with backward compatibility🐛 Fix alias support for Pydantic v2Oct 9, 2025
@xdewx

Copy link
Copy Markdown

Could you please let me know when this PR will be merged and released?

@tiangolotiangolo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Awesome, thank you @ravishan16! 🚀

And thanks @YuriiMotov! 🍰

This will be available in SQLModel 0.0.29, released in the next hours. 🎉

@tiangolo
tiangolo enabled auto-merge (squash) December 23, 2025 20:57
@tiangolo
tiangolo merged commit 5b1d8e6 into fastapi:mainDec 23, 2025
24 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugSomething isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Field(alias="") doesn't work with Pydantic V2

5 participants

@ravishan16@svlandeg@YuriiMotov@xdewx@tiangolo