Skip to content

Conversation

@alexisgaziello
Copy link

Fixing:

Private attributes cannot be used in SQLModel. Attributes initialized with PrivateAttr cannot be found.

Proposed solution:

Add a missing line from Pydantic source code.

Note: the class method from_orm does have this initialization.

Related issues:

#149 mentions this issue

Disclaimer

This is my first-ever contribution to an open-source project. I would appreciate any kind of feedback.

@github-actions

This comment was marked as outdated.

@codecov

This comment was marked as outdated.

@t1mk1k
Copy link

t1mk1k commented Nov 2, 2022

Thanks for the fix! I've used it as a workaround in my project by adding it to the init method of my own model.

I noticed that when I loaded a model from the database the private attributes were not initialized because the init in SQLModel is not called. I had to call it separately on the model before being able to use the private attribute.

Have you tested whether your fix works when loading a model from the database?

@alexisgaziello
Copy link
Author

Thanks for the fix! I've used it as a workaround in my project by adding it to the init method of my own model.

I noticed that when I loaded a model from the database the private attributes were not initialized because the init in SQLModel is not called. I had to call it separately on the model before being able to use the private attribute.

Have you tested whether your fix works when loading a model from the database?

Do you mean in an example such as the one below?

test_id = uuid4()


class Hero(SQLModel, table=True):
    id: UUID = Field(default_factory=uuid4, primary_key=True)
    _private_hero_attribute: str = PrivateAttr(default="private hero value")

    metadata = MetaData()


with Session(engine) as session:
    hero_rusty_man = Hero(
        id=test_id,
    )
    session.add(hero_rusty_man)
    session.commit()

with Session(engine) as session:
    statement = select(Hero).where(Hero.id == test_id)
    hero = session.exec(statement).scalars().first()

    assert hero._private_hero_attribute == "private hero value"  # this fails

    hero = Hero.from_orm(hero)
    assert hero._private_hero_attribute == "private hero value"  # this doesn't fail

Indeed, when running SQLAlchemy, the private attributes don't get correctly initialized.
Calling the method from_orm solves the problem for this particular case. Not ideal.

I haven't figured out if there is a way to "automate" this process. Maybe SQLAlchemy is using another init/factory_function?

@github-actions
Copy link
Contributor

📝 Docs preview for commit 844e21c at: https://639ce0b204318b01e2823f64--sqlmodel.netlify.app

@lucasgadams
Copy link

Whats the status of this?

@lucas-labs
Copy link

Any chance we can get this merged?

@alexisgaziello
Copy link
Author

@lucas-labs the reality is that the fix doesn't fully work, as shown in #472 (comment)

@tiangolo tiangolo added the bug Something isn't working label Oct 22, 2023
@AAraKKe
Copy link

AAraKKe commented Feb 5, 2024

Calling the method from_orm solves the problem for this particular case. Not ideal.

This is the way this works in Pydantic; the method is called from init, from_orm, and construct; we need to call it from any place where initialization is required. Could you release this fix? We have the entire infrastructure from a base model from where all objects inherit from a BaseModel to make it much simpler to handle connections. Still, without the ability to use private attributes, this makes it impossible to handle.

I imagine this is the case for many users since a model that represents a database object might need to have more often attributes that do not necessary link to the database itself and are used for internal logic.

@sharenz
Copy link

sharenz commented Feb 10, 2024

I think I am also running into this issue. I am retrieving an object from the database and want to copy it like described in the fastapi docs for partial updates.

object_from_db = session.exec(select_cmd).one_or_none()

copied_object = object_from_db.model_copy() 

The model_copy results in the following error:
object has no attribute '__pydantic_extra__'. Did you mean: '__pydantic_private__'?

Is there a workaround I can use?

@overgodofchaos
Copy link

Maybe it will help someone.

If I need to use an attribute that will not be linked to the database, I use attributes with double double underscores.

class Hero(SQLModel, table=True):
    id: int = Field(primary_key=True)
    name: str = Field(...)
    __private_hero_attribute__: str = "private hero value"

They work, but there is no validation applied to them.

@Seluj78
Copy link

Seluj78 commented Apr 9, 2025

Ping, this is still needed !

@YuriiMotov
Copy link
Member

@alexisgaziello, thanks for your efforts!

Do you have any plans to continue working on this?

@github-actions github-actions bot added the conflicts Automatically generated when a PR has a merge conflict label Sep 5, 2025
@github-actions
Copy link
Contributor

github-actions bot commented Sep 5, 2025

This pull request has a merge conflict that needs to be resolved.

@github-actions
Copy link
Contributor

github-actions bot commented Oct 6, 2025

As this PR has been waiting for the original user for a while but seems to be inactive, it's now going to be closed. But if there's anyone interested, feel free to create a new PR.

@github-actions github-actions bot closed this Oct 6, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working conflicts Automatically generated when a PR has a merge conflict investigate waiting

Projects

None yet

Development

Successfully merging this pull request may close these issues.

10 participants