Fix PrivateAttr defaults not initialized when loading from DB#1
Open
Fix PrivateAttr defaults not initialized when loading from DB#1
Conversation
When SQLAlchemy loads objects from the database, it bypasses __init__ and only calls __new__. The init_pydantic_private_attrs function was setting __pydantic_private__ to None, which caused accessing any PrivateAttr to raise "TypeError: 'NoneType' object is not subscriptable". Now properly initializes __pydantic_private__ with default values from __private_attributes__, matching Pydantic's own init_private_attributes behavior. Also fixes the same issue in sqlmodel_table_construct. Fixes fastapi#149 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes fastapi#149 -
Pydantic.PrivateAttrdefaultanddefault_factoryare ignored by SQLModel.When SQLAlchemy loads objects from the database, it bypasses
__init__and only calls__new__. Theinit_pydantic_private_attrsfunction was setting__pydantic_private__toNone, which caused accessing anyPrivateAttrto raise:Changes
sqlmodel/_compat.py: Updatedinit_pydantic_private_attrs()to properly iterate over__private_attributes__and populate__pydantic_private__with default values, matching Pydantic's owninit_private_attributesbehavior. Also fixed the same issue insqlmodel_table_construct()where__pydantic_private__was set toNonewhen__pydantic_post_init__is falsy.tests/test_private_attr.py: Added 6 comprehensive tests covering:PrivateAttr(default=...)on direct constructionPrivateAttr(default_factory=...)on direct constructionPrivateAttr(default=...)after loading from DBPrivateAttr(default_factory=...)after loading from DBPrivateAttrafter DB loadPrivateAttr()without defaults is correctly excluded from the dictBefore this fix
After this fix
Test plan
PrivateAttrbehavior all passPrivateAttr(default=...),PrivateAttr(default_factory=...), andPrivateAttr()(no default)