-
-
Notifications
You must be signed in to change notification settings - Fork 783
Description
First Check
- I added a very descriptive title to this issue.
- I used the GitHub search to find a similar issue and didn't find it.
- I searched the SQLModel documentation, with the integrated search.
- I already searched in Google "How to X in SQLModel" and didn't find any information.
- I already read and followed all the tutorial in the docs and didn't find an answer.
- I already checked if it is not related to SQLModel but to Pydantic.
- I already checked if it is not related to SQLModel but to SQLAlchemy.
Commit to Help
- I commit to help with one of those options 👆
Example Code
import pytest
from pydantic import BaseModel
from typing import List, Optional
from sqlmodel import Session, SQLModel, Field, Relationship, create_engine
from sqlmodel.pool import StaticPool
# MODELS
class Ticket(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
class Mascot(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
class Groups(SQLModel):
ticket: Ticket
mascot: Mascot
# FIXTURES
@pytest.fixture(name='session')
def session_fixture():
engine = create_engine(
"sqlite://",
connect_args={"check_same_thread": False},
poolclass=StaticPool,
)
SQLModel.metadata.create_all(engine)
with Session(engine) as session:
yield session
@pytest.fixture(name='ticket')
def ticket_fixture(session: Session):
t = Ticket()
session.add(t)
session.commit()
session.refresh(t)
yield t
@pytest.fixture(name='mascot')
def mascot_fixture(session: Session):
m = Mascot()
session.add(m)
session.commit()
session.refresh(m)
yield m
# TEST
def test_combine(
session: Session,
ticket: Ticket,
mascot: Mascot,
):
# Problem 1: @ticket is empty, @mascot is set (reverse their order would
# reverses the outcome). Access to any field in @ticket fixes the problem.
print(f'ticket = {ticket.__dict__}') # <-- ticket is empty, no ticket.id value
print(f'mascot = {mascot.__dict__}') # <-- mascot has proper mascot.id as expected
# this works (and when un-commented, makes problem 2 below disappear):
# id = ticket.id if ticket else 0
# Problem 2: this fails (but making class Groups not inherit SQLModel or
# BaseModel, makes the problem disappear):
group = Groups(ticket=ticket, mascot=mascot)
id = group.ticket.id if group.ticket else 0 # <-- sqlalchemy.orm.exc.UnmappedInstanceErrorDescription
Use pytest to run this sample code.
The code defines two simple models/tables (Ticket, Mascot) and a third model (Group) that groups them. It uses pytest to populate the database for testing. The are two problems that occur in the function test_combine():
-
The function takes three fixtures:
sessionand the modelsticket,mascot. The expected behavior is to have both properly initialized inside the function. However, in reality only the latter (mascot) holds data while the former (ticket) appears empty. If we reverse their order in the function arguments, the reverse happens. Also, any access to the content (e.g. toticket.id) refreshes the object - so perhaps somehow the object became stale. -
The function then instantiates a
Groupobject containing the two models, and then tries to access the (stale, from before)ticket. This time, the object does not get auto-refreshed, but instead an exception is raised. Like before, access to the content (e.g. toticket.id) any time earlier makes the problem go away. Also, changingclass Groupto not inherit fromSQLModel(orpydantic.BaseModel), also resolves the problem. So perhaps this is related to nested models (even thoughGroupis not intended to be user/input facing so does not strictly need SQLModel/BaseModel capabilities).
Operating System
Linux
Operating System Details
Ubuntu 21.10
sqlalchemy 1.4.35
pydantic 1.9.1
pytest 7.1.2
SQLModel Version
0.0.6
Python Version
3.9
Additional Context
To run the test, run the command:
pytest -s reproduce.py