자동 ID, None 기본값, 그리고 데이터 새로고침
자동 ID, None 기본값, 그리고 데이터 새로고침 (Automatic IDs, None Defaults, and Refreshing Data)
이번 장에서는 id 필드가 왜 데이터베이스에서는 NULL이 될 수 없고, 파이썬 코드에서는 None이 될 수 있는지, 그리고 데이터를 자동으로 새로고침(refresh)하는 메커니즘을 자세히 배워요. 데이터베이스와 파이썬 객체가 어떤 순간에 동기화되는지 이해하는 중요한 장입니다.
출처: 공식문서
배경
이전 장에서 SQLModel로 데이터베이스에 행을 추가하는 방법을 봤어요.
이제 id 필드가 왜 데이터베이스에서 NULL이 될 수 없는지에 대해 이야기해 볼게요. 기본 키(primary key)이기 때문이죠. 그래서 우리는 그것을 Field(primary_key=True)로 선언해요.
하지만 같은 id 필드는 파이썬 코드에서 실제로 None이 될 수 있어요. 그래서 타입을 int | None으로 선언하고, 기본값을 Field(default=None)으로 설정했죠.
# Code above omitted 👆
class Hero(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str
secret_name: str
age: int | None = None
# Code below omitted 👇
먼저 지금 다루는 코드 전체를 보여드릴게요. 각 단계에서 데이터베이스와 파이썬 코드 사이의 동기화가 어떻게 일어나는지 보려고 echo=True로 engine을 만들었기 때문에, 실행되는 SQL 문을 볼 수 있어요.
from sqlmodel import Field, Session, SQLModel, create_engine
class Hero(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str
secret_name: str
age: int | None = None
sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"
engine = create_engine(sqlite_url, echo=True)
def create_db_and_tables():
SQLModel.metadata.create_all(engine)
def create_heroes():
hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)
print("Before interacting with the database")
print("Hero 1:", hero_1)
print("Hero 2:", hero_2)
print("Hero 3:", hero_3)
with Session(engine) as session:
session.add(hero_1)
session.add(hero_2)
session.add(hero_3)
print("After adding to the session")
print("Hero 1:", hero_1)
print("Hero 2:", hero_2)
print("Hero 3:", hero_3)
session.commit()
print("After committing the session")
print("Hero 1:", hero_1)
print("Hero 2:", hero_2)
print("Hero 3:", hero_3)
print("After committing the session, show IDs")
print("Hero 1 ID:", hero_1.id)
print("Hero 2 ID:", hero_2.id)
print("Hero 3 ID:", hero_3.id)
print("After committing the session, show names")
print("Hero 1 name:", hero_1.name)
print("Hero 2 name:", hero_2.name)
print("Hero 3 name:", hero_3.name)
session.refresh(hero_1)
session.refresh(hero_2)
session.refresh(hero_3)
print("After refreshing the heroes")
print("Hero 1:", hero_1)
print("Hero 2:", hero_2)
print("Hero 3:", hero_3)
print("After the session closes")
print("Hero 1:", hero_1)
print("Hero 2:", hero_2)
print("Hero 3:", hero_3)
def main():
create_db_and_tables()
create_heroes()
if __name__ == "__main__":
main()
다음으로, 데이터베이스와 파이썬 코드 사이의 데이터 동기화에 대해 조금 더 보여드릴게요.
그 id 필드에서 실제 int를 데이터베이스에서 언제 얻게 될까요? 그걸 모두 봅시다. 👇
새 Hero 인스턴스 만들기
새 Hero 인스턴스를 만들 때 우리는 id를 설정하지 않아요.
# Code above omitted 👆
def create_heroes():
hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)
# Code below omitted 👇
int | None이 어떻게 도움을 주나
id를 설정하지 않으니까, Field(default=None)에서 설정한 파이썬 기본값 None을 갖게 돼요.
우리가 int | None으로, 그리고 기본값 None으로 정의하는 유일한 이유가 바로 이거예요.
왜냐하면 코드의 이 시점, 즉 데이터베이스와 상호작용하기 전에는 파이썬 값이 실제로 None일 수 있으니까요.
만약 id가 항상 int라고 가정하고 int | None 없이 타입 어노테이션을 추가했다면, 이렇게 깨진 코드를 쓸 수도 있어요.
next_hero_id = hero_1.id + 1
이 코드를 히어로를 데이터베이스에 저장하기 전에 실행하고 hero_1.id가 여전히 None이었다면, 이런 오류가 나요.
TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'
하지만 int | None으로 선언하면, 에디터가 hero_1.id가 None이면 코드가 유효하지 않을 수 있다는 경고를 보여줘서 깨진 코드를 피하도록 도와줘요. 🔍
기본 id 값 출력하기
히어로들을 데이터베이스에 추가하기 전에 출력해서 그걸 확인할 수 있어요.
# Code above omitted 👆
def create_heroes():
hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)
print("Before interacting with the database")
print("Hero 1:", hero_1)
print("Hero 2:", hero_2)
print("Hero 3:", hero_3)
# Code below omitted 👇
그러면 이렇게 출력돼요.
$ uv run python app.py
// Output above omitted 👆
Before interacting with the database
Hero 1: id=None name='Deadpond' secret_name='Dive Wilson' age=None
Hero 2: id=None name='Spider-Boy' secret_name='Pedro Parqueador' age=None
Hero 3: id=None name='Rusty-Man' secret_name='Tommy Sharp' age=48
모두 id=None인 걸 볼 수 있어요.
그게 우리가 Hero 모델 클래스에 정의한 기본값이에요.
이 객체들을 session에 add 하면 무슨 일이 일어날까요?
객체들을 세션에 추가하기
Hero 인스턴스 객체들을 session에 추가한 뒤에도, ID는 여전히 None이에요.
with 블록으로 세션을 만들고 객체들을 추가한 다음 다시 출력해서 확인할 수 있어요.
# Code above omitted 👆
def create_heroes():
hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)
print("Before interacting with the database")
print("Hero 1:", hero_1)
print("Hero 2:", hero_2)
print("Hero 3:", hero_3)
with Session(engine) as session:
session.add(hero_1)
session.add(hero_2)
session.add(hero_3)
print("After adding to the session")
print("Hero 1:", hero_1)
print("Hero 2:", hero_2)
print("Hero 3:", hero_3)
# Code below omitted 👇
이것도 다시 객체들의 id를 None으로 출력할 거예요.
$ uv run python app.py
// Output above omitted 👆
After adding to the session
Hero 1: id=None name='Deadpond' secret_name='Dive Wilson' age=None
Hero 2: id=None name='Spider-Boy' secret_name='Pedro Parqueador' age=None
Hero 3: id=None name='Rusty-Man' secret_name='Tommy Sharp' age=48
앞서 봤듯이, session은 똑똑해서 변경할 것을 준비할 때마다 데이터베이스와 대화하지 않아요. 우리가 준비가 되고 commit 하라고 말한 뒤에야 모든 SQL을 데이터베이스로 보내서 데이터를 저장해요.
변경 사항을 데이터베이스에 커밋하기
그리고 나서 세션의 변경 사항을 commit 하고 다시 출력할 수 있어요.
# Code above omitted 👆
with Session(engine) as session:
session.add(hero_1)
session.add(hero_2)
session.add(hero_3)
print("After adding to the session")
print("Hero 1:", hero_1)
print("Hero 2:", hero_2)
print("Hero 3:", hero_3)
session.commit()
print("After committing the session")
print("Hero 1:", hero_1)
print("Hero 2:", hero_2)
print("Hero 3:", hero_3)
# Code below omitted 👇
그리고 나서, 예상치 못한 일이 일어나요. 출력을 보면 Hero 인스턴스 객체들이 데이터가 전혀 없는 것처럼 보여요.
$ uv run python app.py
// Output above omitted 👆
// Here the engine talks to the database, the SQL part
INFO Engine BEGIN (implicit)
INFO Engine INSERT INTO hero (name, secret_name, age) VALUES (?, ?, ?)
INFO Engine [generated in 0.00018s] ('Deadpond', 'Dive Wilson', None)
INFO Engine INSERT INTO hero (name, secret_name, age) VALUES (?, ?, ?)
INFO Engine [cached since 0.0008968s ago] ('Spider-Boy', 'Pedro Parqueador', None)
INFO Engine INSERT INTO hero (name, secret_name, age) VALUES (?, ?, ?)
INFO Engine [cached since 0.001143s ago] ('Rusty-Man', 'Tommy Sharp', 48)
INFO Engine COMMIT
// And now our prints
After committing the session
Hero 1:
Hero 2:
Hero 3:
// What is happening here? 😱
무슨 일이냐면, SQLModel(실제로는 SQLAlchemy)이 내부적으로 이 객체들을 "expired(만료)"라고 표시해서, 그들이 자신의 데이터 최신 버전을 갖고 있지 않다고 여기는 거예요. 이는 데이터베이스에서 일부 필드가 업데이트될 수 있기 때문이에요. 예를 들어, 변경 사항을 저장할 때 자동으로 업데이트되는 updated_at: datetime 필드를 상상해 보세요.
다른 값들도 바뀌었을 수 있으므로, session이 확실하고 안전하게 하기 위해 선택하는 방법은 객체들을 내부적으로 expired로 표시하는 거예요.
그리고 나서, 다음에 각 속성에 접근할 때, 예를 들어:
current_hero_name = hero_1.name
...SQLModel(실제로는 SQLAlchemy)이 데이터베이스에 연락해서 데이터의 가장 최신 버전을 가져와 객체의 그 name 필드를 업데이트하고, 나머지 파이썬 표현식에서 사용할 수 있게 해요. 위 예제에서 그 시점에 파이썬은 계속 실행하면서 (방금 업데이트된) hero_1.name 값을 사용해 current_hero_name 변수에 넣을 수 있어요.
이 모든 것이 자동으로, 그리고 뒤에서 일어나요. ✨
그리고 우리 예제의 재미있고 이상한 점이 바로 여기 있어요.
print("Hero 1:", hero_1)
우리는 객체의 속성, 예를 들어 hero.name에 접근하지 않았어요. 객체 전체에 접근해서 출력만 했죠. 그래서 SQLAlchemy는 우리가 이 객체의 데이터에 접근하고 싶다는 것을 알 방법이 없어요.
필드 하나 출력하기
속성에 접근할 때 이 자동 만료와 새로고침이 어떻게 동작하는지 확인하고 이해하기 위해, 개별 필드(인스턴스 속성) 몇 개를 출력할 수 있어요.
# Code above omitted 👆
with Session(engine) as session:
session.add(hero_1)
session.add(hero_2)
session.add(hero_3)
print("After adding to the session")
print("Hero 1:", hero_1)
print("Hero 2:", hero_2)
print("Hero 3:", hero_3)
session.commit()
print("After committing the session")
print("Hero 1:", hero_1)
print("Hero 2:", hero_2)
print("Hero 3:", hero_3)
print("After committing the session, show IDs")
print("Hero 1 ID:", hero_1.id)
print("Hero 2 ID:", hero_2.id)
print("Hero 3 ID:", hero_3.id)
print("After committing the session, show names")
print("Hero 1 name:", hero_1.name)
print("Hero 2 name:", hero_2.name)
print("Hero 3 name:", hero_3.name)
# Code below omitted 👇
이제 우리는 실제로 속성에 접근하고 있어요. 객체 전체 hero_1을 출력하는 대신:
print("Hero 1:", hero_1)
...이제 hero.id의 id 속성을 출력하고 있죠.
print("Hero 1 ID:", hero_1.id)
속성에 접근하면 SQLModel(실제로는 SQLAlchemy)이 아래에서 데이터베이스에서 데이터를 새로고침하고, 객체의 id 속성에 설정하고, 파이썬 표현식(이 경우엔 그냥 출력)에 사용할 수 있게 하는 많은 작업을 촉발해요.
어떻게 동작하는지 봅시다.
$ uv run python app.py
// Output above omitted 👆
// After committing, the objects are expired and have no values
After committing the session
Hero 1:
Hero 2:
Hero 3:
// Now we will access an attribute like the ID, this is the first print
After committing the session, show IDs
// Notice that before printing the first ID, the Session makes the Engine go to the database to refresh the data 🤓
INFO Engine BEGIN (implicit)
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
FROM hero
WHERE hero.id = ?
INFO Engine [generated in 0.00017s] (1,)
// Here's our first print, now we have the database-generated ID
Hero 1 ID: 1
// Before the next print, refresh the data for the second object
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
FROM hero
WHERE hero.id = ?
INFO Engine [cached since 0.001245s ago] (2,)
// Here's our print for the second hero with its auto-generated ID
Hero 2 ID: 2
// Before the third print, refresh its data
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
FROM hero
WHERE hero.id = ?
INFO Engine [cached since 0.002215s ago] (3,)
// And here's our print for the third hero
Hero 3 ID: 3
// What if we print another attribute like the name?
After committing the session, show names
Hero 1 name: Deadpond
Hero 2 name: Spider-Boy
Hero 3 name: Rusty-Man
// Because the Session already refreshed these objects with all their data and the session knows they are not expired, it doesn't have to go again to the database for the names 🤓
객체들을 명시적으로 새로고침하기
방금 session이 속성에 접근할 때 부수 효과로 뒤에서 데이터를 자동으로 어떻게 새로고침하는지 배웠어요.
하지만 데이터를 명시적으로 새로고침하고 싶다면 어떻게 할까요?
session.refresh(object)로 할 수 있어요.
# Code above omitted 👆
with Session(engine) as session:
session.add(hero_1)
session.add(hero_2)
session.add(hero_3)
print("After adding to the session")
print("Hero 1:", hero_1)
print("Hero 2:", hero_2)
print("Hero 3:", hero_3)
session.commit()
print("After committing the session")
print("Hero 1:", hero_1)
print("Hero 2:", hero_2)
print("Hero 3:", hero_3)
print("After committing the session, show IDs")
print("Hero 1 ID:", hero_1.id)
print("Hero 2 ID:", hero_2.id)
print("Hero 3 ID:", hero_3.id)
print("After committing the session, show names")
print("Hero 1 name:", hero_1.name)
print("Hero 2 name:", hero_2.name)
print("Hero 3 name:", hero_3.name)
session.refresh(hero_1)
session.refresh(hero_2)
session.refresh(hero_3)
print("After refreshing the heroes")
print("Hero 1:", hero_1)
print("Hero 2:", hero_2)
print("Hero 3:", hero_3)
# Code below omitted 👇
파이썬이 이 코드를 실행할 때:
session.refresh(hero_1)
...session이 가서 engine이 이 객체 hero_1의 최근 데이터를 얻기 위해 데이터베이스와 소통하게 하고, 그다음 그 데이터를 hero_1 객체에 넣고 "fresh(신선함)" 또는 "not expired(만료되지 않음)"로 표시해요.
출력은 이렇게 생겨요.
$ uv run python app.py
// Output above omitted 👆
// The first refresh
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
FROM hero
WHERE hero.id = ?
INFO Engine [generated in 0.00024s] (1,)
// The second refresh
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
FROM hero
WHERE hero.id = ?
INFO Engine [cached since 0.001487s ago] (2,)
// The third refresh
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
FROM hero
WHERE hero.id = ?
INFO Engine [cached since 0.002377s ago] (3,)
// Now print the data, as it's already refreshed there's no need for the Session to go and refresh it again
After refreshing the heroes
Hero 1: age=None id=1 name='Deadpond' secret_name='Dive Wilson'
Hero 2: age=None id=2 name='Spider-Boy' secret_name='Pedro Parqueador'
Hero 3: age=48 id=3 name='Rusty-Man' secret_name='Tommy Sharp'
이건 유용할 수 있어요. 예를 들어 히어로를 만드는 웹 API를 만들고 있을 때요. 어떤 데이터로 히어로가 만들어지면, 그것을 클라이언트에 반환하죠.
데이터를 새로고침하는 자동 마법이 촉발되지 않아서 비어 보이는 객체를 반환하고 싶지 않을 거예요.
이 경우, session으로 객체를 데이터베이스에 commit 한 뒤, 그것을 새로고침한 다음 클라이언트에 반환하면 돼요. 이러면 객체가 신선한 데이터를 갖고 있게 됩니다.
세션을 닫은 뒤 데이터 출력하기
이제, 마지막 실험으로, session이 닫힌 뒤에도 데이터를 출력할 수 있어요.
여기서 놀랄 일은 없어요. 여전히 동작해요.
# Code above omitted 👆
with Session(engine) as session:
# Code here omitted 👈
print("After the session closes")
print("Hero 1:", hero_1)
print("Hero 2:", hero_2)
print("Hero 3:", hero_3)
# Code below omitted 👇
출력도 역시 같은 데이터를 보여줘요.
$ uv run python app.py
// Output above omitted 👆
// By finishing the with block, the Session is closed, including a rollback of any pending transaction that could have been there and was not committed
INFO Engine ROLLBACK
// Then we print the data, it works normally
After the session closes
Hero 1: age=None id=1 name='Deadpond' secret_name='Dive Wilson'
Hero 2: age=None id=2 name='Spider-Boy' secret_name='Pedro Parqueador'
Hero 3: age=48 id=3 name='Rusty-Man' secret_name='Tommy Sharp'
코드 전체 다시 보기
이제 이 모든 코드를 다시 봅시다.
팁
번호가 매겨진 풍선 각각은 출력에서 각 줄이 무엇을 출력할지 보여줘요.
그리고 engine을
echo=True로 만들었으니, 각 단계에서 실행되는 SQL 문을 볼 수 있어요.
from sqlmodel import Field, Session, SQLModel, create_engine
class Hero(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str
secret_name: str
age: int | None = None
sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"
engine = create_engine(sqlite_url, echo=True)
def create_db_and_tables():
SQLModel.metadata.create_all(engine)
def create_heroes():
hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)
print("Before interacting with the database")
print("Hero 1:", hero_1)
print("Hero 2:", hero_2)
print("Hero 3:", hero_3)
with Session(engine) as session:
session.add(hero_1)
session.add(hero_2)
session.add(hero_3)
print("After adding to the session")
print("Hero 1:", hero_1)
print("Hero 2:", hero_2)
print("Hero 3:", hero_3)
session.commit()
print("After committing the session")
print("Hero 1:", hero_1)
print("Hero 2:", hero_2)
print("Hero 3:", hero_3)
print("After committing the session, show IDs")
print("Hero 1 ID:", hero_1.id)
print("Hero 2 ID:", hero_2.id)
print("Hero 3 ID:", hero_3.id)
print("After committing the session, show names")
print("Hero 1 name:", hero_1.name)
print("Hero 2 name:", hero_2.name)
print("Hero 3 name:", hero_3.name)
session.refresh(hero_1)
session.refresh(hero_2)
session.refresh(hero_3)
print("After refreshing the heroes")
print("Hero 1:", hero_1)
print("Hero 2:", hero_2)
print("Hero 3:", hero_3)
print("After the session closes")
print("Hero 1:", hero_1)
print("Hero 2:", hero_2)
print("Hero 3:", hero_3)
def main():
create_db_and_tables()
create_heroes()
if __name__ == "__main__":
main()
이 프로그램을 실행해 나오는 모든 출력을 한 번에 모아 보면 이렇게 돼요.
$ uv run python app.py
INFO Engine BEGIN (implicit)
INFO Engine PRAGMA main.table_info("hero")
INFO Engine [raw sql] ()
INFO Engine PRAGMA temp.table_info("hero")
INFO Engine [raw sql] ()
INFO Engine
CREATE TABLE hero (
id INTEGER,
name VARCHAR NOT NULL,
secret_name VARCHAR NOT NULL,
age INTEGER,
PRIMARY KEY (id)
)
INFO Engine [no key 0.00018s] ()
INFO Engine COMMIT
Before interacting with the database
Hero 1: id=None name='Deadpond' secret_name='Dive Wilson' age=None
Hero 2: id=None name='Spider-Boy' secret_name='Pedro Parqueador' age=None
Hero 3: id=None name='Rusty-Man' secret_name='Tommy Sharp' age=48
After adding to the session
Hero 1: id=None name='Deadpond' secret_name='Dive Wilson' age=None
Hero 2: id=None name='Spider-Boy' secret_name='Pedro Parqueador' age=None
Hero 3: id=None name='Rusty-Man' secret_name='Tommy Sharp' age=48
INFO Engine BEGIN (implicit)
INFO Engine INSERT INTO hero (name, secret_name, age) VALUES (?, ?, ?)
INFO Engine [generated in 0.00022s] ('Deadpond', 'Dive Wilson', None)
INFO Engine INSERT INTO hero (name, secret_name, age) VALUES (?, ?, ?)
INFO Engine [cached since 0.001127s ago] ('Spider-Boy', 'Pedro Parqueador', None)
INFO Engine INSERT INTO hero (name, secret_name, age) VALUES (?, ?, ?)
INFO Engine [cached since 0.001483s ago] ('Rusty-Man', 'Tommy Sharp', 48)
INFO Engine COMMIT
After committing the session
Hero 1:
Hero 2:
Hero 3:
After committing the session, show IDs
INFO Engine BEGIN (implicit)
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
FROM hero
WHERE hero.id = ?
INFO Engine [generated in 0.00029s] (1,)
Hero 1 ID: 1
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
FROM hero
WHERE hero.id = ?
INFO Engine [cached since 0.002132s ago] (2,)
Hero 2 ID: 2
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
FROM hero
WHERE hero.id = ?
INFO Engine [cached since 0.003367s ago] (3,)
Hero 3 ID: 3
After committing the session, show names
Hero 1 name: Deadpond
Hero 2 name: Spider-Boy
Hero 3 name: Rusty-Man
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
FROM hero
WHERE hero.id = ?
INFO Engine [generated in 0.00025s] (1,)
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
FROM hero
WHERE hero.id = ?
INFO Engine [cached since 0.001583s ago] (2,)
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
FROM hero
WHERE hero.id = ?
INFO Engine [cached since 0.002722s ago] (3,)
After refreshing the heroes
Hero 1: age=None id=1 name='Deadpond' secret_name='Dive Wilson'
Hero 2: age=None id=2 name='Spider-Boy' secret_name='Pedro Parqueador'
Hero 3: age=48 id=3 name='Rusty-Man' secret_name='Tommy Sharp'
INFO Engine ROLLBACK
After the session closes
Hero 1: age=None id=1 name='Deadpond' secret_name='Dive Wilson'
Hero 2: age=None id=2 name='Spider-Boy' secret_name='Pedro Parqueador'
Hero 3: age=48 id=3 name='Rusty-Man' secret_name='Tommy Sharp'
요약 (Recap)
그걸 다 읽으셨네요! 정말 많았죠! 케이크 하나 드세요, 받을 자격이 있어요. 🍰
우리는 session이 어떻게 engine을 사용해 데이터베이스에 SQL을 보내서 데이터를 만들고 가져오는지 이야기했어요. 어떻게 "expired"(만료)와 "fresh"(신선) 데이터를 추적하는지, 어떤 순간에 자동으로 데이터를 가져오는지 (인스턴스 속성에 접근할 때), 그리고 메모리 안의 객체와 데이터베이스 사이의 데이터가 session을 통해 어떻게 동기화되는지요.
그걸 다 이해했다면 이제 SQLModel, SQLAlchemy, 그리고 파이썬에서 데이터베이스와의 상호작용이 일반적으로 어떻게 동작하는지 많이 알게 된 거예요.
만약 다 이해하지 못했다 해도 괜찮아요. 나중에 와서 개념을 refresh(새로고침)하면 되죠.
이게 아마 문제를 일으키고 머리를 긁게 만드는 주요 버그 유형 중 하나일 거예요. 공부하느라 수고했어요! 💪