Skip to content

Commit 80f17af

Browse files
committed
feat(dto): update dto
1 parent 579370d commit 80f17af

File tree

5 files changed

+115
-122
lines changed

5 files changed

+115
-122
lines changed

src/_types.py

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import pydantic
66
from fastapi import Query
7-
from pydantic import ConfigDict, Field, constr
7+
from pydantic import ConfigDict, Field, StringConstraints
88
from pydantic.functional_validators import BeforeValidator
99

1010
from src.validators import items_to_list, mac_address_validator
@@ -18,8 +18,8 @@
1818
StrList = Annotated[str | list[str], BeforeValidator(items_to_list)]
1919
IntList = Annotated[int | list[int], BeforeValidator(items_to_list)]
2020
MacAddress = Annotated[str, BeforeValidator(mac_address_validator)]
21-
NameStr = constr(pattern="^[a-zA-Z0-9_-].$", max_length=50)
22-
NameChineseStr = constr(pattern="^[\u4e00-\u9fa5a-zA-Z0-9_-].$", max_length=50)
21+
NameStr = Annotated[str, StringConstraints(pattern="^[a-zA-Z0-9_-].$", max_length=50)]
22+
NameChineseStr = Annotated[str, StringConstraints(pattern="^[\u4e00-\u9fa5a-zA-Z0-9_-].$", max_length=50)]
2323

2424

2525
class BaseModel(pydantic.BaseModel):
@@ -44,9 +44,18 @@ class AuditUser(BaseModel):
4444
updated_by: AuditUserBase | None = None
4545

4646

47+
class AuditLog(BaseModel):
48+
id: int
49+
created_at: datetime
50+
request_id: str
51+
action: str
52+
diff: dict | None = None
53+
user: AuditUserBase | None = None
54+
55+
4756
class ListT(BaseModel, Generic[T]):
4857
count: int
49-
results: T | None = None
58+
results: list[T] | None = None
5059

5160

5261
class AppStrEnum(str, Enum):
@@ -70,15 +79,6 @@ class AuidtUserQuery(BaseModel):
7079
updated_by_fk: list[int] = Field(Query(default=[]))
7180

7281

73-
class AuditLog(BaseModel):
74-
id: int
75-
created_at: datetime
76-
request_id: str
77-
action: str
78-
diff: dict | None = None
79-
user: AuditUserBase | None = None
80-
81-
8282
class QueryParams(BaseModel):
8383
limit: int | None = Query(default=20, ge=0, le=1000, description="Number of results to return per request.")
8484
offset: int | None = Query(default=0, ge=0, description="The initial index from which return the results.")
@@ -108,3 +108,7 @@ class VisibleName(TypedDict, total=True):
108108

109109
class IdResponse(BaseModel):
110110
id: int
111+
112+
113+
class IdCreate(IdResponse):
114+
...

src/auth/api.py

Lines changed: 41 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
from fastapi import APIRouter, Depends, status
22
from fastapi.security import OAuth2PasswordRequestForm
3-
from sqlalchemy import select
43
from sqlalchemy.ext.asyncio import AsyncSession
54
from sqlalchemy.orm import selectinload
65

76
from src import errors
87
from src._types import IdResponse, ListT
98
from src.auth import schemas
10-
from src.auth.models import Group, Menu, Permission, Role, User
11-
from src.auth.services import GroupDto, MenuDto, RoleDto, UserDto
9+
from src.auth.models import Group, Menu, Role, User
10+
from src.auth.services import MenuDto, UserDto
1211
from src.cbv import cbv
12+
from src.db.dtobase import DtoBase
1313
from src.deps import auth, get_session
1414
from src.exceptions import GenerError
1515
from src.security import generate_access_token_response
@@ -23,21 +23,21 @@ async def login_pwd(
2323
user: OAuth2PasswordRequestForm = Depends(),
2424
session: AsyncSession = Depends(get_session),
2525
) -> schemas.AccessToken:
26-
user_dto = UserDto(User)
27-
result = await user_dto.verify_user(session, user)
26+
dto = UserDto(User)
27+
result = await dto.verify_user(session, user)
2828
return generate_access_token_response(result.id)
2929

3030

3131
@cbv(router)
3232
class UserAPI:
3333
user: User = Depends(auth)
3434
session: AsyncSession = Depends(get_session)
35-
user_dto = UserDto(User)
35+
dto = UserDto(User)
3636

3737
@router.post("/users", operation_id="5091fff6-1adc-4a22-8a8c-ef0107122df7", summary="创建新用户/Create new user")
3838
async def create_user(self, user: schemas.UserCreate) -> IdResponse:
39-
new_user = await self.user_dto.create(self.session, user)
40-
result = await self.user_dto.commit(self.session, new_user)
39+
new_user = await self.dto.create(self.session, user)
40+
result = await self.dto.commit(self.session, new_user)
4141
return IdResponse(id=result.id)
4242

4343
@router.get(
@@ -46,7 +46,7 @@ async def create_user(self, user: schemas.UserCreate) -> IdResponse:
4646
summary="获取单个用户/Get user information by ID",
4747
)
4848
async def get_user(self, id: int) -> schemas.UserDetail:
49-
db_user = await self.user_dto.get_one_or_404(
49+
db_user = await self.dto.get_one_or_404(
5050
self.session,
5151
id,
5252
selectinload(User.role).load_only(Role.id, Role.name),
@@ -55,8 +55,8 @@ async def get_user(self, id: int) -> schemas.UserDetail:
5555
return schemas.UserDetail.model_validate(db_user)
5656

5757
@router.get("/users", operation_id="2485e2a2-4d81-4601-a6fd-c633b23ce5fc")
58-
async def get_users(self, query: schemas.UserQuery = Depends()) -> ListT[list[schemas.UserDetail]]:
59-
count, results = await self.user_dto.list_and_count(
58+
async def get_users(self, query: schemas.UserQuery = Depends()) -> ListT[schemas.UserDetail]:
59+
count, results = await self.dto.list_and_count(
6060
self.session,
6161
query,
6262
selectinload(User.role).load_only(Role.id, Role.name),
@@ -68,129 +68,110 @@ async def get_users(self, query: schemas.UserQuery = Depends()) -> ListT[list[sc
6868
async def update_user(self, id: int, user: schemas.UserUpdate) -> IdResponse:
6969
update_user = user.model_dump(exclude_unset=True)
7070
if "password" in update_user and update_user["password"] is None:
71-
raise GenerError(errors.ERR_10006, status_code=status.HTTP_406_NOT_ACCEPTABLE)
72-
db_user = await self.user_dto.get_one_or_404(self.session, id)
73-
await self.user_dto.update(self.session, db_user, user)
71+
raise GenerError(errors.ERR_10005, status_code=status.HTTP_406_NOT_ACCEPTABLE)
72+
db_user = await self.dto.get_one_or_404(self.session, id)
73+
await self.dto.update(self.session, db_user, user)
7474
return IdResponse(id=id)
7575

7676
@router.delete("/users/{id}", operation_id="78e48ceb-d7cf-46fe-bf9e-d04958aade7d")
7777
async def delete_user(self, id: int) -> IdResponse:
78-
db_user = await self.user_dto.get_one_or_404(self.session, id)
79-
await self.user_dto.delete(self.session, db_user)
78+
db_user = await self.dto.get_one_or_404(self.session, id)
79+
await self.dto.delete(self.session, db_user)
8080
return IdResponse(id=id)
8181

8282

8383
@cbv(router)
8484
class GroupAPI:
8585
user: User = Depends(auth)
8686
session: AsyncSession = Depends(get_session)
87-
group_dto = GroupDto(Group)
87+
dto = DtoBase(Group)
8888

8989
@router.post("/groups", operation_id="9e3e639d-c694-467d-9209-717b038cf267")
9090
async def create_group(self, group: schemas.GroupCreate) -> IdResponse:
91-
if not group.user_ids:
92-
new_group = await self.group_dto.create(self.session, group)
93-
else:
94-
users = (await self.session.scalars(select(User).where(User.id.in_(group.user_ids)))).all()
95-
new_group = await self.group_dto.create_with_users(self.session, group, users)
91+
new_group = await self.dto.create(self.session, group)
9692
return IdResponse(id=new_group.id)
9793

9894
@router.get("/groups/{id}", operation_id="00327087-9443-4d24-8d04-e396e3244744")
9995
async def get_group(self, id: int) -> schemas.GroupDetail:
100-
db_group = await self.group_dto.get_one_or_404(self.session, id, undefer_load=True)
96+
db_group = await self.dto.get_one_or_404(self.session, id, undefer_load=True)
10197
return schemas.GroupDetail.model_validate(db_group)
10298

10399
@router.get("/groups", operation_id="a1d1f8f1-4d4d-4fab-868b-3f977df26e05")
104-
async def get_groups(self, query: schemas.GroupQuery = Depends()) -> ListT[list[schemas.GroupDetail]]:
105-
count, results = await self.group_dto.list_and_count(self.session, query)
100+
async def get_groups(self, query: schemas.GroupQuery = Depends()) -> ListT[schemas.GroupDetail]:
101+
count, results = await self.dto.list_and_count(self.session, query)
106102
return ListT(count=count, results=[schemas.GroupDetail.model_validate(r) for r in results])
107103

108104
@router.put("/groups/{id}", operation_id="3d5badd1-665c-49f8-85c4-6f6d7f3a1b2a")
109105
async def update_group(self, id: int, group: schemas.GroupUpdate) -> IdResponse:
110-
db_group = await self.group_dto.get_one_or_404(self.session, id, selectinload(Group.user))
111-
update_group = group.model_dump(exclude_unset=True)
112-
if "user_ids" in update_group:
113-
db_group = await self.group_dto.update_relationship_field(
114-
self.session, db_group, User, "user", group.user_ids
115-
)
116-
await self.group_dto.update(self.session, db_group, group, excludes={"user_ids"})
106+
db_group = await self.dto.get_one_or_404(self.session, id, selectinload(Group.user))
107+
await self.dto.update(self.session, db_group, group)
117108
return IdResponse(id=id)
118109

119110
@router.delete("/groups/{id}", operation_id="e16830da-2973-4369-8e75-da9b4174ab72")
120111
async def delete_group(self, id: int) -> IdResponse:
121-
db_group = await self.group_dto.get_one_or_404(self.session, id)
122-
await self.group_dto.delete(self.session, db_group)
112+
db_group = await self.dto.get_one_or_404(self.session, id)
113+
await self.dto.delete(self.session, db_group)
123114
return IdResponse(id=id)
124115

125116

126117
@cbv(router)
127118
class RoleAPI:
128119
user: User = Depends(auth)
129120
session: AsyncSession = Depends(get_session)
130-
role_dto = RoleDto(Role)
121+
dto = DtoBase(Role)
131122

132123
@router.post("/roles", operation_id="a18a152b-e9e9-4128-b8be-8a8e9c842abb")
133124
async def create_role(self, role: schemas.RoleCreate) -> IdResponse:
134-
if not role.permission_ids:
135-
new_role = await self.role_dto.create(self.session, role)
136-
else:
137-
permissions = (
138-
await self.session.scalars(select(Permission).where(Permission.id.in_(role.permission_ids)))
139-
).all()
140-
new_role = await self.role_dto.create_with_permissions(self.session, role, permissions)
125+
new_role = await self.dto.create(self.session, role)
141126
return IdResponse(id=new_role.id)
142127

143128
@router.get("/roles/{id}", operation_id="2b45f59a-77a1-45d4-bf43-94373da517e3")
144129
async def get_role(self, id: int) -> schemas.RoleDetail:
145-
db_role = await self.role_dto.get_one_or_404(self.session, id, selectinload(Role.permission), undefer_load=True)
130+
db_role = await self.dto.get_one_or_404(self.session, id, selectinload(Role.permission), undefer_load=True)
146131
return schemas.RoleDetail.model_validate(db_role)
147132

148133
@router.get("/roles", operation_id="c5f793b1-7adf-4b4e-a498-732b0fa7d758")
149-
async def get_roles(self, query: schemas.RoleQuery = Depends()) -> ListT[list[schemas.RoleList]]:
150-
count, results = await self.role_dto.list_and_count(self.session, query)
134+
async def get_roles(self, query: schemas.RoleQuery = Depends()) -> ListT[schemas.RoleList]:
135+
count, results = await self.dto.list_and_count(self.session, query)
151136
return ListT(count=count, results=[schemas.RoleList.model_validate(r) for r in results])
152137

153138
@router.put("/roles/{id}", operation_id="2fda2e00-ad86-4296-a1d4-c7f02366b52e")
154139
async def update_role(self, id: int, role: schemas.RoleUpdate) -> IdResponse:
155-
db_role = await self.role_dto.get_one_or_404(self.session, id, selectinload(Role.permission))
156-
if "permission_ids" in role.model_dump(exclude_unset=True):
157-
db_role = await self.role_dto.update_relationship_field(
158-
self.session, db_role, Permission, "permission", role.permission_ids
159-
)
160-
await self.role_dto.update(self.session, db_role, role, excludes={"permission_ids"})
140+
db_role = await self.dto.get_one_or_404(self.session, id, selectinload(Role.permission))
141+
await self.dto.update(self.session, db_role, role)
161142
return IdResponse(id=id)
162143

163144
@router.delete("/roles/{id}", operation_id="c4e9e0e8-6b0c-4f6f-9e6c-8d9f9f9f9f9f")
164145
async def delete_role(self, id: int) -> IdResponse:
165-
db_role = await self.role_dto.get_one_or_404(self.session, id)
166-
await self.role_dto.delete(self.session, db_role)
146+
db_role = await self.dto.get_one_or_404(self.session, id)
147+
await self.dto.delete(self.session, db_role)
167148
return IdResponse(id=id)
168149

169150

170151
@cbv(router)
171152
class MenuAPI:
172153
user: User = Depends(auth)
173154
session: AsyncSession = Depends(get_session)
174-
menu_dto = MenuDto(Menu)
155+
dto = MenuDto(Menu)
175156

176157
@router.post("/menus", operation_id="008bf4d4-cc01-48b0-82b8-1a67c0348b31")
177158
async def create_menu(self, meun: schemas.MenuCreate) -> IdResponse:
178-
new_menu = await self.menu_dto.create(self.session, meun)
159+
new_menu = await self.dto.create(self.session, meun)
179160
return IdResponse(id=new_menu.id)
180161

181162
@router.get("/menus", operation_id="cb7f25ab-798b-4668-a838-6339425e2889")
182163
async def get_menus(self) -> schemas.MenuTree:
183-
results = await self.menu_dto.get_all(self.session)
164+
results = await self.dto.get_all(self.session)
184165
data = list_to_tree([r.dict() for r in results])
185166
return schemas.MenuTree.model_validate(data)
186167

187168
@router.put("menus/{id}", operation_id="b4d7ac97-a182-4bd1-a75c-6ae44b5fcf0a")
188169
async def update_menu(self, id: int, meun: schemas.MenuUpdate) -> IdResponse:
189-
db_menu = await self.menu_dto.get_one_or_404(self.session, id)
190-
await self.menu_dto.update(self.session, db_menu, meun)
170+
db_menu = await self.dto.get_one_or_404(self.session, id)
171+
await self.dto.update(self.session, db_menu, meun)
191172
return IdResponse(id=id)
192173

193174
async def delete_menu(self, id: int) -> IdResponse:
194-
db_menu = await self.menu_dto.get_one_or_404(self.session, id)
195-
await self.menu_dto.delete(self.session, db_menu)
175+
db_menu = await self.dto.get_one_or_404(self.session, id)
176+
await self.dto.delete(self.session, db_menu)
196177
return IdResponse(id=id)

src/auth/schemas.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
from datetime import datetime
2-
from uuid import UUID
32

43
from pydantic_extra_types.phone_numbers import PhoneNumber
54

6-
from src._types import AuditTime, BaseModel, QueryParams
5+
from src._types import AuditTime, BaseModel, IdCreate, QueryParams
76

87

98
class AccessToken(BaseModel):
@@ -43,7 +42,7 @@ class UserBase(BaseModel):
4342
avatar: str | None = None
4443

4544

46-
class UserBrief(UserBase, AuditTime):
45+
class UserBrief(UserBase):
4746
id: int
4847

4948

@@ -130,11 +129,11 @@ class UserCreate(UserBase):
130129
class GroupCreate(GroupBase):
131130
password: str
132131
role_id: int
133-
user_ids: list[int] | None = None
132+
user: list[IdCreate]
134133

135134

136135
class RoleCreate(RoleBase):
137-
permission_ids: list[UUID] | None = None
136+
permission: list[IdCreate]
138137

139138

140139
class UserUpdate(UserCreate):

src/auth/services.py

Lines changed: 1 addition & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from sqlalchemy.ext.asyncio import AsyncSession
66

77
from src.auth import schemas
8-
from src.auth.models import Group, Menu, Permission, Role, User
8+
from src.auth.models import Menu, Permission, User
99
from src.auth.schemas import PermissionCreate, PermissionUpdate
1010
from src.context import locale_ctx
1111
from src.db.dtobase import DtoBase
@@ -24,48 +24,6 @@ async def verify_user(self, session: AsyncSession, user: OAuth2PasswordRequestFo
2424
return db_user
2525

2626

27-
class GroupDto(DtoBase[Group, schemas.GroupCreate, schemas.GroupUpdate, schemas.GroupQuery]):
28-
async def create_with_users(
29-
self, session: AsyncSession, group: schemas.GroupCreate, users: Sequence[User]
30-
) -> Group:
31-
"""
32-
Create a new group with the specified users.
33-
34-
Parameters:
35-
session (AsyncSession): The database session.
36-
group (schemas.GroupCreate): The group data to create.
37-
users (Sequence[User]): The list of users to associate with the group.
38-
39-
Returns:
40-
Group: The newly created group.
41-
42-
"""
43-
new_group = await self.create(session, group, excludes={"user_ids"}, commit=False)
44-
new_group.user.extend(users)
45-
return await self.commit(session, new_group)
46-
47-
48-
class RoleDto(DtoBase[Role, schemas.RoleCreate, schemas.RoleUpdate, schemas.RoleQuery]):
49-
async def create_with_permissions(
50-
self, session: AsyncSession, role: schemas.RoleCreate, permissions: Sequence[Permission]
51-
) -> Role:
52-
"""
53-
Create a new role with the specified permissions.
54-
55-
Parameters:
56-
session (AsyncSession): The database session.
57-
role (schemas.RoleCreate): The role data to create.
58-
permissions (Sequence[Permission]): The list of permissions to associate with the role.
59-
60-
Returns:
61-
Role: The newly created role.
62-
63-
"""
64-
new_role = await self.create(session, role, excludes={"permission_ids"}, commit=False)
65-
new_role.permission.extend(permissions)
66-
return await self.commit(session, new_role)
67-
68-
6927
class PermissionDto(DtoBase[Permission, schemas.PermissionCreate, schemas.PermissionUpdate, schemas.PermissionQuery]):
7028
async def create(
7129
self,

0 commit comments

Comments
 (0)