mirror of
https://github.com/fastapi-practices/fastapi_best_architecture.git
synced 2025-08-26 13:26:04 +08:00
update to python3.10 (#29)
This commit is contained in:
@ -18,10 +18,10 @@ repos:
|
||||
rev: 23.3.0
|
||||
hooks:
|
||||
- id: black
|
||||
language_version: python3.8
|
||||
language_version: python3.10
|
||||
args:
|
||||
- '--skip-string-normalization'
|
||||
- '--line-length'
|
||||
- '120'
|
||||
- '--target-version'
|
||||
- 'py38'
|
||||
- 'py310'
|
||||
|
12
.ruff.toml
12
.ruff.toml
@ -9,11 +9,14 @@ select = [
|
||||
"PGH004",
|
||||
"PLE1142",
|
||||
"RUF100",
|
||||
"I002",
|
||||
"F404",
|
||||
"TCH",
|
||||
"UP007"
|
||||
]
|
||||
ignore = ["F401"]
|
||||
line-length = 120
|
||||
format = "grouped"
|
||||
target-version = "py38"
|
||||
target-version = "py310"
|
||||
cache-dir = "./.ruff_cache"
|
||||
|
||||
[flake8-pytest-style]
|
||||
@ -34,3 +37,8 @@ ignore-variadic-names = true
|
||||
[isort]
|
||||
lines-between-types = 1
|
||||
order-by-type = true
|
||||
|
||||
[per-file-ignores]
|
||||
"backend/app/api/v1/*.py" = ["TCH"]
|
||||
"backend/app/models/*.py" = ["TCH003"]
|
||||
"backend/app/**/__init__.py" = ["F401"]
|
||||
|
@ -5,6 +5,8 @@ This is a base project of the FastAPI framework.
|
||||
It‘s purpose is to allow you to develop your project directly with it
|
||||
as your base project
|
||||
|
||||
Support python3.10 and above
|
||||
|
||||
## Skill
|
||||
|
||||
- [x] FastAPI
|
||||
|
@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Any, Union
|
||||
from typing import Any
|
||||
|
||||
from fastapi import Depends
|
||||
from fastapi.security import OAuth2PasswordBearer
|
||||
@ -42,7 +42,7 @@ def password_verify(plain_password: str, hashed_password: str) -> bool:
|
||||
return pwd_context.verify(plain_password, hashed_password)
|
||||
|
||||
|
||||
def create_access_token(data: Union[int, Any], expires_delta: Union[timedelta, None] = None) -> str:
|
||||
def create_access_token(data: int | Any, expires_delta: timedelta | None = None) -> str:
|
||||
"""
|
||||
Generate encryption token
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
from typing import TypeVar, Generic, Sequence, Dict, Union
|
||||
from typing import TypeVar, Generic, Sequence, Dict
|
||||
|
||||
from fastapi import Query
|
||||
from fastapi_pagination.bases import AbstractPage, AbstractParams, RawParams
|
||||
@ -35,7 +35,7 @@ class Page(AbstractPage[T], Generic[T]):
|
||||
page: int # 第n页
|
||||
size: int # 每页数量
|
||||
total_pages: int # 总页数
|
||||
links: Dict[str, Union[str, None]] # 跳转链接
|
||||
links: Dict[str, str | None] # 跳转链接
|
||||
|
||||
__params_type__ = Params # 使用自定义的Params
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
from datetime import datetime
|
||||
from typing import Optional, Any, Union, Set, Dict
|
||||
from typing import Any, Union, Set, Dict
|
||||
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from pydantic import validate_arguments, BaseModel
|
||||
|
||||
_JsonEncoder = Union[Set[Union[int, str]], Dict[Union[int, str], Any]]
|
||||
_JsonEncoder = Union[Set[int | str], Dict[int | str, Any]]
|
||||
|
||||
__all__ = ['ResponseModel', 'response_base']
|
||||
|
||||
@ -18,7 +18,7 @@ class ResponseModel(BaseModel):
|
||||
|
||||
code: int = 200
|
||||
msg: str = 'Success'
|
||||
data: Optional[Any] = None
|
||||
data: Any | None = None
|
||||
|
||||
class Config:
|
||||
json_encoders = {datetime: lambda x: x.strftime('%Y-%m-%d %H:%M:%S')}
|
||||
@ -31,9 +31,7 @@ class ResponseBase:
|
||||
|
||||
@staticmethod
|
||||
@validate_arguments
|
||||
def success(
|
||||
*, code: int = 200, msg: str = 'Success', data: Optional[Any] = None, exclude: Optional[_JsonEncoder] = None
|
||||
):
|
||||
def success(*, code: int = 200, msg: str = 'Success', data: Any | None = None, exclude: _JsonEncoder | None = None):
|
||||
"""
|
||||
请求成功返回通用方法
|
||||
|
||||
@ -48,13 +46,13 @@ class ResponseBase:
|
||||
|
||||
@staticmethod
|
||||
@validate_arguments
|
||||
def fail(*, code: int = 400, msg: str = 'Bad Request', data: Any = None, exclude: Optional[_JsonEncoder] = None):
|
||||
def fail(*, code: int = 400, msg: str = 'Bad Request', data: Any = None, exclude: _JsonEncoder | None = None):
|
||||
data = data if data is None else ResponseBase.__encode_json(data)
|
||||
return ResponseModel(code=code, msg=msg, data=data).dict(exclude={'data': exclude})
|
||||
|
||||
@staticmethod
|
||||
@validate_arguments
|
||||
def response_200(*, msg: str = 'Success', data: Optional[Any] = None, exclude: Optional[_JsonEncoder] = None):
|
||||
def response_200(*, msg: str = 'Success', data: Any | None = None, exclude: _JsonEncoder | None = None):
|
||||
data = data if data is None else ResponseBase.__encode_json(data)
|
||||
return ResponseModel(code=200, msg=msg, data=data).dict(exclude={'data': exclude})
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
from functools import lru_cache
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseSettings, root_validator
|
||||
|
||||
@ -35,9 +34,9 @@ class Settings(BaseSettings):
|
||||
TITLE: str = 'FastAPI'
|
||||
VERSION: str = '0.0.1'
|
||||
DESCRIPTION: str = 'FastAPI Best Architecture'
|
||||
DOCS_URL: Optional[str] = '/v1/docs'
|
||||
REDOCS_URL: Optional[str] = '/v1/redocs'
|
||||
OPENAPI_URL: Optional[str] = '/v1/openapi'
|
||||
DOCS_URL: str | None = '/v1/docs'
|
||||
REDOCS_URL: str | None = '/v1/redocs'
|
||||
OPENAPI_URL: str | None = '/v1/openapi'
|
||||
|
||||
@root_validator
|
||||
def validator_api_url(cls, values):
|
||||
|
@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
from typing import Any, Dict, Generic, Type, TypeVar, Union, Optional, NoReturn
|
||||
from typing import Any, Dict, Generic, Type, TypeVar, NoReturn
|
||||
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy import select, update, delete
|
||||
@ -17,7 +17,7 @@ class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]):
|
||||
def __init__(self, model: Type[ModelType]):
|
||||
self.model = model
|
||||
|
||||
async def get(self, db: AsyncSession, pk: int) -> Optional[ModelType]:
|
||||
async def get(self, db: AsyncSession, pk: int) -> ModelType | None:
|
||||
"""
|
||||
通过主键 id 获取一条数据
|
||||
|
||||
@ -28,7 +28,7 @@ class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]):
|
||||
model = await db.execute(select(self.model).where(self.model.id == pk))
|
||||
return model.scalars().first()
|
||||
|
||||
async def create(self, db: AsyncSession, obj_in: CreateSchemaType, user_id: Optional[int] = None) -> NoReturn:
|
||||
async def create(self, db: AsyncSession, obj_in: CreateSchemaType, user_id: int | None = None) -> NoReturn:
|
||||
"""
|
||||
新增一条数据
|
||||
|
||||
@ -44,7 +44,7 @@ class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]):
|
||||
db.add(db_obj)
|
||||
|
||||
async def update(
|
||||
self, db: AsyncSession, pk: int, obj_in: Union[UpdateSchemaType, Dict[str, Any]], user_id: Optional[int] = None
|
||||
self, db: AsyncSession, pk: int, obj_in: UpdateSchemaType | Dict[str, Any], user_id: int | None = None
|
||||
) -> int:
|
||||
"""
|
||||
通过主键 id 更新一条数据
|
||||
|
@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
from typing import Optional, NoReturn
|
||||
from typing import NoReturn
|
||||
|
||||
from sqlalchemy import func, select, update, desc
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
@ -13,10 +13,10 @@ from backend.app.schemas.user import CreateUser, UpdateUser, Avatar
|
||||
|
||||
|
||||
class CRUDUser(CRUDBase[User, CreateUser, UpdateUser]):
|
||||
async def get_user_by_id(self, db: AsyncSession, user_id: int) -> Optional[User]:
|
||||
async def get_user_by_id(self, db: AsyncSession, user_id: int) -> User | None:
|
||||
return await self.get(db, user_id)
|
||||
|
||||
async def get_user_by_username(self, db: AsyncSession, username: str) -> Optional[User]:
|
||||
async def get_user_by_username(self, db: AsyncSession, username: str) -> User | None:
|
||||
user = await db.execute(select(self.model).where(self.model.username == username))
|
||||
return user.scalars().first()
|
||||
|
||||
|
@ -2,7 +2,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from sqlalchemy import func
|
||||
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, declared_attr, MappedAsDataclass
|
||||
@ -24,9 +23,9 @@ class _BaseMixin(MappedAsDataclass):
|
||||
"""
|
||||
|
||||
create_user: Mapped[int] = mapped_column(sort_order=9999, comment='创建者')
|
||||
update_user: Mapped[Optional[int]] = mapped_column(init=False, default=None, sort_order=9999, comment='修改者')
|
||||
update_user: Mapped[int | None] = mapped_column(init=False, default=None, sort_order=9999, comment='修改者')
|
||||
created_time: Mapped[datetime] = mapped_column(init=False, default=func.now(), sort_order=9999, comment='创建时间')
|
||||
updated_time: Mapped[Optional[datetime]] = mapped_column(
|
||||
updated_time: Mapped[datetime | None] = mapped_column(
|
||||
init=False, onupdate=func.now(), sort_order=9999, comment='更新时间'
|
||||
)
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from sqlalchemy import func, String
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
@ -21,7 +20,7 @@ class User(DataClassBase):
|
||||
email: Mapped[str] = mapped_column(String(50), unique=True, index=True, comment='邮箱')
|
||||
is_superuser: Mapped[bool] = mapped_column(default=False, comment='超级权限')
|
||||
is_active: Mapped[bool] = mapped_column(default=True, comment='用户账号状态')
|
||||
avatar: Mapped[Optional[str]] = mapped_column(String(255), default=None, comment='头像')
|
||||
mobile_number: Mapped[Optional[str]] = mapped_column(String(11), default=None, comment='手机号')
|
||||
avatar: Mapped[str | None] = mapped_column(String(255), default=None, comment='头像')
|
||||
mobile_number: Mapped[str | None] = mapped_column(String(11), default=None, comment='手机号')
|
||||
time_joined: Mapped[datetime] = mapped_column(init=False, default=func.now(), comment='注册时间')
|
||||
last_login: Mapped[Optional[datetime]] = mapped_column(init=False, onupdate=func.now(), comment='上次登录')
|
||||
last_login: Mapped[datetime | None] = mapped_column(init=False, onupdate=func.now(), comment='上次登录')
|
||||
|
@ -1,6 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
@ -10,4 +9,4 @@ class Token(BaseModel):
|
||||
msg: str = 'Success'
|
||||
access_token: str
|
||||
token_type: str = 'Bearer'
|
||||
is_superuser: Optional[bool] = None
|
||||
is_superuser: bool | None = None
|
||||
|
@ -1,7 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import datetime
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel, Field, HttpUrl
|
||||
|
||||
@ -18,7 +17,7 @@ class CreateUser(Auth):
|
||||
class UpdateUser(BaseModel):
|
||||
username: str
|
||||
email: str
|
||||
mobile_number: Optional[str] = None
|
||||
mobile_number: str | None = None
|
||||
|
||||
|
||||
class Avatar(BaseModel):
|
||||
@ -28,9 +27,9 @@ class Avatar(BaseModel):
|
||||
class GetUserInfo(UpdateUser):
|
||||
id: int
|
||||
uid: str
|
||||
avatar: Optional[str] = None
|
||||
avatar: str | None = None
|
||||
time_joined: datetime.datetime = None
|
||||
last_login: Optional[datetime.datetime] = None
|
||||
last_login: datetime.datetime | None = None
|
||||
is_superuser: bool
|
||||
is_active: bool
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
from functools import lru_cache
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseSettings
|
||||
|
||||
@ -11,9 +10,9 @@ class Settings(BaseSettings):
|
||||
TITLE: str = 'FastAPI'
|
||||
VERSION: str = 'v0.0.1'
|
||||
DESCRIPTION: str = 'FastAPI Best Architecture'
|
||||
DOCS_URL: Optional[str] = '/v1/docs'
|
||||
REDOCS_URL: Optional[str] = None
|
||||
OPENAPI_URL: Optional[str] = '/v1/openapi'
|
||||
DOCS_URL: str | None = '/v1/docs'
|
||||
REDOCS_URL: str | None = None
|
||||
OPENAPI_URL: str | None = '/v1/openapi'
|
||||
|
||||
# Static Server
|
||||
STATIC_FILES: bool = False
|
||||
|
Reference in New Issue
Block a user