🏷️ Adjust type annotation for Field.sa_type to support instantiated SQLAlchemy types - #1345

Open
diachkow wants to merge 14 commits into
fastapi:mainfrom
diachkow:main
Open

🏷️ Adjust type annotation for Field.sa_type to support instantiated SQLAlchemy types#1345
diachkow wants to merge 14 commits into
fastapi:mainfrom
diachkow:main

Conversation

@diachkow

@diachkowdiachkow commented Apr 21, 2025

Copy link
Copy Markdown

Note

When I was writing description for this PR, I found another discussion started for just the same issue I was experiencing with mypy, so this changes are basically fixing the issue described here

Using sa_type and sa_column_kwargs instead of just sa_column can benefit when using inheritance for classes derived from SQLModel as it was suggested here.

If sa_column is not specified and sa_type is provided, it will be passed as a second, type argumnet to sqlalchemy.Column instance. If you would check sqlalchemy.Column construction definition, it looks as following:

def__init__(
self,
__name_pos: Optional[
Union[str, _TypeEngineArgument[_T], SchemaEventTarget]
] =None,
__type_pos: Optional[
Union[_TypeEngineArgument[_T], SchemaEventTarget]
] =None,
*args: SchemaEventTarget,
name: Optional[str] =None,
type_: Optional[_TypeEngineArgument[_T]] =None,
autoincrement: _AutoIncrementType="auto",
default: Optional[Any] =_NoArg.NO_ARG,
insert_default: Optional[Any] =_NoArg.NO_ARG,
doc: Optional[str] =None,
key: Optional[str] =None,
index: Optional[bool] =None,
unique: Optional[bool] =None,
info: Optional[_InfoType] =None,
nullable: Optional[
Union[bool, Literal[SchemaConst.NULL_UNSPECIFIED]]
] =SchemaConst.NULL_UNSPECIFIED,
onupdate: Optional[Any] =None,
primary_key: bool=False,
server_default: Optional[_ServerDefaultArgument] =None,
server_onupdate: Optional[_ServerOnUpdateArgument] =None,
quote: Optional[bool] =None,
system: bool=False,
comment: Optional[str] =None,
insert_sentinel: bool=False,
_omit_from_statements: bool=False,
_proxies: Optional[Any] =None,
**dialect_kwargs: Any,
):

Note the __type_pos argument with Union[_TypeEngineArgument[_T], SchemaEventTarget] where

_TypeEngineArgument=Union[Type["TypeEngine[_T]"], "TypeEngine[_T]"]

So, from technical perspective you can pass not only the subclass of TypeEngine, e.g. SQLAlchemy's sqltype such as String, Integer, DateTime, JSON etc, but also an instance of this type.

I was trying for JSONB(none_as_null=True) and String(50) and it worked just fine, alembic migrations were generated correctly, only mypy was arguing for type mismatch with call-overload issue.

To fix mypy error, we can update type annotation for sqlmodel.main.Field.sa_type to support also an instantiated SQLAlchemy's sqltype to match those of sqlalchemy.Column

Related discussions:

@diachkow

This comment was marked as resolved.

@svlandegsvlandeg added the feature New feature or request label Apr 22, 2025
@svlandeg

This comment was marked as resolved.

@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

Simple code example to check (in the details)

Details
fromdatetimeimportdatetimefromsqlmodelimportDateTime, Field, SQLModel, create_engineclassA(SQLModel):
created_at: datetime=Field(sa_type=DateTime(timezone=False))
engine=create_engine("sqlite:///")
SQLModel.metadata.create_all(engine)

Running mypy gives

error: No overload variant of "Field" matches argument type "DateTime" [call-overload]

on master and

Success: no issues found in 1 source file

after applying this fix

Comment threadsqlmodel/main.py Outdated
@YuriiMotovYuriiMotov changed the title 🏷️ Adjust type annotation for Field.sa_type to support instantiated SQLAlchemy types🏷️ Adjust type annotation for Field.sa_type to support instantiated SQLAlchemy typesAug 21, 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.

@diachkow

Copy link
Copy Markdown
Author

Thanks for approval! Who is responsible for merging this, what are the rules in this repo?

@YuriiMotov

Copy link
Copy Markdown
Member

Thanks for approval! Who is responsible for merging this, what are the rules in this repo?

Only Sebastian can merge it. I already forwarded it to him. We should just wait

@github-actionsgithub-actionsBot added the conflicts Automatically generated when a PR has a merge conflict label Dec 26, 2025
@github-actions

This comment was marked as resolved.

@github-actionsgithub-actionsBot removed the conflicts Automatically generated when a PR has a merge conflict label Dec 27, 2025
@diachkow

Copy link
Copy Markdown
Author

I have resolved conflicts here. I see you guys made some patch releases recently, hope this change can make it into one of them 🙃

@github-actions

This comment was marked as resolved.

@github-actionsgithub-actionsBot added the conflicts Automatically generated when a PR has a merge conflict label Jan 13, 2026
@github-actionsgithub-actionsBot removed the conflicts Automatically generated when a PR has a merge conflict label Jan 13, 2026
@github-actionsgithub-actionsBot added the conflicts Automatically generated when a PR has a merge conflict label Feb 20, 2026
@github-actions

This comment was marked as resolved.

@github-actionsgithub-actionsBot removed the conflicts Automatically generated when a PR has a merge conflict label Mar 3, 2026
@github-actions

This comment was marked as resolved.

@github-actionsgithub-actionsBot removed the conflicts Automatically generated when a PR has a merge conflict label Apr 3, 2026
@AgamjotSB

Copy link
Copy Markdown

was hitting this error when using sa_type, thanks for keeping it updated

@diachkow

Copy link
Copy Markdown
Author

It would be nice to get it merged though 😅

@AgamjotSB

Copy link
Copy Markdown

Hey @tiangolo, gently bumping this. It's been open about a year now, has an LGTM from @YuriiMotov, and @svlandeg mentioned back in September she'd forwarded it to you for merging.

To recap the issue:

  • Runtime is fine: sa_type already works correctly at runtime (SQLAlchemy accepts instantiated types like DateTime(timezone=False) just fine).
  • Typing is failing: The type annotation on Field.sa_type doesn't reflect that, so type checkers (basedpyright, zuban, mypy) flag valid code as an error.
  • Impact: It doesn't break anything at runtime, but it causes CI pipelines to fail.

Worth noting this is really a bug fix, not a feature. It's currently labeled as feature, but the code already does the right thing, this PR just corrects the type signature to match. Might be worth relabeling!

Totally understand if it's just buried under everything else, I didn't want it to get lost. Thanks for all the incredible work you do on FastAPI and SQLModel (and friends) :)

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

Labels

featureNew feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@diachkow@svlandeg@YuriiMotov@AgamjotSB@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

🏷️ Adjust type annotation for Field.sa_type to support instantiated SQLAlchemy types - #1345

Open
diachkow wants to merge 14 commits into
fastapi:mainfrom
diachkow:main
Open

🏷️ Adjust type annotation for Field.sa_type to support instantiated SQLAlchemy types#1345
diachkow wants to merge 14 commits into
fastapi:mainfrom
diachkow:main

Conversation

@diachkow

@diachkowdiachkow commented Apr 21, 2025

Copy link
Copy Markdown

Note

When I was writing description for this PR, I found another discussion started for just the same issue I was experiencing with mypy, so this changes are basically fixing the issue described here

Using sa_type and sa_column_kwargs instead of just sa_column can benefit when using inheritance for classes derived from SQLModel as it was suggested here.

If sa_column is not specified and sa_type is provided, it will be passed as a second, type argumnet to sqlalchemy.Column instance. If you would check sqlalchemy.Column construction definition, it looks as following:

def__init__(
self,
__name_pos: Optional[
Union[str, _TypeEngineArgument[_T], SchemaEventTarget]
] =None,
__type_pos: Optional[
Union[_TypeEngineArgument[_T], SchemaEventTarget]
] =None,
*args: SchemaEventTarget,
name: Optional[str] =None,
type_: Optional[_TypeEngineArgument[_T]] =None,
autoincrement: _AutoIncrementType="auto",
default: Optional[Any] =_NoArg.NO_ARG,
insert_default: Optional[Any] =_NoArg.NO_ARG,
doc: Optional[str] =None,
key: Optional[str] =None,
index: Optional[bool] =None,
unique: Optional[bool] =None,
info: Optional[_InfoType] =None,
nullable: Optional[
Union[bool, Literal[SchemaConst.NULL_UNSPECIFIED]]
] =SchemaConst.NULL_UNSPECIFIED,
onupdate: Optional[Any] =None,
primary_key: bool=False,
server_default: Optional[_ServerDefaultArgument] =None,
server_onupdate: Optional[_ServerOnUpdateArgument] =None,
quote: Optional[bool] =None,
system: bool=False,
comment: Optional[str] =None,
insert_sentinel: bool=False,
_omit_from_statements: bool=False,
_proxies: Optional[Any] =None,
**dialect_kwargs: Any,
):

Note the __type_pos argument with Union[_TypeEngineArgument[_T], SchemaEventTarget] where

_TypeEngineArgument=Union[Type["TypeEngine[_T]"], "TypeEngine[_T]"]

So, from technical perspective you can pass not only the subclass of TypeEngine, e.g. SQLAlchemy's sqltype such as String, Integer, DateTime, JSON etc, but also an instance of this type.

I was trying for JSONB(none_as_null=True) and String(50) and it worked just fine, alembic migrations were generated correctly, only mypy was arguing for type mismatch with call-overload issue.

To fix mypy error, we can update type annotation for sqlmodel.main.Field.sa_type to support also an instantiated SQLAlchemy's sqltype to match those of sqlalchemy.Column

Related discussions:

@diachkow

This comment was marked as resolved.

@svlandegsvlandeg added the feature New feature or request label Apr 22, 2025
@svlandeg

This comment was marked as resolved.

@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

Simple code example to check (in the details)

Details
fromdatetimeimportdatetimefromsqlmodelimportDateTime, Field, SQLModel, create_engineclassA(SQLModel):
created_at: datetime=Field(sa_type=DateTime(timezone=False))
engine=create_engine("sqlite:///")
SQLModel.metadata.create_all(engine)

Running mypy gives

error: No overload variant of "Field" matches argument type "DateTime" [call-overload]

on master and

Success: no issues found in 1 source file

after applying this fix

Comment threadsqlmodel/main.py Outdated
@YuriiMotovYuriiMotov changed the title 🏷️ Adjust type annotation for Field.sa_type to support instantiated SQLAlchemy types🏷️ Adjust type annotation for Field.sa_type to support instantiated SQLAlchemy typesAug 21, 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.

@diachkow

Copy link
Copy Markdown
Author

Thanks for approval! Who is responsible for merging this, what are the rules in this repo?

@YuriiMotov

Copy link
Copy Markdown
Member

Thanks for approval! Who is responsible for merging this, what are the rules in this repo?

Only Sebastian can merge it. I already forwarded it to him. We should just wait

@github-actionsgithub-actionsBot added the conflicts Automatically generated when a PR has a merge conflict label Dec 26, 2025
@github-actions

This comment was marked as resolved.

@github-actionsgithub-actionsBot removed the conflicts Automatically generated when a PR has a merge conflict label Dec 27, 2025
@diachkow

Copy link
Copy Markdown
Author

I have resolved conflicts here. I see you guys made some patch releases recently, hope this change can make it into one of them 🙃

@github-actions

This comment was marked as resolved.

@github-actionsgithub-actionsBot added the conflicts Automatically generated when a PR has a merge conflict label Jan 13, 2026
@github-actionsgithub-actionsBot removed the conflicts Automatically generated when a PR has a merge conflict label Jan 13, 2026
@github-actionsgithub-actionsBot added the conflicts Automatically generated when a PR has a merge conflict label Feb 20, 2026
@github-actions

This comment was marked as resolved.

@github-actionsgithub-actionsBot removed the conflicts Automatically generated when a PR has a merge conflict label Mar 3, 2026
@github-actions

This comment was marked as resolved.

@github-actionsgithub-actionsBot removed the conflicts Automatically generated when a PR has a merge conflict label Apr 3, 2026
@AgamjotSB

Copy link
Copy Markdown

was hitting this error when using sa_type, thanks for keeping it updated

@diachkow

Copy link
Copy Markdown
Author

It would be nice to get it merged though 😅

@AgamjotSB

Copy link
Copy Markdown

Hey @tiangolo, gently bumping this. It's been open about a year now, has an LGTM from @YuriiMotov, and @svlandeg mentioned back in September she'd forwarded it to you for merging.

To recap the issue:

  • Runtime is fine: sa_type already works correctly at runtime (SQLAlchemy accepts instantiated types like DateTime(timezone=False) just fine).
  • Typing is failing: The type annotation on Field.sa_type doesn't reflect that, so type checkers (basedpyright, zuban, mypy) flag valid code as an error.
  • Impact: It doesn't break anything at runtime, but it causes CI pipelines to fail.

Worth noting this is really a bug fix, not a feature. It's currently labeled as feature, but the code already does the right thing, this PR just corrects the type signature to match. Might be worth relabeling!

Totally understand if it's just buried under everything else, I didn't want it to get lost. Thanks for all the incredible work you do on FastAPI and SQLModel (and friends) :)

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

Labels

featureNew feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@diachkow@svlandeg@YuriiMotov@AgamjotSB@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

🏷️ Adjust type annotation for Field.sa_type to support instantiated SQLAlchemy types - #1345

Open
diachkow wants to merge 14 commits into
fastapi:mainfrom
diachkow:main
Open

🏷️ Adjust type annotation for Field.sa_type to support instantiated SQLAlchemy types#1345
diachkow wants to merge 14 commits into
fastapi:mainfrom
diachkow:main

Conversation

@diachkow

@diachkowdiachkow commented Apr 21, 2025

Copy link
Copy Markdown

Note

When I was writing description for this PR, I found another discussion started for just the same issue I was experiencing with mypy, so this changes are basically fixing the issue described here

Using sa_type and sa_column_kwargs instead of just sa_column can benefit when using inheritance for classes derived from SQLModel as it was suggested here.

If sa_column is not specified and sa_type is provided, it will be passed as a second, type argumnet to sqlalchemy.Column instance. If you would check sqlalchemy.Column construction definition, it looks as following:

def__init__(
self,
__name_pos: Optional[
Union[str, _TypeEngineArgument[_T], SchemaEventTarget]
] =None,
__type_pos: Optional[
Union[_TypeEngineArgument[_T], SchemaEventTarget]
] =None,
*args: SchemaEventTarget,
name: Optional[str] =None,
type_: Optional[_TypeEngineArgument[_T]] =None,
autoincrement: _AutoIncrementType="auto",
default: Optional[Any] =_NoArg.NO_ARG,
insert_default: Optional[Any] =_NoArg.NO_ARG,
doc: Optional[str] =None,
key: Optional[str] =None,
index: Optional[bool] =None,
unique: Optional[bool] =None,
info: Optional[_InfoType] =None,
nullable: Optional[
Union[bool, Literal[SchemaConst.NULL_UNSPECIFIED]]
] =SchemaConst.NULL_UNSPECIFIED,
onupdate: Optional[Any] =None,
primary_key: bool=False,
server_default: Optional[_ServerDefaultArgument] =None,
server_onupdate: Optional[_ServerOnUpdateArgument] =None,
quote: Optional[bool] =None,
system: bool=False,
comment: Optional[str] =None,
insert_sentinel: bool=False,
_omit_from_statements: bool=False,
_proxies: Optional[Any] =None,
**dialect_kwargs: Any,
):

Note the __type_pos argument with Union[_TypeEngineArgument[_T], SchemaEventTarget] where

_TypeEngineArgument=Union[Type["TypeEngine[_T]"], "TypeEngine[_T]"]

So, from technical perspective you can pass not only the subclass of TypeEngine, e.g. SQLAlchemy's sqltype such as String, Integer, DateTime, JSON etc, but also an instance of this type.

I was trying for JSONB(none_as_null=True) and String(50) and it worked just fine, alembic migrations were generated correctly, only mypy was arguing for type mismatch with call-overload issue.

To fix mypy error, we can update type annotation for sqlmodel.main.Field.sa_type to support also an instantiated SQLAlchemy's sqltype to match those of sqlalchemy.Column

Related discussions:

@diachkow

This comment was marked as resolved.

@svlandegsvlandeg added the feature New feature or request label Apr 22, 2025
@svlandeg

This comment was marked as resolved.

@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

Simple code example to check (in the details)

Details
fromdatetimeimportdatetimefromsqlmodelimportDateTime, Field, SQLModel, create_engineclassA(SQLModel):
created_at: datetime=Field(sa_type=DateTime(timezone=False))
engine=create_engine("sqlite:///")
SQLModel.metadata.create_all(engine)

Running mypy gives

error: No overload variant of "Field" matches argument type "DateTime" [call-overload]

on master and

Success: no issues found in 1 source file

after applying this fix

Comment threadsqlmodel/main.py Outdated
@YuriiMotovYuriiMotov changed the title 🏷️ Adjust type annotation for Field.sa_type to support instantiated SQLAlchemy types🏷️ Adjust type annotation for Field.sa_type to support instantiated SQLAlchemy typesAug 21, 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.

@diachkow

Copy link
Copy Markdown
Author

Thanks for approval! Who is responsible for merging this, what are the rules in this repo?

@YuriiMotov

Copy link
Copy Markdown
Member

Thanks for approval! Who is responsible for merging this, what are the rules in this repo?

Only Sebastian can merge it. I already forwarded it to him. We should just wait

@github-actionsgithub-actionsBot added the conflicts Automatically generated when a PR has a merge conflict label Dec 26, 2025
@github-actions

This comment was marked as resolved.

@github-actionsgithub-actionsBot removed the conflicts Automatically generated when a PR has a merge conflict label Dec 27, 2025
@diachkow

Copy link
Copy Markdown
Author

I have resolved conflicts here. I see you guys made some patch releases recently, hope this change can make it into one of them 🙃

@github-actions

This comment was marked as resolved.

@github-actionsgithub-actionsBot added the conflicts Automatically generated when a PR has a merge conflict label Jan 13, 2026
@github-actionsgithub-actionsBot removed the conflicts Automatically generated when a PR has a merge conflict label Jan 13, 2026
@github-actionsgithub-actionsBot added the conflicts Automatically generated when a PR has a merge conflict label Feb 20, 2026
@github-actions

This comment was marked as resolved.

@github-actionsgithub-actionsBot removed the conflicts Automatically generated when a PR has a merge conflict label Mar 3, 2026
@github-actions

This comment was marked as resolved.

@github-actionsgithub-actionsBot removed the conflicts Automatically generated when a PR has a merge conflict label Apr 3, 2026
@AgamjotSB

Copy link
Copy Markdown

was hitting this error when using sa_type, thanks for keeping it updated

@diachkow

Copy link
Copy Markdown
Author

It would be nice to get it merged though 😅

@AgamjotSB

Copy link
Copy Markdown

Hey @tiangolo, gently bumping this. It's been open about a year now, has an LGTM from @YuriiMotov, and @svlandeg mentioned back in September she'd forwarded it to you for merging.

To recap the issue:

  • Runtime is fine: sa_type already works correctly at runtime (SQLAlchemy accepts instantiated types like DateTime(timezone=False) just fine).
  • Typing is failing: The type annotation on Field.sa_type doesn't reflect that, so type checkers (basedpyright, zuban, mypy) flag valid code as an error.
  • Impact: It doesn't break anything at runtime, but it causes CI pipelines to fail.

Worth noting this is really a bug fix, not a feature. It's currently labeled as feature, but the code already does the right thing, this PR just corrects the type signature to match. Might be worth relabeling!

Totally understand if it's just buried under everything else, I didn't want it to get lost. Thanks for all the incredible work you do on FastAPI and SQLModel (and friends) :)

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

Labels

featureNew feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@diachkow@svlandeg@YuriiMotov@AgamjotSB@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

🏷️ Adjust type annotation for Field.sa_type to support instantiated SQLAlchemy types - #1345

Open
diachkow wants to merge 14 commits into
fastapi:mainfrom
diachkow:main
Open

🏷️ Adjust type annotation for Field.sa_type to support instantiated SQLAlchemy types#1345
diachkow wants to merge 14 commits into
fastapi:mainfrom
diachkow:main

Conversation

@diachkow

@diachkowdiachkow commented Apr 21, 2025

Copy link
Copy Markdown

Note

When I was writing description for this PR, I found another discussion started for just the same issue I was experiencing with mypy, so this changes are basically fixing the issue described here

Using sa_type and sa_column_kwargs instead of just sa_column can benefit when using inheritance for classes derived from SQLModel as it was suggested here.

If sa_column is not specified and sa_type is provided, it will be passed as a second, type argumnet to sqlalchemy.Column instance. If you would check sqlalchemy.Column construction definition, it looks as following:

def__init__(
self,
__name_pos: Optional[
Union[str, _TypeEngineArgument[_T], SchemaEventTarget]
] =None,
__type_pos: Optional[
Union[_TypeEngineArgument[_T], SchemaEventTarget]
] =None,
*args: SchemaEventTarget,
name: Optional[str] =None,
type_: Optional[_TypeEngineArgument[_T]] =None,
autoincrement: _AutoIncrementType="auto",
default: Optional[Any] =_NoArg.NO_ARG,
insert_default: Optional[Any] =_NoArg.NO_ARG,
doc: Optional[str] =None,
key: Optional[str] =None,
index: Optional[bool] =None,
unique: Optional[bool] =None,
info: Optional[_InfoType] =None,
nullable: Optional[
Union[bool, Literal[SchemaConst.NULL_UNSPECIFIED]]
] =SchemaConst.NULL_UNSPECIFIED,
onupdate: Optional[Any] =None,
primary_key: bool=False,
server_default: Optional[_ServerDefaultArgument] =None,
server_onupdate: Optional[_ServerOnUpdateArgument] =None,
quote: Optional[bool] =None,
system: bool=False,
comment: Optional[str] =None,
insert_sentinel: bool=False,
_omit_from_statements: bool=False,
_proxies: Optional[Any] =None,
**dialect_kwargs: Any,
):

Note the __type_pos argument with Union[_TypeEngineArgument[_T], SchemaEventTarget] where

_TypeEngineArgument=Union[Type["TypeEngine[_T]"], "TypeEngine[_T]"]

So, from technical perspective you can pass not only the subclass of TypeEngine, e.g. SQLAlchemy's sqltype such as String, Integer, DateTime, JSON etc, but also an instance of this type.

I was trying for JSONB(none_as_null=True) and String(50) and it worked just fine, alembic migrations were generated correctly, only mypy was arguing for type mismatch with call-overload issue.

To fix mypy error, we can update type annotation for sqlmodel.main.Field.sa_type to support also an instantiated SQLAlchemy's sqltype to match those of sqlalchemy.Column

Related discussions:

@diachkow

This comment was marked as resolved.

@svlandegsvlandeg added the feature New feature or request label Apr 22, 2025
@svlandeg

This comment was marked as resolved.

@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

Simple code example to check (in the details)

Details
fromdatetimeimportdatetimefromsqlmodelimportDateTime, Field, SQLModel, create_engineclassA(SQLModel):
created_at: datetime=Field(sa_type=DateTime(timezone=False))
engine=create_engine("sqlite:///")
SQLModel.metadata.create_all(engine)

Running mypy gives

error: No overload variant of "Field" matches argument type "DateTime" [call-overload]

on master and

Success: no issues found in 1 source file

after applying this fix

Comment threadsqlmodel/main.py Outdated
@YuriiMotovYuriiMotov changed the title 🏷️ Adjust type annotation for Field.sa_type to support instantiated SQLAlchemy types🏷️ Adjust type annotation for Field.sa_type to support instantiated SQLAlchemy typesAug 21, 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.

@diachkow

Copy link
Copy Markdown
Author

Thanks for approval! Who is responsible for merging this, what are the rules in this repo?

@YuriiMotov

Copy link
Copy Markdown
Member

Thanks for approval! Who is responsible for merging this, what are the rules in this repo?

Only Sebastian can merge it. I already forwarded it to him. We should just wait

@github-actionsgithub-actionsBot added the conflicts Automatically generated when a PR has a merge conflict label Dec 26, 2025
@github-actions

This comment was marked as resolved.

@github-actionsgithub-actionsBot removed the conflicts Automatically generated when a PR has a merge conflict label Dec 27, 2025
@diachkow

Copy link
Copy Markdown
Author

I have resolved conflicts here. I see you guys made some patch releases recently, hope this change can make it into one of them 🙃

@github-actions

This comment was marked as resolved.

@github-actionsgithub-actionsBot added the conflicts Automatically generated when a PR has a merge conflict label Jan 13, 2026
@github-actionsgithub-actionsBot removed the conflicts Automatically generated when a PR has a merge conflict label Jan 13, 2026
@github-actionsgithub-actionsBot added the conflicts Automatically generated when a PR has a merge conflict label Feb 20, 2026
@github-actions

This comment was marked as resolved.

@github-actionsgithub-actionsBot removed the conflicts Automatically generated when a PR has a merge conflict label Mar 3, 2026
@github-actions

This comment was marked as resolved.

@github-actionsgithub-actionsBot removed the conflicts Automatically generated when a PR has a merge conflict label Apr 3, 2026
@AgamjotSB

Copy link
Copy Markdown

was hitting this error when using sa_type, thanks for keeping it updated

@diachkow

Copy link
Copy Markdown
Author

It would be nice to get it merged though 😅

@AgamjotSB

Copy link
Copy Markdown

Hey @tiangolo, gently bumping this. It's been open about a year now, has an LGTM from @YuriiMotov, and @svlandeg mentioned back in September she'd forwarded it to you for merging.

To recap the issue:

  • Runtime is fine: sa_type already works correctly at runtime (SQLAlchemy accepts instantiated types like DateTime(timezone=False) just fine).
  • Typing is failing: The type annotation on Field.sa_type doesn't reflect that, so type checkers (basedpyright, zuban, mypy) flag valid code as an error.
  • Impact: It doesn't break anything at runtime, but it causes CI pipelines to fail.

Worth noting this is really a bug fix, not a feature. It's currently labeled as feature, but the code already does the right thing, this PR just corrects the type signature to match. Might be worth relabeling!

Totally understand if it's just buried under everything else, I didn't want it to get lost. Thanks for all the incredible work you do on FastAPI and SQLModel (and friends) :)

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

Labels

featureNew feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@diachkow@svlandeg@YuriiMotov@AgamjotSB@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

🏷️ Adjust type annotation for Field.sa_type to support instantiated SQLAlchemy types - #1345

Open
diachkow wants to merge 14 commits into
fastapi:mainfrom
diachkow:main
Open

🏷️ Adjust type annotation for Field.sa_type to support instantiated SQLAlchemy types#1345
diachkow wants to merge 14 commits into
fastapi:mainfrom
diachkow:main

Conversation

@diachkow

@diachkowdiachkow commented Apr 21, 2025

Copy link
Copy Markdown

Note

When I was writing description for this PR, I found another discussion started for just the same issue I was experiencing with mypy, so this changes are basically fixing the issue described here

Using sa_type and sa_column_kwargs instead of just sa_column can benefit when using inheritance for classes derived from SQLModel as it was suggested here.

If sa_column is not specified and sa_type is provided, it will be passed as a second, type argumnet to sqlalchemy.Column instance. If you would check sqlalchemy.Column construction definition, it looks as following:

def__init__(
self,
__name_pos: Optional[
Union[str, _TypeEngineArgument[_T], SchemaEventTarget]
] =None,
__type_pos: Optional[
Union[_TypeEngineArgument[_T], SchemaEventTarget]
] =None,
*args: SchemaEventTarget,
name: Optional[str] =None,
type_: Optional[_TypeEngineArgument[_T]] =None,
autoincrement: _AutoIncrementType="auto",
default: Optional[Any] =_NoArg.NO_ARG,
insert_default: Optional[Any] =_NoArg.NO_ARG,
doc: Optional[str] =None,
key: Optional[str] =None,
index: Optional[bool] =None,
unique: Optional[bool] =None,
info: Optional[_InfoType] =None,
nullable: Optional[
Union[bool, Literal[SchemaConst.NULL_UNSPECIFIED]]
] =SchemaConst.NULL_UNSPECIFIED,
onupdate: Optional[Any] =None,
primary_key: bool=False,
server_default: Optional[_ServerDefaultArgument] =None,
server_onupdate: Optional[_ServerOnUpdateArgument] =None,
quote: Optional[bool] =None,
system: bool=False,
comment: Optional[str] =None,
insert_sentinel: bool=False,
_omit_from_statements: bool=False,
_proxies: Optional[Any] =None,
**dialect_kwargs: Any,
):

Note the __type_pos argument with Union[_TypeEngineArgument[_T], SchemaEventTarget] where

_TypeEngineArgument=Union[Type["TypeEngine[_T]"], "TypeEngine[_T]"]

So, from technical perspective you can pass not only the subclass of TypeEngine, e.g. SQLAlchemy's sqltype such as String, Integer, DateTime, JSON etc, but also an instance of this type.

I was trying for JSONB(none_as_null=True) and String(50) and it worked just fine, alembic migrations were generated correctly, only mypy was arguing for type mismatch with call-overload issue.

To fix mypy error, we can update type annotation for sqlmodel.main.Field.sa_type to support also an instantiated SQLAlchemy's sqltype to match those of sqlalchemy.Column

Related discussions:

@diachkow

This comment was marked as resolved.

@svlandegsvlandeg added the feature New feature or request label Apr 22, 2025
@svlandeg

This comment was marked as resolved.

@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

Simple code example to check (in the details)

Details
fromdatetimeimportdatetimefromsqlmodelimportDateTime, Field, SQLModel, create_engineclassA(SQLModel):
created_at: datetime=Field(sa_type=DateTime(timezone=False))
engine=create_engine("sqlite:///")
SQLModel.metadata.create_all(engine)

Running mypy gives

error: No overload variant of "Field" matches argument type "DateTime" [call-overload]

on master and

Success: no issues found in 1 source file

after applying this fix

Comment threadsqlmodel/main.py Outdated
@YuriiMotovYuriiMotov changed the title 🏷️ Adjust type annotation for Field.sa_type to support instantiated SQLAlchemy types🏷️ Adjust type annotation for Field.sa_type to support instantiated SQLAlchemy typesAug 21, 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.

@diachkow

Copy link
Copy Markdown
Author

Thanks for approval! Who is responsible for merging this, what are the rules in this repo?

@YuriiMotov

Copy link
Copy Markdown
Member

Thanks for approval! Who is responsible for merging this, what are the rules in this repo?

Only Sebastian can merge it. I already forwarded it to him. We should just wait

@github-actionsgithub-actionsBot added the conflicts Automatically generated when a PR has a merge conflict label Dec 26, 2025
@github-actions

This comment was marked as resolved.

@github-actionsgithub-actionsBot removed the conflicts Automatically generated when a PR has a merge conflict label Dec 27, 2025
@diachkow

Copy link
Copy Markdown
Author

I have resolved conflicts here. I see you guys made some patch releases recently, hope this change can make it into one of them 🙃

@github-actions

This comment was marked as resolved.

@github-actionsgithub-actionsBot added the conflicts Automatically generated when a PR has a merge conflict label Jan 13, 2026
@github-actionsgithub-actionsBot removed the conflicts Automatically generated when a PR has a merge conflict label Jan 13, 2026
@github-actionsgithub-actionsBot added the conflicts Automatically generated when a PR has a merge conflict label Feb 20, 2026
@github-actions

This comment was marked as resolved.

@github-actionsgithub-actionsBot removed the conflicts Automatically generated when a PR has a merge conflict label Mar 3, 2026
@github-actions

This comment was marked as resolved.

@github-actionsgithub-actionsBot removed the conflicts Automatically generated when a PR has a merge conflict label Apr 3, 2026
@AgamjotSB

Copy link
Copy Markdown

was hitting this error when using sa_type, thanks for keeping it updated

@diachkow

Copy link
Copy Markdown
Author

It would be nice to get it merged though 😅

@AgamjotSB

Copy link
Copy Markdown

Hey @tiangolo, gently bumping this. It's been open about a year now, has an LGTM from @YuriiMotov, and @svlandeg mentioned back in September she'd forwarded it to you for merging.

To recap the issue:

  • Runtime is fine: sa_type already works correctly at runtime (SQLAlchemy accepts instantiated types like DateTime(timezone=False) just fine).
  • Typing is failing: The type annotation on Field.sa_type doesn't reflect that, so type checkers (basedpyright, zuban, mypy) flag valid code as an error.
  • Impact: It doesn't break anything at runtime, but it causes CI pipelines to fail.

Worth noting this is really a bug fix, not a feature. It's currently labeled as feature, but the code already does the right thing, this PR just corrects the type signature to match. Might be worth relabeling!

Totally understand if it's just buried under everything else, I didn't want it to get lost. Thanks for all the incredible work you do on FastAPI and SQLModel (and friends) :)

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

Labels

featureNew feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@diachkow@svlandeg@YuriiMotov@AgamjotSB@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

🏷️ Adjust type annotation for Field.sa_type to support instantiated SQLAlchemy types - #1345

Open
diachkow wants to merge 14 commits into
fastapi:mainfrom
diachkow:main
Open

🏷️ Adjust type annotation for Field.sa_type to support instantiated SQLAlchemy types#1345
diachkow wants to merge 14 commits into
fastapi:mainfrom
diachkow:main

Conversation

@diachkow

@diachkowdiachkow commented Apr 21, 2025

Copy link
Copy Markdown

Note

When I was writing description for this PR, I found another discussion started for just the same issue I was experiencing with mypy, so this changes are basically fixing the issue described here

Using sa_type and sa_column_kwargs instead of just sa_column can benefit when using inheritance for classes derived from SQLModel as it was suggested here.

If sa_column is not specified and sa_type is provided, it will be passed as a second, type argumnet to sqlalchemy.Column instance. If you would check sqlalchemy.Column construction definition, it looks as following:

def__init__(
self,
__name_pos: Optional[
Union[str, _TypeEngineArgument[_T], SchemaEventTarget]
] =None,
__type_pos: Optional[
Union[_TypeEngineArgument[_T], SchemaEventTarget]
] =None,
*args: SchemaEventTarget,
name: Optional[str] =None,
type_: Optional[_TypeEngineArgument[_T]] =None,
autoincrement: _AutoIncrementType="auto",
default: Optional[Any] =_NoArg.NO_ARG,
insert_default: Optional[Any] =_NoArg.NO_ARG,
doc: Optional[str] =None,
key: Optional[str] =None,
index: Optional[bool] =None,
unique: Optional[bool] =None,
info: Optional[_InfoType] =None,
nullable: Optional[
Union[bool, Literal[SchemaConst.NULL_UNSPECIFIED]]
] =SchemaConst.NULL_UNSPECIFIED,
onupdate: Optional[Any] =None,
primary_key: bool=False,
server_default: Optional[_ServerDefaultArgument] =None,
server_onupdate: Optional[_ServerOnUpdateArgument] =None,
quote: Optional[bool] =None,
system: bool=False,
comment: Optional[str] =None,
insert_sentinel: bool=False,
_omit_from_statements: bool=False,
_proxies: Optional[Any] =None,
**dialect_kwargs: Any,
):

Note the __type_pos argument with Union[_TypeEngineArgument[_T], SchemaEventTarget] where

_TypeEngineArgument=Union[Type["TypeEngine[_T]"], "TypeEngine[_T]"]

So, from technical perspective you can pass not only the subclass of TypeEngine, e.g. SQLAlchemy's sqltype such as String, Integer, DateTime, JSON etc, but also an instance of this type.

I was trying for JSONB(none_as_null=True) and String(50) and it worked just fine, alembic migrations were generated correctly, only mypy was arguing for type mismatch with call-overload issue.

To fix mypy error, we can update type annotation for sqlmodel.main.Field.sa_type to support also an instantiated SQLAlchemy's sqltype to match those of sqlalchemy.Column

Related discussions:

@diachkow

This comment was marked as resolved.

@svlandegsvlandeg added the feature New feature or request label Apr 22, 2025
@svlandeg

This comment was marked as resolved.

@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

Simple code example to check (in the details)

Details
fromdatetimeimportdatetimefromsqlmodelimportDateTime, Field, SQLModel, create_engineclassA(SQLModel):
created_at: datetime=Field(sa_type=DateTime(timezone=False))
engine=create_engine("sqlite:///")
SQLModel.metadata.create_all(engine)

Running mypy gives

error: No overload variant of "Field" matches argument type "DateTime" [call-overload]

on master and

Success: no issues found in 1 source file

after applying this fix

Comment threadsqlmodel/main.py Outdated
@YuriiMotovYuriiMotov changed the title 🏷️ Adjust type annotation for Field.sa_type to support instantiated SQLAlchemy types🏷️ Adjust type annotation for Field.sa_type to support instantiated SQLAlchemy typesAug 21, 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.

@diachkow

Copy link
Copy Markdown
Author

Thanks for approval! Who is responsible for merging this, what are the rules in this repo?

@YuriiMotov

Copy link
Copy Markdown
Member

Thanks for approval! Who is responsible for merging this, what are the rules in this repo?

Only Sebastian can merge it. I already forwarded it to him. We should just wait

@github-actionsgithub-actionsBot added the conflicts Automatically generated when a PR has a merge conflict label Dec 26, 2025
@github-actions

This comment was marked as resolved.

@github-actionsgithub-actionsBot removed the conflicts Automatically generated when a PR has a merge conflict label Dec 27, 2025
@diachkow

Copy link
Copy Markdown
Author

I have resolved conflicts here. I see you guys made some patch releases recently, hope this change can make it into one of them 🙃

@github-actions

This comment was marked as resolved.

@github-actionsgithub-actionsBot added the conflicts Automatically generated when a PR has a merge conflict label Jan 13, 2026
@github-actionsgithub-actionsBot removed the conflicts Automatically generated when a PR has a merge conflict label Jan 13, 2026
@github-actionsgithub-actionsBot added the conflicts Automatically generated when a PR has a merge conflict label Feb 20, 2026
@github-actions

This comment was marked as resolved.

@github-actionsgithub-actionsBot removed the conflicts Automatically generated when a PR has a merge conflict label Mar 3, 2026
@github-actions

This comment was marked as resolved.

@github-actionsgithub-actionsBot removed the conflicts Automatically generated when a PR has a merge conflict label Apr 3, 2026
@AgamjotSB

Copy link
Copy Markdown

was hitting this error when using sa_type, thanks for keeping it updated

@diachkow

Copy link
Copy Markdown
Author

It would be nice to get it merged though 😅

@AgamjotSB

Copy link
Copy Markdown

Hey @tiangolo, gently bumping this. It's been open about a year now, has an LGTM from @YuriiMotov, and @svlandeg mentioned back in September she'd forwarded it to you for merging.

To recap the issue:

  • Runtime is fine: sa_type already works correctly at runtime (SQLAlchemy accepts instantiated types like DateTime(timezone=False) just fine).
  • Typing is failing: The type annotation on Field.sa_type doesn't reflect that, so type checkers (basedpyright, zuban, mypy) flag valid code as an error.
  • Impact: It doesn't break anything at runtime, but it causes CI pipelines to fail.

Worth noting this is really a bug fix, not a feature. It's currently labeled as feature, but the code already does the right thing, this PR just corrects the type signature to match. Might be worth relabeling!

Totally understand if it's just buried under everything else, I didn't want it to get lost. Thanks for all the incredible work you do on FastAPI and SQLModel (and friends) :)

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

Labels

featureNew feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@diachkow@svlandeg@YuriiMotov@AgamjotSB@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

🏷️ Adjust type annotation for Field.sa_type to support instantiated SQLAlchemy types - #1345

Open
diachkow wants to merge 14 commits into
fastapi:mainfrom
diachkow:main
Open

🏷️ Adjust type annotation for Field.sa_type to support instantiated SQLAlchemy types#1345
diachkow wants to merge 14 commits into
fastapi:mainfrom
diachkow:main

Conversation

@diachkow

@diachkowdiachkow commented Apr 21, 2025

Copy link
Copy Markdown

Note

When I was writing description for this PR, I found another discussion started for just the same issue I was experiencing with mypy, so this changes are basically fixing the issue described here

Using sa_type and sa_column_kwargs instead of just sa_column can benefit when using inheritance for classes derived from SQLModel as it was suggested here.

If sa_column is not specified and sa_type is provided, it will be passed as a second, type argumnet to sqlalchemy.Column instance. If you would check sqlalchemy.Column construction definition, it looks as following:

def__init__(
self,
__name_pos: Optional[
Union[str, _TypeEngineArgument[_T], SchemaEventTarget]
] =None,
__type_pos: Optional[
Union[_TypeEngineArgument[_T], SchemaEventTarget]
] =None,
*args: SchemaEventTarget,
name: Optional[str] =None,
type_: Optional[_TypeEngineArgument[_T]] =None,
autoincrement: _AutoIncrementType="auto",
default: Optional[Any] =_NoArg.NO_ARG,
insert_default: Optional[Any] =_NoArg.NO_ARG,
doc: Optional[str] =None,
key: Optional[str] =None,
index: Optional[bool] =None,
unique: Optional[bool] =None,
info: Optional[_InfoType] =None,
nullable: Optional[
Union[bool, Literal[SchemaConst.NULL_UNSPECIFIED]]
] =SchemaConst.NULL_UNSPECIFIED,
onupdate: Optional[Any] =None,
primary_key: bool=False,
server_default: Optional[_ServerDefaultArgument] =None,
server_onupdate: Optional[_ServerOnUpdateArgument] =None,
quote: Optional[bool] =None,
system: bool=False,
comment: Optional[str] =None,
insert_sentinel: bool=False,
_omit_from_statements: bool=False,
_proxies: Optional[Any] =None,
**dialect_kwargs: Any,
):

Note the __type_pos argument with Union[_TypeEngineArgument[_T], SchemaEventTarget] where

_TypeEngineArgument=Union[Type["TypeEngine[_T]"], "TypeEngine[_T]"]

So, from technical perspective you can pass not only the subclass of TypeEngine, e.g. SQLAlchemy's sqltype such as String, Integer, DateTime, JSON etc, but also an instance of this type.

I was trying for JSONB(none_as_null=True) and String(50) and it worked just fine, alembic migrations were generated correctly, only mypy was arguing for type mismatch with call-overload issue.

To fix mypy error, we can update type annotation for sqlmodel.main.Field.sa_type to support also an instantiated SQLAlchemy's sqltype to match those of sqlalchemy.Column

Related discussions:

@diachkow

This comment was marked as resolved.

@svlandegsvlandeg added the feature New feature or request label Apr 22, 2025
@svlandeg

This comment was marked as resolved.

@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

Simple code example to check (in the details)

Details
fromdatetimeimportdatetimefromsqlmodelimportDateTime, Field, SQLModel, create_engineclassA(SQLModel):
created_at: datetime=Field(sa_type=DateTime(timezone=False))
engine=create_engine("sqlite:///")
SQLModel.metadata.create_all(engine)

Running mypy gives

error: No overload variant of "Field" matches argument type "DateTime" [call-overload]

on master and

Success: no issues found in 1 source file

after applying this fix

Comment threadsqlmodel/main.py Outdated
@YuriiMotovYuriiMotov changed the title 🏷️ Adjust type annotation for Field.sa_type to support instantiated SQLAlchemy types🏷️ Adjust type annotation for Field.sa_type to support instantiated SQLAlchemy typesAug 21, 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.

@diachkow

Copy link
Copy Markdown
Author

Thanks for approval! Who is responsible for merging this, what are the rules in this repo?

@YuriiMotov

Copy link
Copy Markdown
Member

Thanks for approval! Who is responsible for merging this, what are the rules in this repo?

Only Sebastian can merge it. I already forwarded it to him. We should just wait

@github-actionsgithub-actionsBot added the conflicts Automatically generated when a PR has a merge conflict label Dec 26, 2025
@github-actions

This comment was marked as resolved.

@github-actionsgithub-actionsBot removed the conflicts Automatically generated when a PR has a merge conflict label Dec 27, 2025
@diachkow

Copy link
Copy Markdown
Author

I have resolved conflicts here. I see you guys made some patch releases recently, hope this change can make it into one of them 🙃

@github-actions

This comment was marked as resolved.

@github-actionsgithub-actionsBot added the conflicts Automatically generated when a PR has a merge conflict label Jan 13, 2026
@github-actionsgithub-actionsBot removed the conflicts Automatically generated when a PR has a merge conflict label Jan 13, 2026
@github-actionsgithub-actionsBot added the conflicts Automatically generated when a PR has a merge conflict label Feb 20, 2026
@github-actions

This comment was marked as resolved.

@github-actionsgithub-actionsBot removed the conflicts Automatically generated when a PR has a merge conflict label Mar 3, 2026
@github-actions

This comment was marked as resolved.

@github-actionsgithub-actionsBot removed the conflicts Automatically generated when a PR has a merge conflict label Apr 3, 2026
@AgamjotSB

Copy link
Copy Markdown

was hitting this error when using sa_type, thanks for keeping it updated

@diachkow

Copy link
Copy Markdown
Author

It would be nice to get it merged though 😅

@AgamjotSB

Copy link
Copy Markdown

Hey @tiangolo, gently bumping this. It's been open about a year now, has an LGTM from @YuriiMotov, and @svlandeg mentioned back in September she'd forwarded it to you for merging.

To recap the issue:

  • Runtime is fine: sa_type already works correctly at runtime (SQLAlchemy accepts instantiated types like DateTime(timezone=False) just fine).
  • Typing is failing: The type annotation on Field.sa_type doesn't reflect that, so type checkers (basedpyright, zuban, mypy) flag valid code as an error.
  • Impact: It doesn't break anything at runtime, but it causes CI pipelines to fail.

Worth noting this is really a bug fix, not a feature. It's currently labeled as feature, but the code already does the right thing, this PR just corrects the type signature to match. Might be worth relabeling!

Totally understand if it's just buried under everything else, I didn't want it to get lost. Thanks for all the incredible work you do on FastAPI and SQLModel (and friends) :)

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

Labels

featureNew feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@diachkow@svlandeg@YuriiMotov@AgamjotSB@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

🏷️ Adjust type annotation for Field.sa_type to support instantiated SQLAlchemy types - #1345

Open
diachkow wants to merge 14 commits into
fastapi:mainfrom
diachkow:main
Open

🏷️ Adjust type annotation for Field.sa_type to support instantiated SQLAlchemy types#1345
diachkow wants to merge 14 commits into
fastapi:mainfrom
diachkow:main

Conversation

@diachkow

@diachkowdiachkow commented Apr 21, 2025

Copy link
Copy Markdown

Note

When I was writing description for this PR, I found another discussion started for just the same issue I was experiencing with mypy, so this changes are basically fixing the issue described here

Using sa_type and sa_column_kwargs instead of just sa_column can benefit when using inheritance for classes derived from SQLModel as it was suggested here.

If sa_column is not specified and sa_type is provided, it will be passed as a second, type argumnet to sqlalchemy.Column instance. If you would check sqlalchemy.Column construction definition, it looks as following:

def__init__(
self,
__name_pos: Optional[
Union[str, _TypeEngineArgument[_T], SchemaEventTarget]
] =None,
__type_pos: Optional[
Union[_TypeEngineArgument[_T], SchemaEventTarget]
] =None,
*args: SchemaEventTarget,
name: Optional[str] =None,
type_: Optional[_TypeEngineArgument[_T]] =None,
autoincrement: _AutoIncrementType="auto",
default: Optional[Any] =_NoArg.NO_ARG,
insert_default: Optional[Any] =_NoArg.NO_ARG,
doc: Optional[str] =None,
key: Optional[str] =None,
index: Optional[bool] =None,
unique: Optional[bool] =None,
info: Optional[_InfoType] =None,
nullable: Optional[
Union[bool, Literal[SchemaConst.NULL_UNSPECIFIED]]
] =SchemaConst.NULL_UNSPECIFIED,
onupdate: Optional[Any] =None,
primary_key: bool=False,
server_default: Optional[_ServerDefaultArgument] =None,
server_onupdate: Optional[_ServerOnUpdateArgument] =None,
quote: Optional[bool] =None,
system: bool=False,
comment: Optional[str] =None,
insert_sentinel: bool=False,
_omit_from_statements: bool=False,
_proxies: Optional[Any] =None,
**dialect_kwargs: Any,
):

Note the __type_pos argument with Union[_TypeEngineArgument[_T], SchemaEventTarget] where

_TypeEngineArgument=Union[Type["TypeEngine[_T]"], "TypeEngine[_T]"]

So, from technical perspective you can pass not only the subclass of TypeEngine, e.g. SQLAlchemy's sqltype such as String, Integer, DateTime, JSON etc, but also an instance of this type.

I was trying for JSONB(none_as_null=True) and String(50) and it worked just fine, alembic migrations were generated correctly, only mypy was arguing for type mismatch with call-overload issue.

To fix mypy error, we can update type annotation for sqlmodel.main.Field.sa_type to support also an instantiated SQLAlchemy's sqltype to match those of sqlalchemy.Column

Related discussions:

@diachkow

This comment was marked as resolved.

@svlandegsvlandeg added the feature New feature or request label Apr 22, 2025
@svlandeg

This comment was marked as resolved.

@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

Simple code example to check (in the details)

Details
fromdatetimeimportdatetimefromsqlmodelimportDateTime, Field, SQLModel, create_engineclassA(SQLModel):
created_at: datetime=Field(sa_type=DateTime(timezone=False))
engine=create_engine("sqlite:///")
SQLModel.metadata.create_all(engine)

Running mypy gives

error: No overload variant of "Field" matches argument type "DateTime" [call-overload]

on master and

Success: no issues found in 1 source file

after applying this fix

Comment threadsqlmodel/main.py Outdated
@YuriiMotovYuriiMotov changed the title 🏷️ Adjust type annotation for Field.sa_type to support instantiated SQLAlchemy types🏷️ Adjust type annotation for Field.sa_type to support instantiated SQLAlchemy typesAug 21, 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.

@diachkow

Copy link
Copy Markdown
Author

Thanks for approval! Who is responsible for merging this, what are the rules in this repo?

@YuriiMotov

Copy link
Copy Markdown
Member

Thanks for approval! Who is responsible for merging this, what are the rules in this repo?

Only Sebastian can merge it. I already forwarded it to him. We should just wait

@github-actionsgithub-actionsBot added the conflicts Automatically generated when a PR has a merge conflict label Dec 26, 2025
@github-actions

This comment was marked as resolved.

@github-actionsgithub-actionsBot removed the conflicts Automatically generated when a PR has a merge conflict label Dec 27, 2025
@diachkow

Copy link
Copy Markdown
Author

I have resolved conflicts here. I see you guys made some patch releases recently, hope this change can make it into one of them 🙃

@github-actions

This comment was marked as resolved.

@github-actionsgithub-actionsBot added the conflicts Automatically generated when a PR has a merge conflict label Jan 13, 2026
@github-actionsgithub-actionsBot removed the conflicts Automatically generated when a PR has a merge conflict label Jan 13, 2026
@github-actionsgithub-actionsBot added the conflicts Automatically generated when a PR has a merge conflict label Feb 20, 2026
@github-actions

This comment was marked as resolved.

@github-actionsgithub-actionsBot removed the conflicts Automatically generated when a PR has a merge conflict label Mar 3, 2026
@github-actions

This comment was marked as resolved.

@github-actionsgithub-actionsBot removed the conflicts Automatically generated when a PR has a merge conflict label Apr 3, 2026
@AgamjotSB

Copy link
Copy Markdown

was hitting this error when using sa_type, thanks for keeping it updated

@diachkow

Copy link
Copy Markdown
Author

It would be nice to get it merged though 😅

@AgamjotSB

Copy link
Copy Markdown

Hey @tiangolo, gently bumping this. It's been open about a year now, has an LGTM from @YuriiMotov, and @svlandeg mentioned back in September she'd forwarded it to you for merging.

To recap the issue:

  • Runtime is fine: sa_type already works correctly at runtime (SQLAlchemy accepts instantiated types like DateTime(timezone=False) just fine).
  • Typing is failing: The type annotation on Field.sa_type doesn't reflect that, so type checkers (basedpyright, zuban, mypy) flag valid code as an error.
  • Impact: It doesn't break anything at runtime, but it causes CI pipelines to fail.

Worth noting this is really a bug fix, not a feature. It's currently labeled as feature, but the code already does the right thing, this PR just corrects the type signature to match. Might be worth relabeling!

Totally understand if it's just buried under everything else, I didn't want it to get lost. Thanks for all the incredible work you do on FastAPI and SQLModel (and friends) :)

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

Labels

featureNew feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@diachkow@svlandeg@YuriiMotov@AgamjotSB@tiangolo