Skip to content

๐Ÿ—„ (๐Ÿ”—) ๐Ÿ’ฝ โฎ๏ธ ๐Ÿ’

Warning

๐Ÿšฅ ๐Ÿ‘† โ–ถ๏ธ, ๐Ÿ”ฐ ๐Ÿ—„ (๐Ÿ”—) ๐Ÿ’ฝ ๐Ÿ‘ˆ โš™๏ธ ๐Ÿ‡ธ๐Ÿ‡ฒ ๐Ÿ”œ ๐Ÿฅƒ.

๐Ÿ’ญ ๐Ÿ†“ ๐Ÿšถ ๐Ÿ‘‰.

๐Ÿšฅ ๐Ÿ‘† โ–ถ๏ธ ๐Ÿ— โšช๏ธโžก๏ธ ๐Ÿ–Œ, ๐Ÿ‘† ๐ŸŽฒ ๐Ÿ‘ป ๐Ÿ“† โฎ๏ธ ๐Ÿ‡ธ๐Ÿ‡ฒ ๐Ÿœ (๐Ÿ—„ (๐Ÿ”—) ๐Ÿ’ฝ), โš–๏ธ ๐Ÿ™† ๐ŸŽ ๐Ÿ” ๐Ÿœ.

๐Ÿšฅ ๐Ÿ‘† โช โœ”๏ธ ๐Ÿ“Ÿ ๐Ÿงข ๐Ÿ‘ˆ โš™๏ธ ๐Ÿ’ ๐Ÿœ, ๐Ÿ‘† ๐Ÿ’ช โœ… ๐Ÿ“ฅ โ” โš™๏ธ โšซ๏ธ โฎ๏ธ 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().