🗄 (🔗) 💽 ⏮️ 🏒¶
🚥 👆 ▶️ 🏗 ⚪️➡️ 🖌, 👆 🎲 👻 📆 ⏮️ 🇸🇲 🐜 (🗄 (🔗) 💽), ⚖️ 🙆 🎏 🔁 🐜.
🚥 👆 ⏪ ✔️ 📟 🧢 👈 ⚙️ 🏒 🐜, 👆 💪 ✅ 📥 ❔ ⚙️ ⚫️ ⏮️ FastAPI.
🐍 3️⃣.7️⃣ ➕ ✔
👆 🔜 💪 🐍 3️⃣.7️⃣ ⚖️ 🔛 🔒 ⚙️ 🏒 ⏮️ FastAPI.
🏒 🔁¶
🏒 🚫 🔧 🔁 🛠️, ⚖️ ⏮️ 👫 🤯.
🏒 ✔️ 🏋️ 🔑 🔃 🚮 🔢 & 🔃 ❔ ⚫️ 🔜 ⚙️.
🚥 👆 🛠️ 🈸 ⏮️ 🗝 🚫-🔁 🛠️, & 💪 👷 ⏮️ 🌐 🚮 🔢, ⚫️ 💪 👑 🧰.
✋️ 🚥 👆 💪 🔀 🔢, 🐕🦺 🌖 🌘 1️⃣ 🔁 💽, 👷 ⏮️ 🔁 🛠️ (💖 FastAPI), ♒️, 👆 🔜 💪 🚮 🏗 ➕ 📟 🔐 👈 🔢.
👐, ⚫️ 💪 ⚫️, & 📥 👆 🔜 👀 ⚫️❔ ⚫️❔ 📟 👆 ✔️ 🚮 💪 ⚙️ 🏒 ⏮️ FastAPI.
🎏 📱¶
👥 🔜 ✍ 🎏 🈸 🇸🇲 🔰 (🗄 (🔗) 💽).
🌅 📟 🤙 🎏.
, 👥 🔜 🎯 🕴 🔛 🔺.
📁 📊¶
➡️ 💬 👆 ✔️ 📁 📛 my_super_project
👈 🔌 🎧-📁 🤙 sql_app
⏮️ 📊 💖 👉:
.
└── sql_app
├── __init__.py
├── crud.py
├── database.py
├── main.py
└── schemas.py
👉 🌖 🎏 📊 👥 ✔️ 🇸🇲 🔰.
🔜 ➡️ 👀 ⚫️❔ 🔠 📁/🕹 🔨.
✍ 🏒 🍕¶
➡️ 🔗 📁 sql_app/database.py
.
🐩 🏒 📟¶
➡️ 🥇 ✅ 🌐 😐 🏒 📟, ✍ 🏒 💽:
from contextvars import ContextVar
import peewee
DATABASE_NAME = "test.db"
db_state_default = {"closed": None, "conn": None, "ctx": None, "transactions": None}
db_state = ContextVar("db_state", default=db_state_default.copy())
class PeeweeConnectionState(peewee._ConnectionState):
def __init__(self, **kwargs):
super().__setattr__("_state", db_state)
super().__init__(**kwargs)
def __setattr__(self, name, value):
self._state.get()[name] = value
def __getattr__(self, name):
return self._state.get()[name]
db = peewee.SqliteDatabase(DATABASE_NAME, check_same_thread=False)
db._state = PeeweeConnectionState()
Tip
✔️ 🤯 👈 🚥 👆 💚 ⚙️ 🎏 💽, 💖 ✳, 👆 🚫 🚫 🔀 🎻. 👆 🔜 💪 ⚙️ 🎏 🏒 💽 🎓.
🗒¶
❌:
check_same_thread=False
🌓 1️⃣ 🇸🇲 🔰:
connect_args={"check_same_thread": False}
...⚫️ 💪 🕴 SQLite
.
📡 ℹ
⚫️❔ 🎏 📡 ℹ 🗄 (🔗) 💽 ✔.
⚒ 🏒 🔁-🔗 PeeweeConnectionState
¶
👑 ❔ ⏮️ 🏒 & FastAPI 👈 🏒 ⚓️ 🙇 🔛 🐍 threading.local
, & ⚫️ 🚫 ✔️ 🎯 🌌 🔐 ⚫️ ⚖️ ➡️ 👆 🍵 🔗/🎉 🔗 (🔨 🇸🇲 🔰).
& threading.local
🚫 🔗 ⏮️ 🆕 🔁 ⚒ 🏛 🐍.
📡 ℹ
threading.local
⚙️ ✔️ "🎱" 🔢 👈 ✔️ 🎏 💲 🔠 🧵.
👉 ⚠ 🗝 🛠️ 🏗 ✔️ 1️⃣ 👁 🧵 📍 📨, 🙅♂ 🌖, 🙅♂ 🌘.
⚙️ 👉, 🔠 📨 🔜 ✔️ 🚮 👍 💽 🔗/🎉, ❔ ☑ 🏁 🥅.
✋️ FastAPI, ⚙️ 🆕 🔁 ⚒, 💪 🍵 🌅 🌘 1️⃣ 📨 🔛 🎏 🧵. & 🎏 🕰, 👁 📨, ⚫️ 💪 🏃 💗 👜 🎏 🧵 (🧵), ⚓️ 🔛 🚥 👆 ⚙️ async def
⚖️ 😐 def
. 👉 ⚫️❔ 🤝 🌐 🎭 📈 FastAPI.
✋️ 🐍 3️⃣.7️⃣ & 🔛 🚚 🌖 🏧 🎛 threading.local
, 👈 💪 ⚙️ 🥉 🌐❔ threading.local
🔜 ⚙️, ✋️ 🔗 ⏮️ 🆕 🔁 ⚒.
👥 🔜 ⚙️ 👈. ⚫️ 🤙 contextvars
.
👥 🔜 🔐 🔗 🍕 🏒 👈 ⚙️ threading.local
& ❎ 👫 ⏮️ contextvars
, ⏮️ 🔗 ℹ.
👉 5️⃣📆 😑 🍖 🏗 (& ⚫️ 🤙), 👆 🚫 🤙 💪 🍕 🤔 ❔ ⚫️ 👷 ⚙️ ⚫️.
👥 🔜 ✍ PeeweeConnectionState
:
from contextvars import ContextVar
import peewee
DATABASE_NAME = "test.db"
db_state_default = {"closed": None, "conn": None, "ctx": None, "transactions": None}
db_state = ContextVar("db_state", default=db_state_default.copy())
class PeeweeConnectionState(peewee._ConnectionState):
def __init__(self, **kwargs):
super().__setattr__("_state", db_state)
super().__init__(**kwargs)
def __setattr__(self, name, value):
self._state.get()[name] = value
def __getattr__(self, name):
return self._state.get()[name]
db = peewee.SqliteDatabase(DATABASE_NAME, check_same_thread=False)
db._state = PeeweeConnectionState()
👉 🎓 😖 ⚪️➡️ 🎁 🔗 🎓 ⚙️ 🏒.
⚫️ ✔️ 🌐 ⚛ ⚒ 🏒 ⚙️ contextvars
↩️ threading.local
.
contextvars
👷 🍖 🎏 🌘 threading.local
. ✋️ 🎂 🏒 🔗 📟 🤔 👈 👉 🎓 👷 ⏮️ threading.local
.
, 👥 💪 ➕ 🎱 ⚒ ⚫️ 👷 🚥 ⚫️ ⚙️ threading.local
. __init__
, __setattr__
, & __getattr__
🛠️ 🌐 ✔ 🎱 👉 ⚙️ 🏒 🍵 🤔 👈 ⚫️ 🔜 🔗 ⏮️ FastAPI.
Tip
👉 🔜 ⚒ 🏒 🎭 ☑ 🕐❔ ⚙️ ⏮️ FastAPI. 🚫 🎲 📂 ⚖️ 📪 🔗 👈 ➖ ⚙️, 🏗 ❌, ♒️.
✋️ ⚫️ 🚫 🤝 🏒 🔁 💎-🏋️. 👆 🔜 ⚙️ 😐 def
🔢 & 🚫 async def
.
⚙️ 🛃 PeeweeConnectionState
🎓¶
🔜, 📁 ._state
🔗 🔢 🏒 💽 db
🎚 ⚙️ 🆕 PeeweeConnectionState
:
from contextvars import ContextVar
import peewee
DATABASE_NAME = "test.db"
db_state_default = {"closed": None, "conn": None, "ctx": None, "transactions": None}
db_state = ContextVar("db_state", default=db_state_default.copy())
class PeeweeConnectionState(peewee._ConnectionState):
def __init__(self, **kwargs):
super().__setattr__("_state", db_state)
super().__init__(**kwargs)
def __setattr__(self, name, value):
self._state.get()[name] = value
def __getattr__(self, name):
return self._state.get()[name]
db = peewee.SqliteDatabase(DATABASE_NAME, check_same_thread=False)
db._state = PeeweeConnectionState()
Tip
⚒ 💭 👆 📁 db._state
⏮️ 🏗 db
.
Tip
👆 🔜 🎏 🙆 🎏 🏒 💽, 🔌 PostgresqlDatabase
, MySQLDatabase
, ♒️.
✍ 💽 🏷¶
➡️ 🔜 👀 📁 sql_app/models.py
.
✍ 🏒 🏷 👆 💽¶
🔜 ✍ 🏒 🏷 (🎓) User
& Item
.
👉 🎏 👆 🔜 🚥 👆 ⏩ 🏒 🔰 & ℹ 🏷 ✔️ 🎏 💽 🇸🇲 🔰.
Tip
🏒 ⚙️ ⚖ "🏷" 🔗 👉 🎓 & 👐 👈 🔗 ⏮️ 💽.
✋️ Pydantic ⚙️ ⚖ "🏷" 🔗 🕳 🎏, 💽 🔬, 🛠️, & 🧾 🎓 & 👐.
🗄 db
⚪️➡️ database
(📁 database.py
⚪️➡️ 🔛) & ⚙️ ⚫️ 📥.
import peewee
from .database import db
class User(peewee.Model):
email = peewee.CharField(unique=True, index=True)
hashed_password = peewee.CharField()
is_active = peewee.BooleanField(default=True)
class Meta:
database = db
class Item(peewee.Model):
title = peewee.CharField(index=True)
description = peewee.CharField(index=True)
owner = peewee.ForeignKeyField(User, backref="items")
class Meta:
database = db
Tip
🏒 ✍ 📚 🎱 🔢.
⚫️ 🔜 🔁 🚮 id
🔢 🔢 👑 🔑.
⚫️ 🔜 ⚒ 📛 🏓 ⚓️ 🔛 🎓 📛.
Item
, ⚫️ 🔜 ✍ 🔢 owner_id
⏮️ 🔢 🆔 User
. ✋️ 👥 🚫 📣 ⚫️ 🙆.
✍ Pydantic 🏷¶
🔜 ➡️ ✅ 📁 sql_app/schemas.py
.
Tip
❎ 😨 🖖 🏒 🏷 & Pydantic 🏷, 👥 🔜 ✔️ 📁 models.py
⏮️ 🏒 🏷, & 📁 schemas.py
⏮️ Pydantic 🏷.
👫 Pydantic 🏷 🔬 🌅 ⚖️ 🌘 "🔗" (☑ 📊 💠).
👉 🔜 ℹ 👥 ❎ 😨 ⏪ ⚙️ 👯♂️.
✍ Pydantic 🏷 / 🔗¶
✍ 🌐 🎏 Pydantic 🏷 🇸🇲 🔰:
from typing import Any, List, Union
import peewee
from pydantic import BaseModel
from pydantic.utils import GetterDict
class PeeweeGetterDict(GetterDict):
def get(self, key: Any, default: Any = None):
res = getattr(self._obj, key, default)
if isinstance(res, peewee.ModelSelect):
return list(res)
return res
class ItemBase(BaseModel):
title: str
description: Union[str, None] = None
class ItemCreate(ItemBase):
pass
class Item(ItemBase):
id: int
owner_id: int
class Config:
orm_mode = True
getter_dict = PeeweeGetterDict
class UserBase(BaseModel):
email: str
class UserCreate(UserBase):
password: str
class User(UserBase):
id: int
is_active: bool
items: List[Item] = []
class Config:
orm_mode = True
getter_dict = PeeweeGetterDict
Tip
📥 👥 🏗 🏷 ⏮️ id
.
👥 🚫 🎯 ✔ id
🔢 🏒 🏷, ✋️ 🏒 🚮 1️⃣ 🔁.
👥 ❎ 🎱 owner_id
🔢 Item
.
✍ PeeweeGetterDict
Pydantic 🏷 / 🔗¶
🕐❔ 👆 🔐 💛 🏒 🎚, 💖 some_user.items
, 🏒 🚫 🚚 list
Item
.
⚫️ 🚚 🎁 🛃 🎚 🎓 ModelSelect
.
⚫️ 💪 ✍ list
🚮 🏬 ⏮️ list(some_user.items)
.
✋️ 🎚 ⚫️ 🚫 list
. & ⚫️ 🚫 ☑ 🐍 🚂. ↩️ 👉, Pydantic 🚫 💭 🔢 ❔ 🗜 ⚫️ list
Pydantic 🏷 / 🔗.
✋️ ⏮️ ⏬ Pydantic ✔ 🚚 🛃 🎓 👈 😖 ⚪️➡️ pydantic.utils.GetterDict
, 🚚 🛠️ ⚙️ 🕐❔ ⚙️ orm_mode = True
🗃 💲 🐜 🏷 🔢.
👥 🔜 ✍ 🛃 PeeweeGetterDict
🎓 & ⚙️ ⚫️ 🌐 🎏 Pydantic 🏷 / 🔗 👈 ⚙️ orm_mode
:
from typing import Any, List, Union
import peewee
from pydantic import BaseModel
from pydantic.utils import GetterDict
class PeeweeGetterDict(GetterDict):
def get(self, key: Any, default: Any = None):
res = getattr(self._obj, key, default)
if isinstance(res, peewee.ModelSelect):
return list(res)
return res
class ItemBase(BaseModel):
title: str
description: Union[str, None] = None
class ItemCreate(ItemBase):
pass
class Item(ItemBase):
id: int
owner_id: int
class Config:
orm_mode = True
getter_dict = PeeweeGetterDict
class UserBase(BaseModel):
email: str
class UserCreate(UserBase):
password: str
class User(UserBase):
id: int
is_active: bool
items: List[Item] = []
class Config:
orm_mode = True
getter_dict = PeeweeGetterDict
📥 👥 ✅ 🚥 🔢 👈 ➖ 🔐 (✅ .items
some_user.items
) 👐 peewee.ModelSelect
.
& 🚥 👈 💼, 📨 list
⏮️ ⚫️.
& ⤴️ 👥 ⚙️ ⚫️ Pydantic 🏷 / 🔗 👈 ⚙️ orm_mode = True
, ⏮️ 📳 🔢 getter_dict = PeeweeGetterDict
.
Tip
👥 🕴 💪 ✍ 1️⃣ PeeweeGetterDict
🎓, & 👥 💪 ⚙️ ⚫️ 🌐 Pydantic 🏷 / 🔗.
💩 🇨🇻¶
🔜 ➡️ 👀 📁 sql_app/crud.py
.
✍ 🌐 💩 🇨🇻¶
✍ 🌐 🎏 💩 🇨🇻 🇸🇲 🔰, 🌐 📟 📶 🎏:
from . import models, schemas
def get_user(user_id: int):
return models.User.filter(models.User.id == user_id).first()
def get_user_by_email(email: str):
return models.User.filter(models.User.email == email).first()
def get_users(skip: int = 0, limit: int = 100):
return list(models.User.select().offset(skip).limit(limit))
def create_user(user: schemas.UserCreate):
fake_hashed_password = user.password + "notreallyhashed"
db_user = models.User(email=user.email, hashed_password=fake_hashed_password)
db_user.save()
return db_user
def get_items(skip: int = 0, limit: int = 100):
return list(models.Item.select().offset(skip).limit(limit))
def create_user_item(item: schemas.ItemCreate, user_id: int):
db_item = models.Item(**item.dict(), owner_id=user_id)
db_item.save()
return db_item
📤 🔺 ⏮️ 📟 🇸🇲 🔰.
👥 🚫 🚶♀️ db
🔢 🤭. ↩️ 👥 ⚙️ 🏷 🔗. 👉 ↩️ db
🎚 🌐 🎚, 👈 🔌 🌐 🔗 ⚛. 👈 ⚫️❔ 👥 ✔️ 🌐 contextvars
ℹ 🔛.
🆖, 🕐❔ 🛬 📚 🎚, 💖 get_users
, 👥 🔗 🤙 list
, 💖:
list(models.User.select())
👉 🎏 🤔 👈 👥 ✔️ ✍ 🛃 PeeweeGetterDict
. ✋️ 🛬 🕳 👈 ⏪ list
↩️ peewee.ModelSelect
response_model
➡ 🛠️ ⏮️ List[models.User]
(👈 👥 🔜 👀 ⏪) 🔜 👷 ☑.
👑 FastAPI 📱¶
& 🔜 📁 sql_app/main.py
➡️ 🛠️ & ⚙️ 🌐 🎏 🍕 👥 ✍ ⏭.
✍ 💽 🏓¶
📶 🙃 🌌 ✍ 💽 🏓:
import time
from typing import List
from fastapi import Depends, FastAPI, HTTPException
from . import crud, database, models, schemas
from .database import db_state_default
database.db.connect()
database.db.create_tables([models.User, models.Item])
database.db.close()
app = FastAPI()
sleep_time = 10
async def reset_db_state():
database.db._state._state.set(db_state_default.copy())
database.db._state.reset()
def get_db(db_state=Depends(reset_db_state)):
try:
database.db.connect()
yield
finally:
if not database.db.is_closed():
database.db.close()
@app.post("/users/", response_model=schemas.User, dependencies=[Depends(get_db)])
def create_user(user: schemas.UserCreate):
db_user = crud.get_user_by_email(email=user.email)
if db_user:
raise HTTPException(status_code=400, detail="Email already registered")
return crud.create_user(user=user)
@app.get("/users/", response_model=List[schemas.User], dependencies=[Depends(get_db)])
def read_users(skip: int = 0, limit: int = 100):
users = crud.get_users(skip=skip, limit=limit)
return users
@app.get(
"/users/{user_id}", response_model=schemas.User, dependencies=[Depends(get_db)]
)
def read_user(user_id: int):
db_user = crud.get_user(user_id=user_id)
if db_user is None:
raise HTTPException(status_code=404, detail="User not found")
return db_user
@app.post(
"/users/{user_id}/items/",
response_model=schemas.Item,
dependencies=[Depends(get_db)],
)
def create_item_for_user(user_id: int, item: schemas.ItemCreate):
return crud.create_user_item(item=item, user_id=user_id)
@app.get("/items/", response_model=List[schemas.Item], dependencies=[Depends(get_db)])
def read_items(skip: int = 0, limit: int = 100):
items = crud.get_items(skip=skip, limit=limit)
return items
@app.get(
"/slowusers/", response_model=List[schemas.User], dependencies=[Depends(get_db)]
)
def read_slow_users(skip: int = 0, limit: int = 100):
global sleep_time
sleep_time = max(0, sleep_time - 1)
time.sleep(sleep_time) # Fake long processing request
users = crud.get_users(skip=skip, limit=limit)
return users
✍ 🔗¶
✍ 🔗 👈 🔜 🔗 💽 ▶️️ ▶️ 📨 & 🔌 ⚫️ 🔚:
import time
from typing import List
from fastapi import Depends, FastAPI, HTTPException
from . import crud, database, models, schemas
from .database import db_state_default
database.db.connect()
database.db.create_tables([models.User, models.Item])
database.db.close()
app = FastAPI()
sleep_time = 10
async def reset_db_state():
database.db._state._state.set(db_state_default.copy())
database.db._state.reset()
def get_db(db_state=Depends(reset_db_state)):
try:
database.db.connect()
yield
finally:
if not database.db.is_closed():
database.db.close()
@app.post("/users/", response_model=schemas.User, dependencies=[Depends(get_db)])
def create_user(user: schemas.UserCreate):
db_user = crud.get_user_by_email(email=user.email)
if db_user:
raise HTTPException(status_code=400, detail="Email already registered")
return crud.create_user(user=user)
@app.get("/users/", response_model=List[schemas.User], dependencies=[Depends(get_db)])
def read_users(skip: int = 0, limit: int = 100):
users = crud.get_users(skip=skip, limit=limit)
return users
@app.get(
"/users/{user_id}", response_model=schemas.User, dependencies=[Depends(get_db)]
)
def read_user(user_id: int):
db_user = crud.get_user(user_id=user_id)
if db_user is None:
raise HTTPException(status_code=404, detail="User not found")
return db_user
@app.post(
"/users/{user_id}/items/",
response_model=schemas.Item,
dependencies=[Depends(get_db)],
)
def create_item_for_user(user_id: int, item: schemas.ItemCreate):
return crud.create_user_item(item=item, user_id=user_id)
@app.get("/items/", response_model=List[schemas.Item], dependencies=[Depends(get_db)])
def read_items(skip: int = 0, limit: int = 100):
items = crud.get_items(skip=skip, limit=limit)
return items
@app.get(
"/slowusers/", response_model=List[schemas.User], dependencies=[Depends(get_db)]
)
def read_slow_users(skip: int = 0, limit: int = 100):
global sleep_time
sleep_time = max(0, sleep_time - 1)
time.sleep(sleep_time) # Fake long processing request
users = crud.get_users(skip=skip, limit=limit)
return users
📥 👥 ✔️ 🛁 yield
↩️ 👥 🤙 🚫 ⚙️ 💽 🎚 🔗.
⚫️ 🔗 💽 & ♻ 🔗 💽 🔗 🔢 👈 🔬 🔠 📨 (⚙️ contextvars
🎱 ⚪️➡️ 🔛).
↩️ 💽 🔗 ⚠ 👤/🅾 🚧, 👉 🔗 ✍ ⏮️ 😐 def
🔢.
& ⤴️, 🔠 ➡ 🛠️ 🔢 👈 💪 🔐 💽 👥 🚮 ⚫️ 🔗.
✋️ 👥 🚫 ⚙️ 💲 👐 👉 🔗 (⚫️ 🤙 🚫 🤝 🙆 💲, ⚫️ ✔️ 🛁 yield
). , 👥 🚫 🚮 ⚫️ ➡ 🛠️ 🔢 ✋️ ➡ 🛠️ 👨🎨 dependencies
🔢:
import time
from typing import List
from fastapi import Depends, FastAPI, HTTPException
from . import crud, database, models, schemas
from .database import db_state_default
database.db.connect()
database.db.create_tables([models.User, models.Item])
database.db.close()
app = FastAPI()
sleep_time = 10
async def reset_db_state():
database.db._state._state.set(db_state_default.copy())
database.db._state.reset()
def get_db(db_state=Depends(reset_db_state)):
try:
database.db.connect()
yield
finally:
if not database.db.is_closed():
database.db.close()
@app.post("/users/", response_model=schemas.User, dependencies=[Depends(get_db)])
def create_user(user: schemas.UserCreate):
db_user = crud.get_user_by_email(email=user.email)
if db_user:
raise HTTPException(status_code=400, detail="Email already registered")
return crud.create_user(user=user)
@app.get("/users/", response_model=List[schemas.User], dependencies=[Depends(get_db)])
def read_users(skip: int = 0, limit: int = 100):
users = crud.get_users(skip=skip, limit=limit)
return users
@app.get(
"/users/{user_id}", response_model=schemas.User, dependencies=[Depends(get_db)]
)
def read_user(user_id: int):
db_user = crud.get_user(user_id=user_id)
if db_user is None:
raise HTTPException(status_code=404, detail="User not found")
return db_user
@app.post(
"/users/{user_id}/items/",
response_model=schemas.Item,
dependencies=[Depends(get_db)],
)
def create_item_for_user(user_id: int, item: schemas.ItemCreate):
return crud.create_user_item(item=item, user_id=user_id)
@app.get("/items/", response_model=List[schemas.Item], dependencies=[Depends(get_db)])
def read_items(skip: int = 0, limit: int = 100):
items = crud.get_items(skip=skip, limit=limit)
return items
@app.get(
"/slowusers/", response_model=List[schemas.User], dependencies=[Depends(get_db)]
)
def read_slow_users(skip: int = 0, limit: int = 100):
global sleep_time
sleep_time = max(0, sleep_time - 1)
time.sleep(sleep_time) # Fake long processing request
users = crud.get_users(skip=skip, limit=limit)
return users
🔑 🔢 🎧-🔗¶
🌐 contextvars
🍕 👷, 👥 💪 ⚒ 💭 👥 ✔️ 🔬 💲 ContextVar
🔠 📨 👈 ⚙️ 💽, & 👈 💲 🔜 ⚙️ 💽 🇵🇸 (🔗, 💵, ♒️) 🎂 📨.
👈, 👥 💪 ✍ ➕1️⃣ async
🔗 reset_db_state()
👈 ⚙️ 🎧-🔗 get_db()
. ⚫️ 🔜 ⚒ 💲 🔑 🔢 (⏮️ 🔢 dict
) 👈 🔜 ⚙️ 💽 🇵🇸 🎂 📨. & ⤴️ 🔗 get_db()
🔜 🏪 ⚫️ 💽 🇵🇸 (🔗, 💵, ♒️).
import time
from typing import List
from fastapi import Depends, FastAPI, HTTPException
from . import crud, database, models, schemas
from .database import db_state_default
database.db.connect()
database.db.create_tables([models.User, models.Item])
database.db.close()
app = FastAPI()
sleep_time = 10
async def reset_db_state():
database.db._state._state.set(db_state_default.copy())
database.db._state.reset()
def get_db(db_state=Depends(reset_db_state)):
try:
database.db.connect()
yield
finally:
if not database.db.is_closed():
database.db.close()
@app.post("/users/", response_model=schemas.User, dependencies=[Depends(get_db)])
def create_user(user: schemas.UserCreate):
db_user = crud.get_user_by_email(email=user.email)
if db_user:
raise HTTPException(status_code=400, detail="Email already registered")
return crud.create_user(user=user)
@app.get("/users/", response_model=List[schemas.User], dependencies=[Depends(get_db)])
def read_users(skip: int = 0, limit: int = 100):
users = crud.get_users(skip=skip, limit=limit)
return users
@app.get(
"/users/{user_id}", response_model=schemas.User, dependencies=[Depends(get_db)]
)
def read_user(user_id: int):
db_user = crud.get_user(user_id=user_id)
if db_user is None:
raise HTTPException(status_code=404, detail="User not found")
return db_user
@app.post(
"/users/{user_id}/items/",
response_model=schemas.Item,
dependencies=[Depends(get_db)],
)
def create_item_for_user(user_id: int, item: schemas.ItemCreate):
return crud.create_user_item(item=item, user_id=user_id)
@app.get("/items/", response_model=List[schemas.Item], dependencies=[Depends(get_db)])
def read_items(skip: int = 0, limit: int = 100):
items = crud.get_items(skip=skip, limit=limit)
return items
@app.get(
"/slowusers/", response_model=List[schemas.User], dependencies=[Depends(get_db)]
)
def read_slow_users(skip: int = 0, limit: int = 100):
global sleep_time
sleep_time = max(0, sleep_time - 1)
time.sleep(sleep_time) # Fake long processing request
users = crud.get_users(skip=skip, limit=limit)
return users
⏭ 📨, 👥 🔜 ⏲ 👈 🔑 🔢 🔄 async
🔗 reset_db_state()
& ⤴️ ✍ 🆕 🔗 get_db()
🔗, 👈 🆕 📨 🔜 ✔️ 🚮 👍 💽 🇵🇸 (🔗, 💵, ♒️).
Tip
FastAPI 🔁 🛠️, 1️⃣ 📨 💪 ▶️ ➖ 🛠️, & ⏭ 🏁, ➕1️⃣ 📨 💪 📨 & ▶️ 🏭 👍, & ⚫️ 🌐 💪 🛠️ 🎏 🧵.
✋️ 🔑 🔢 🤔 👫 🔁 ⚒,, 🏒 💽 🇵🇸 ⚒ async
🔗 reset_db_state()
🔜 🚧 🚮 👍 💽 🎂 🎂 📨.
& 🎏 🕰, 🎏 🛠️ 📨 🔜 ✔️ 🚮 👍 💽 🇵🇸 👈 🔜 🔬 🎂 📨.
🏒 🗳¶
🚥 👆 ⚙️ 🏒 🗳, ☑ 💽 db.obj
.
, 👆 🔜 ⏲ ⚫️ ⏮️:
async def reset_db_state():
database.db.obj._state._state.set(db_state_default.copy())
database.db.obj._state.reset()
✍ 👆 FastAPI ➡ 🛠️¶
🔜, 😒, 📥 🐩 FastAPI ➡ 🛠️ 📟.
import time
from typing import List
from fastapi import Depends, FastAPI, HTTPException
from . import crud, database, models, schemas
from .database import db_state_default
database.db.connect()
database.db.create_tables([models.User, models.Item])
database.db.close()
app = FastAPI()
sleep_time = 10
async def reset_db_state():
database.db._state._state.set(db_state_default.copy())
database.db._state.reset()
def get_db(db_state=Depends(reset_db_state)):
try:
database.db.connect()
yield
finally:
if not database.db.is_closed():
database.db.close()
@app.post("/users/", response_model=schemas.User, dependencies=[Depends(get_db)])
def create_user(user: schemas.UserCreate):
db_user = crud.get_user_by_email(email=user.email)
if db_user:
raise HTTPException(status_code=400, detail="Email already registered")
return crud.create_user(user=user)
@app.get("/users/", response_model=List[schemas.User], dependencies=[Depends(get_db)])
def read_users(skip: int = 0, limit: int = 100):
users = crud.get_users(skip=skip, limit=limit)
return users
@app.get(
"/users/{user_id}", response_model=schemas.User, dependencies=[Depends(get_db)]
)
def read_user(user_id: int):
db_user = crud.get_user(user_id=user_id)
if db_user is None:
raise HTTPException(status_code=404, detail="User not found")
return db_user
@app.post(
"/users/{user_id}/items/",
response_model=schemas.Item,
dependencies=[Depends(get_db)],
)
def create_item_for_user(user_id: int, item: schemas.ItemCreate):
return crud.create_user_item(item=item, user_id=user_id)
@app.get("/items/", response_model=List[schemas.Item], dependencies=[Depends(get_db)])
def read_items(skip: int = 0, limit: int = 100):
items = crud.get_items(skip=skip, limit=limit)
return items
@app.get(
"/slowusers/", response_model=List[schemas.User], dependencies=[Depends(get_db)]
)
def read_slow_users(skip: int = 0, limit: int = 100):
global sleep_time
sleep_time = max(0, sleep_time - 1)
time.sleep(sleep_time) # Fake long processing request
users = crud.get_users(skip=skip, limit=limit)
return users
🔃 def
🆚 async def
¶
🎏 ⏮️ 🇸🇲, 👥 🚫 🔨 🕳 💖:
user = await models.User.select().first()
...✋️ ↩️ 👥 ⚙️:
user = models.User.select().first()
, 🔄, 👥 🔜 📣 ➡ 🛠️ 🔢 & 🔗 🍵 async def
, ⏮️ 😐 def
,:
# Something goes here
def read_users(skip: int = 0, limit: int = 100):
# Something goes here
🔬 🏒 ⏮️ 🔁¶
👉 🖼 🔌 ➕ ➡ 🛠️ 👈 🔬 📏 🏭 📨 ⏮️ time.sleep(sleep_time)
.
⚫️ 🔜 ✔️ 💽 🔗 📂 ▶️ & 🔜 ⌛ 🥈 ⏭ 🙇 🔙. & 🔠 🆕 📨 🔜 ⌛ 🕐 🥈 🌘.
👉 🔜 💪 ➡️ 👆 💯 👈 👆 📱 ⏮️ 🏒 & FastAPI 🎭 ☑ ⏮️ 🌐 💩 🔃 🧵.
🚥 👆 💚 ✅ ❔ 🏒 🔜 💔 👆 📱 🚥 ⚙️ 🍵 🛠️, 🚶 sql_app/database.py
📁 & 🏤 ⏸:
# db._state = PeeweeConnectionState()
& 📁 sql_app/main.py
📁, 🏤 💪 async
🔗 reset_db_state()
& ❎ ⚫️ ⏮️ pass
:
async def reset_db_state():
# database.db._state._state.set(db_state_default.copy())
# database.db._state.reset()
pass
⤴️ 🏃 👆 📱 ⏮️ Uvicorn:
$ uvicorn sql_app.main:app --reload
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
📂 👆 🖥 http://127.0.0.1:8000/docs & ✍ 👩❤👨 👩💻.
⤴️ 📂 1️⃣0️⃣ 📑 http://127.0.0.1:8000/docs#/default/read_🐌👩💻_slowusers = 🎏 🕰.
🚶 ➡ 🛠️ "🤚 /slowusers/
" 🌐 📑. ⚙️ "🔄 ⚫️ 👅" 🔼 & 🛠️ 📨 🔠 📑, 1️⃣ ▶️️ ⏮️ 🎏.
📑 🔜 ⌛ 🍖 & ⤴️ 👫 🔜 🎦 Internal Server Error
.
⚫️❔ 🔨¶
🥇 📑 🔜 ⚒ 👆 📱 ✍ 🔗 💽 & ⌛ 🥈 ⏭ 🙇 🔙 & 📪 💽 🔗.
⤴️, 📨 ⏭ 📑, 👆 📱 🔜 ⌛ 🕐 🥈 🌘, & 🔛.
👉 ⛓ 👈 ⚫️ 🔜 🔚 🆙 🏁 🏁 📑' 📨 ⏪ 🌘 ⏮️ 🕐.
⤴️ 1️⃣ 🏁 📨 👈 ⌛ 🌘 🥈 🔜 🔄 📂 💽 🔗, ✋️ 1️⃣ 📚 ⏮️ 📨 🎏 📑 🔜 🎲 🍵 🎏 🧵 🥇 🕐, ⚫️ 🔜 ✔️ 🎏 💽 🔗 👈 ⏪ 📂, & 🏒 🔜 🚮 ❌ & 👆 🔜 👀 ⚫️ 📶, & 📨 🔜 ✔️ Internal Server Error
.
👉 🔜 🎲 🔨 🌅 🌘 1️⃣ 📚 📑.
🚥 👆 ✔️ 💗 👩💻 💬 👆 📱 ⚫️❔ 🎏 🕰, 👉 ⚫️❔ 💪 🔨.
& 👆 📱 ▶️ 🍵 🌅 & 🌖 👩💻 🎏 🕰, ⌛ 🕰 👁 📨 💪 📏 & 📏 ⏲ ❌.
🔧 🏒 ⏮️ FastAPI¶
🔜 🚶 🔙 📁 sql_app/database.py
, & ✍ ⏸:
db._state = PeeweeConnectionState()
& 📁 sql_app/main.py
📁, ✍ 💪 async
🔗 reset_db_state()
:
async def reset_db_state():
database.db._state._state.set(db_state_default.copy())
database.db._state.reset()
❎ 👆 🏃♂ 📱 & ▶️ ⚫️ 🔄.
🔁 🎏 🛠️ ⏮️ 1️⃣0️⃣ 📑. 👉 🕰 🌐 👫 🔜 ⌛ & 👆 🔜 🤚 🌐 🏁 🍵 ❌.
...👆 🔧 ⚫️ ❗
📄 🌐 📁¶
💭 👆 🔜 ✔️ 📁 📛 my_super_project
(⚖️ 👐 👆 💚) 👈 🔌 🎧-📁 🤙 sql_app
.
sql_app
🔜 ✔️ 📄 📁:
-
sql_app/__init__.py
: 🛁 📁. -
sql_app/database.py
:
from contextvars import ContextVar
import peewee
DATABASE_NAME = "test.db"
db_state_default = {"closed": None, "conn": None, "ctx": None, "transactions": None}
db_state = ContextVar("db_state", default=db_state_default.copy())
class PeeweeConnectionState(peewee._ConnectionState):
def __init__(self, **kwargs):
super().__setattr__("_state", db_state)
super().__init__(**kwargs)
def __setattr__(self, name, value):
self._state.get()[name] = value
def __getattr__(self, name):
return self._state.get()[name]
db = peewee.SqliteDatabase(DATABASE_NAME, check_same_thread=False)
db._state = PeeweeConnectionState()
sql_app/models.py
:
import peewee
from .database import db
class User(peewee.Model):
email = peewee.CharField(unique=True, index=True)
hashed_password = peewee.CharField()
is_active = peewee.BooleanField(default=True)
class Meta:
database = db
class Item(peewee.Model):
title = peewee.CharField(index=True)
description = peewee.CharField(index=True)
owner = peewee.ForeignKeyField(User, backref="items")
class Meta:
database = db
sql_app/schemas.py
:
from typing import Any, List, Union
import peewee
from pydantic import BaseModel
from pydantic.utils import GetterDict
class PeeweeGetterDict(GetterDict):
def get(self, key: Any, default: Any = None):
res = getattr(self._obj, key, default)
if isinstance(res, peewee.ModelSelect):
return list(res)
return res
class ItemBase(BaseModel):
title: str
description: Union[str, None] = None
class ItemCreate(ItemBase):
pass
class Item(ItemBase):
id: int
owner_id: int
class Config:
orm_mode = True
getter_dict = PeeweeGetterDict
class UserBase(BaseModel):
email: str
class UserCreate(UserBase):
password: str
class User(UserBase):
id: int
is_active: bool
items: List[Item] = []
class Config:
orm_mode = True
getter_dict = PeeweeGetterDict
sql_app/crud.py
:
from . import models, schemas
def get_user(user_id: int):
return models.User.filter(models.User.id == user_id).first()
def get_user_by_email(email: str):
return models.User.filter(models.User.email == email).first()
def get_users(skip: int = 0, limit: int = 100):
return list(models.User.select().offset(skip).limit(limit))
def create_user(user: schemas.UserCreate):
fake_hashed_password = user.password + "notreallyhashed"
db_user = models.User(email=user.email, hashed_password=fake_hashed_password)
db_user.save()
return db_user
def get_items(skip: int = 0, limit: int = 100):
return list(models.Item.select().offset(skip).limit(limit))
def create_user_item(item: schemas.ItemCreate, user_id: int):
db_item = models.Item(**item.dict(), owner_id=user_id)
db_item.save()
return db_item
sql_app/main.py
:
import time
from typing import List
from fastapi import Depends, FastAPI, HTTPException
from . import crud, database, models, schemas
from .database import db_state_default
database.db.connect()
database.db.create_tables([models.User, models.Item])
database.db.close()
app = FastAPI()
sleep_time = 10
async def reset_db_state():
database.db._state._state.set(db_state_default.copy())
database.db._state.reset()
def get_db(db_state=Depends(reset_db_state)):
try:
database.db.connect()
yield
finally:
if not database.db.is_closed():
database.db.close()
@app.post("/users/", response_model=schemas.User, dependencies=[Depends(get_db)])
def create_user(user: schemas.UserCreate):
db_user = crud.get_user_by_email(email=user.email)
if db_user:
raise HTTPException(status_code=400, detail="Email already registered")
return crud.create_user(user=user)
@app.get("/users/", response_model=List[schemas.User], dependencies=[Depends(get_db)])
def read_users(skip: int = 0, limit: int = 100):
users = crud.get_users(skip=skip, limit=limit)
return users
@app.get(
"/users/{user_id}", response_model=schemas.User, dependencies=[Depends(get_db)]
)
def read_user(user_id: int):
db_user = crud.get_user(user_id=user_id)
if db_user is None:
raise HTTPException(status_code=404, detail="User not found")
return db_user
@app.post(
"/users/{user_id}/items/",
response_model=schemas.Item,
dependencies=[Depends(get_db)],
)
def create_item_for_user(user_id: int, item: schemas.ItemCreate):
return crud.create_user_item(item=item, user_id=user_id)
@app.get("/items/", response_model=List[schemas.Item], dependencies=[Depends(get_db)])
def read_items(skip: int = 0, limit: int = 100):
items = crud.get_items(skip=skip, limit=limit)
return items
@app.get(
"/slowusers/", response_model=List[schemas.User], dependencies=[Depends(get_db)]
)
def read_slow_users(skip: int = 0, limit: int = 100):
global sleep_time
sleep_time = max(0, sleep_time - 1)
time.sleep(sleep_time) # Fake long processing request
users = crud.get_users(skip=skip, limit=limit)
return users
📡 ℹ¶
Warning
👉 📶 📡 ℹ 👈 👆 🎲 🚫 💪.
⚠¶
🏒 ⚙️ threading.local
🔢 🏪 ⚫️ 💽 "🇵🇸" 💽 (🔗, 💵, ♒️).
threading.local
✍ 💲 🌟 ⏮️ 🧵, ✋️ 🔁 🛠️ 🔜 🏃 🌐 📟 (✅ 🔠 📨) 🎏 🧵, & 🎲 🚫 ✔.
🔛 🔝 👈, 🔁 🛠️ 💪 🏃 🔁 📟 🧵 (⚙️ asyncio.run_in_executor
), ✋️ 🔗 🎏 📨.
👉 ⛓ 👈, ⏮️ 🏒 ⏮️ 🛠️, 💗 📋 💪 ⚙️ 🎏 threading.local
🔢 & 🔚 🆙 🤝 🎏 🔗 & 💽 (👈 👫 🚫🔜 🚫), & 🎏 🕰, 🚥 👫 🛠️ 🔁 👤/🅾-🚧 📟 🧵 (⏮️ 😐 def
🔢 FastAPI, ➡ 🛠️ & 🔗), 👈 📟 🏆 🚫 ✔️ 🔐 💽 🇵🇸 🔢, ⏪ ⚫️ 🍕 🎏 📨 & ⚫️ 🔜 💪 🤚 🔐 🎏 💽 🇵🇸.
🔑 🔢¶
🐍 3️⃣.7️⃣ ✔️ contextvars
👈 💪 ✍ 🇧🇿 🔢 📶 🎏 threading.local
, ✋️ 🔗 👫 🔁 ⚒.
📤 📚 👜 ✔️ 🤯.
ContextVar
✔️ ✍ 🔝 🕹, 💖:
some_var = ContextVar("some_var", default="default value")
⚒ 💲 ⚙️ ⏮️ "🔑" (✅ ⏮️ 📨) ⚙️:
some_var.set("new value")
🤚 💲 🙆 🔘 🔑 (✅ 🙆 🍕 🚚 ⏮️ 📨) ⚙️:
some_var.get()
⚒ 🔑 🔢 async
🔗 reset_db_state()
¶
🚥 🍕 🔁 📟 ⚒ 💲 ⏮️ some_var.set("updated in function")
(✅ 💖 async
🔗), 🎂 📟 ⚫️ & 📟 👈 🚶 ⏮️ (✅ 📟 🔘 async
🔢 🤙 ⏮️ await
) 🔜 👀 👈 🆕 💲.
, 👆 💼, 🚥 👥 ⚒ 🏒 🇵🇸 🔢 (⏮️ 🔢 dict
) async
🔗, 🌐 🎂 🔗 📟 👆 📱 🔜 👀 👉 💲 & 🔜 💪 ♻ ⚫️ 🎂 📨.
& 🔑 🔢 🔜 ⚒ 🔄 ⏭ 📨, 🚥 👫 🛠️.
⚒ 💽 🇵🇸 🔗 get_db()
¶
get_db()
😐 def
🔢, FastAPI 🔜 ⚒ ⚫️ 🏃 🧵, ⏮️ 📁 "🔑", 🧑🤝🧑 🎏 💲 🔑 🔢 ( dict
⏮️ ⏲ 💽 🇵🇸). ⤴️ ⚫️ 💪 🚮 💽 🇵🇸 👈 dict
, 💖 🔗, ♒️.
✋️ 🚥 💲 🔑 🔢 (🔢 dict
) ⚒ 👈 😐 def
🔢, ⚫️ 🔜 ✍ 🆕 💲 👈 🔜 🚧 🕴 👈 🧵 🧵, & 🎂 📟 (💖 ➡ 🛠️ 🔢) 🚫🔜 ✔️ 🔐 ⚫️. get_db()
👥 💪 🕴 ⚒ 💲 dict
, ✋️ 🚫 🎂 dict
⚫️.
, 👥 💪 ✔️ async
🔗 reset_db_state()
⚒ dict
🔑 🔢. 👈 🌌, 🌐 📟 ✔️ 🔐 🎏 dict
💽 🇵🇸 👁 📨.
🔗 & 🔌 🔗 get_db()
¶
⤴️ ⏭ ❔ 🔜, ⚫️❔ 🚫 🔗 & 🔌 💽 async
🔗 ⚫️, ↩️ get_db()
❓
async
🔗 ✔️ async
🔑 🔢 🛡 🎂 📨, ✋️ 🏗 & 📪 💽 🔗 ⚠ 🚧, ⚫️ 💪 📉 🎭 🚥 ⚫️ 📤.
👥 💪 😐 def
🔗 get_db()
.