Repository navigation
Pydantic.PrivateAttr default and default_factory are ignored by SQLModel #149
Description
Activity
- addedquestionFurther information is requestedFurther information is requested
on Oct 28, 2021 Tried some other options for achieving this, with a pydantic model as the
PrivateAttr. The solution as shown in #147 doens't work either. Interestingly, running this in a notebook yields the expected result forB3.Environment:
Ubuntu 18.04.6 LTS (GNU/Linux 5.4.0-87-generic x86_64)
Python 3.8.12
pydantic==1.9.0
sqlmodel==0.0.6
ipykernel==6.9.0from typing import Optional from pydantic import BaseModel, PrivateAttr from sqlmodel import Field, SQLModel class A(BaseModel): some_attr: int = 2 class B1(SQLModel, table=True): _a: A = PrivateAttr(default_factory=A) id: Optional[int] = Field(default=None, primary_key=True) class B2(SQLModel, table=True): _a: A = PrivateAttr(default=A()) id: Optional[int] = Field(default=None, primary_key=True) class B3(SQLModel, table=True): _a: A = PrivateAttr() id: Optional[int] = Field(default=None, primary_key=True) # from https://pydantic-docs.helpmanual.io/usage/models/#private-model-attributes def __init__(self, **data): super().__init__(**data) self._a = A() class B4(SQLModel, table=True): _a: A = PrivateAttr() id: Optional[int] = Field(default=None, primary_key=True) @property def a(self): return self._a for B in [B1, B2, B3, B4]: b = B() try: print(b._a.some_attr) except Exception as e: print(e)
Result in plain python:
'ModelPrivateAttr' object has no attribute 'some_attr' 'ModelPrivateAttr' object has no attribute 'some_attr' 'ModelPrivateAttr' object has no attribute 'some_attr' 'ModelPrivateAttr' object has no attribute 'some_attr'
Result in a notebook:
'ModelPrivateAttr' object has no attribute 'some_attr' 'ModelPrivateAttr' object has no attribute 'some_attr' 2 'ModelPrivateAttr' object has no attribute 'some_attr'
Any solution for this? I just ran into the same problem
Reacted by Alexis, CallumAdgt and TempuraIt appears that we are missing private attribute initialization. I have raised a PR to fix this.
Reacted by Lev Gorodetskii, Pedro Torres, Arnaud Durand, Andreas Köpf, Alex Kreidler and Nick SweetingGreat! I ended up adding 8 columns to my SQL table because of this bug.., finally able to clean up after this merge
same issue here
I have a similar related issue. I have a PrivateAttr field on my model so it is not persisted with a public getter for clarity. If I construct the model myself the getter works but if I retrieve from the database with
selectthen accessing the getter raises:File "/Volumes/Crucial X8/Projects/Portal/.venv/lib/python3.12/site-packages/pydantic/main.py", line 807, in __getattr__ return self.__pydantic_private__[item] # type: ignore │ │ └ '_anonymous' │ └ <member '__pydantic_private__' of 'BaseModel' objects> └ User(id=1, username='testuser') TypeError: 'NoneType' object is not subscriptable
The model
class User(SQLModel, table=True): __tablename__ = "user" id: int | None = Field(default=None, primary_key=True, gt=0) username: str = Field(index=True, max_length=32) email: str password: str = Field(repr=False) superuser: bool = Field(default=False) _anonymous: bool = PrivateAttr(default=False) @property def is_anonymous(self) -> bool: return self._anonymous
If I change my repository method to the following it works fine. Obviously not a viable solution going forward but serves to prove the point.
def find_by_username(self, username: str) -> User | None: stmt = select(User).where(User.username == username) result = self.session.exec(stmt) r = result.one_or_none() u = User(**r.model_dump()) return u
I have a similar related issue. I have a PrivateAttr field on my model so it is not persisted with a public getter for clarity. If I construct the model myself the getter works but if I retrieve from the database with
selectthen accessing the getter raises:File "/Volumes/Crucial X8/Projects/Portal/.venv/lib/python3.12/site-packages/pydantic/main.py", line 807, in __getattr__ return self.__pydantic_private__[item] # type: ignore │ │ └ '_anonymous' │ └ <member '__pydantic_private__' of 'BaseModel' objects> └ User(id=1, username='testuser') TypeError: 'NoneType' object is not subscriptable
The model
class User(SQLModel, table=True): __tablename__ = "user" id: int | None = Field(default=None, primary_key=True, gt=0) username: str = Field(index=True, max_length=32) email: str password: str = Field(repr=False) superuser: bool = Field(default=False) _anonymous: bool = PrivateAttr(default=False) @property def is_anonymous(self) -> bool: return self._anonymous
If I change my repository method to the following it works fine. Obviously not a viable solution going forward but serves to prove the point.
def find_by_username(self, username: str) -> User | None: stmt = select(User).where(User.username == username) result = self.session.exec(stmt) r = result.one_or_none() u = User(**r.model_dump()) return u
Furthermore, if you update the object and add it to the session sqlmodel considers it a new instance entirely and you will see a unique constraint error for the duplicate ID.
I have a similar related issue. I have a PrivateAttr field on my model so it is not persisted with a public getter for clarity. If I construct the model myself the getter works but if I retrieve from the database with
selectthen accessing the getter raises:File "/Volumes/Crucial X8/Projects/Portal/.venv/lib/python3.12/site-packages/pydantic/main.py", line 807, in getattr
return self.pydantic_private[item] # type: ignore
│ │ └ '_anonymous'
│ └ <member 'pydantic_private' of 'BaseModel' objects>
└ User(id=1, username='testuser')TypeError: 'NoneType' object is not subscriptable
The modelclass User(SQLModel, table=True):
tablename = "user"id: int | None = Field(default=None, primary_key=True, gt=0) username: str = Field(index=True, max_length=32) email: str password: str = Field(repr=False) superuser: bool = Field(default=False) _anonymous: bool = PrivateAttr(default=False) @property def is_anonymous(self) -> bool: return self._anonymousIf I change my repository method to the following it works fine. Obviously not a viable solution going forward but serves to prove the point.
def find_by_username(self, username: str) -> User | None: stmt = select(User).where(User.username == username) result = self.session.exec(stmt) r = result.one_or_none() u = User(**r.model_dump()) return uI've worked around this with an event listener
from pydantic._internal._model_construction import init_private_attributes from sqlalchemy import event from sqlmodel import SQLModel @event.listens_for(SQLModel, "load", propagate=True) def receive_load(target: Any, context: Any) -> None: init_private_attributes(target, None)
Reacted by Erik ForsbergReacted by Kyle Smith- locked and limited conversation to collaborators
on May 18, 2026
First Check
Commit to Help
Example Code
Description
As far as I can tell SQLModel is ignoring the
defaultanddefault_factoryparameters ofpydantic.PrivateAttr. The example I've given above reproduces on my system. The output can be seen here:As you can see the field is not set to
None, and instead is an empty instance ofpydantic.fields.ModelPrivateAttr.Operating System
macOS
Operating System Details
No response
SQLModel Version
0.0.4
Python Version
3.9.5
Additional Context
No response