Skip to content

Commit 23aa724

Browse files
committed
afeat: add app code
1 parent b782a8c commit 23aa724

File tree

17 files changed

+461
-0
lines changed

17 files changed

+461
-0
lines changed

app/__init__.py

Whitespace-only changes.

app/api/user_api.py

Whitespace-only changes.

app/conf/__init__.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import os
2+
from pathlib import Path
3+
4+
from dynaconf import Dynaconf
5+
6+
_BASE_DIR = Path(__file__).parent.parent
7+
8+
# settings.local.toml for local test
9+
# priority: settings.local.toml < settings.toml
10+
#
11+
12+
settings_files = [
13+
Path(__file__).parent / "settings.toml",
14+
] # set absolute path
15+
16+
settings = Dynaconf(
17+
settings_files=settings_files,
18+
load_dotenv=True, # load .env
19+
lowercase_read=True, #
20+
# /app/conf/settings.toml
21+
includes=[os.path.join("/app", "conf", "settings.toml")],
22+
base_dir=_BASE_DIR,
23+
)

app/conf/settings.local.toml

Whitespace-only changes.

app/conf/settings.toml

Whitespace-only changes.

app/database/__init__.py

Whitespace-only changes.

app/database/async/__init__.py

Whitespace-only changes.
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
"""
2+
Base DAOs
3+
"""
4+
5+
import os
6+
from abc import ABC
7+
from typing import Any
8+
from urllib.parse import quote_plus
9+
10+
from loguru import logger
11+
from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, create_async_engine
12+
from sqlalchemy.orm import sessionmaker
13+
14+
15+
class BaseDao(ABC):
16+
"""Interface for base db DAOs table"""
17+
18+
def __init__(
19+
self,
20+
db_dsn: str,
21+
enable_repeatable_read: bool = True,
22+
) -> None:
23+
"""
24+
Init DAOs
25+
"""
26+
self.async_engine = create_async_db_engine(db_dsn, enable_repeatable_read)
27+
self.async_session = sessionmaker(
28+
bind=self.async_engine,
29+
autoflush=False,
30+
class_=AsyncSession,
31+
expire_on_commit=False,
32+
)
33+
self.db_session: AsyncSession
34+
35+
def get_async_session(self) -> AsyncSession:
36+
"""get session"""
37+
return self.async_session()
38+
39+
def __enter__(self) -> AsyncSession:
40+
"""enter"""
41+
self.db_session = self.get_async_session()
42+
return self.db_session
43+
44+
def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
45+
"""close"""
46+
self.db_session.close()
47+
48+
49+
def get_db_dsn() -> str:
50+
"""get"""
51+
user = os.environ.get("MYSQL_USERNAME", "root")
52+
passwd = os.environ.get("MYSQL_PASSWORD", "root")
53+
database_name = os.environ.get("MYSQL_DATABASE", "app_db_name")
54+
address = os.environ.get("MYSQL_ADDRESS", "localhost:3306")
55+
return _get_db_dsn_str(user, passwd, address, database_name)
56+
57+
58+
def _get_db_dsn_str(
59+
user: str,
60+
passwd: str,
61+
address: str,
62+
database_name: str,
63+
passwd_url_encod: bool = True,
64+
) -> str:
65+
"""
66+
get db dsn
67+
"""
68+
if passwd_url_encod:
69+
passwd = quote_plus(passwd)
70+
# url: dialect[+driver]://user:password@host/dbname[?key=value..]
71+
return f"mysql+aiomysql://{user}:{passwd}@{address}/{database_name}?charset=utf8mb4"
72+
73+
74+
def create_async_db_engine(
75+
db_url: str,
76+
enable_repeatable_read: bool = True,
77+
) -> AsyncEngine:
78+
"""create async db engine"""
79+
logger.debug("connect to {}", db_url)
80+
if enable_repeatable_read:
81+
async_engine = create_async_engine(
82+
url=db_url,
83+
isolation_level="REPEATABLE READ", # set isolation as RR
84+
echo=True,
85+
future=True,
86+
pool_size=5,
87+
pool_recycle=3600,
88+
)
89+
else:
90+
async_engine = create_async_engine(
91+
url=db_url,
92+
echo=True,
93+
future=True,
94+
pool_size=5,
95+
pool_recycle=3600,
96+
)
97+
return async_engine

app/database/async/db_op.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
from typing import Optional
2+
3+
from sqlalchemy import select, update
4+
from sqlalchemy.exc import SQLAlchemyError
5+
6+
from app.database.base import BaseDao, get_db_dsn
7+
from app.database.models.users import UserModel
8+
9+
10+
class SQLDAO(BaseDao):
11+
def __init__(self):
12+
"""init"""
13+
super().__init__(db_dsn=get_db_dsn())
14+
15+
async def add_user(
16+
self,
17+
username: str,
18+
password: str,
19+
) -> None:
20+
"""
21+
add new user
22+
"""
23+
db_user = UserModel(username, password)
24+
async with self.get_async_session() as session:
25+
async with session.begin():
26+
try:
27+
session.add(db_user)
28+
await session.flush() # refresh own primary key
29+
session.expunge(db_user) # release data
30+
# begin() context will auto commit trancastion
31+
except SQLAlchemyError as sql_ex:
32+
raise sql_ex
33+
return db_user

app/database/models/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)