Skip to content

Commit 10c45f2

Browse files
committed
patch group logic and filtering
1 parent c4cdd6b commit 10c45f2

3 files changed

Lines changed: 60 additions & 22 deletions

File tree

auth_backend/admin/admin.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33
from sqlalchemy.sql.expression import Select
44
from starlette.requests import Request
55

6+
from auth_backend.admin.filter import FilteredModelConverter
67
from auth_backend.models.db import Group, Scope, User
7-
from auth_backend.routes.groups import create_group_logic, delete_group_id
8+
from auth_backend.routes.groups import create_group_logic, delete_group_id, patch_group_logic
89
from auth_backend.routes.user import patch_user_groups
9-
from auth_backend.schemas.models import GroupPost
10+
from auth_backend.schemas.models import GroupPatch, GroupPost
1011

1112

1213
class ScopeAdmin(ModelView, model=Scope):
@@ -27,6 +28,7 @@ class ScopeAdmin(ModelView, model=Scope):
2728
column_default_sort = [("id", False)]
2829
form_excluded_columns = ["create_ts", "update_ts", "groups", "user_sessions", "is_deleted"]
2930
can_create = False # I don't know how to use UnionAuth there to get user_id that is required
31+
form_converter = FilteredModelConverter
3032

3133
def list_query(self, request: Request) -> Select:
3234
return select(Scope).where(Scope.is_deleted == False)
@@ -65,14 +67,14 @@ class GroupAdmin(ModelView, model=Group):
6567
column_sortable_list = ["id", "name", "parent_id", "is_deleted"]
6668
column_default_sort = [("id", False)]
6769
form_excluded_columns = ["create_ts", "update_ts", "is_deleted"]
70+
form_converter = FilteredModelConverter
6871

6972
def list_query(self, request: Request) -> Select:
7073
return select(Group).where(Group.is_deleted == False)
7174

7275
def count_query(self, request: Request) -> Select:
7376
return select(func.count(Group.id)).where(Group.is_deleted == False)
7477

75-
# add logic with deleted scopes showed
7678
async def insert_model(self, request, data):
7779
scope_ids = [int(s) for s in (data.pop("scopes", None) or [])]
7880
parent_id = int(data["parent_id"]) if data.get("parent_id") else None
@@ -81,7 +83,17 @@ async def insert_model(self, request, data):
8183
result = create_group_logic(group_inp, session)
8284
return Group.get(result["id"], session=session)
8385

84-
# add update group
86+
async def update_model(self, request, pk, data):
87+
scope_ids = [int(s) for s in (data.pop("scopes", None) or [])]
88+
parent_id = int(data["parent_id"]) if data.get("parent_id") else None
89+
group_inp = GroupPatch(
90+
name=data.get("name"),
91+
parent_id=parent_id,
92+
scopes=scope_ids,
93+
)
94+
with self.session_maker(expire_on_commit=False) as session:
95+
return patch_group_logic(int(pk), group_inp, session)
96+
8597
async def delete_model(self, request, pk):
8698
with self.session_maker(expire_on_commit=False) as session:
8799
delete_group_id(int(pk), session)
@@ -104,6 +116,7 @@ class UserAdmin(ModelView, model=User):
104116
column_formatters_detail = {
105117
"scopes": lambda m, a: ", ".join(s.name for s in (m.scopes or set())),
106118
}
119+
form_converter = FilteredModelConverter
107120

108121
def list_query(self, request: Request) -> Select:
109122
return select(User).where(User.is_deleted == False)

auth_backend/admin/filter.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import anyio
2+
from sqladmin.forms import ModelConverter
3+
from sqladmin.helpers import is_async_session_maker
4+
from sqlalchemy import select
5+
6+
7+
class FilteredModelConverter(ModelConverter):
8+
async def _prepare_select_options(self, prop, session_maker):
9+
target_model = prop.mapper.class_
10+
stmt = select(target_model)
11+
if hasattr(target_model, "is_deleted"):
12+
stmt = stmt.where(target_model.is_deleted == False)
13+
if is_async_session_maker(session_maker):
14+
async with session_maker() as session:
15+
objects = await session.execute(stmt)
16+
return [(str(self._get_identifier_value(obj)), str(obj)) for obj in objects.scalars().unique().all()]
17+
else:
18+
with session_maker() as session:
19+
objects = await anyio.to_thread.run_sync(session.execute, stmt)
20+
return [(str(self._get_identifier_value(obj)), str(obj)) for obj in objects.scalars().unique().all()]

auth_backend/routes/groups.py

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -73,47 +73,52 @@ async def create_group(
7373
return Group(**result).model_dump(exclude_unset=True)
7474

7575

76-
@groups.patch("/{id}", response_model=Group)
77-
async def patch_group(
78-
id: int,
79-
group_inp: GroupPatch,
80-
_: UserSession = Depends(UnionAuth(scopes=["auth.group.update"], allow_none=False, auto_error=True)),
81-
) -> Group:
82-
"""
83-
Scopes: `["auth.group.update"]`
84-
"""
76+
def patch_group_logic(id: int, group_inp: GroupPatch, session) -> DbGroup:
8577
if (
86-
exists_check := DbGroup.query(session=db.session)
78+
exists_check := DbGroup.query(session=session)
8779
.filter(DbGroup.name == group_inp.name, DbGroup.id != id)
8880
.one_or_none()
8981
):
9082
raise AlreadyExists(Group, exists_check.id)
91-
group = DbGroup.get(id, session=db.session)
83+
group = DbGroup.get(id, session=session)
9284
if group_inp.parent_id in (row.id for row in group.child):
9385
raise HTTPException(
9486
status_code=400,
9587
detail=StatusResponseModel(status="Error", message="Cycle detected", ru="Найден цикл").model_dump(),
9688
)
9789
result = Group.model_validate(
98-
DbGroup.update(id, session=db.session, **group_inp.model_dump(exclude_unset=True, exclude={"scopes"}))
90+
DbGroup.update(id, session=session, **group_inp.model_dump(exclude_unset=True, exclude={"scopes"}))
9991
).model_dump(exclude_unset=True)
10092
scopes = set()
10193
if group_inp.scopes is not None:
10294
for _scope_id in group_inp.scopes:
103-
scopes.add(Scope.get(session=db.session, id=_scope_id))
95+
scopes.add(Scope.get(session=session, id=_scope_id))
10496
group.scopes = scopes
105-
db.session.commit()
97+
session.commit()
98+
return group
99+
100+
101+
@groups.patch("/{id}", response_model=Group)
102+
async def patch_group(
103+
id: int,
104+
group_inp: GroupPatch,
105+
_: UserSession = Depends(UnionAuth(scopes=["auth.group.update"], allow_none=False, auto_error=True)),
106+
) -> Group:
107+
"""
108+
Scopes: `["auth.group.update"]`
109+
"""
110+
group = patch_group_logic(id, group_inp, db.session)
106111
return Group.model_validate(group)
107112

108113

109114
def delete_group_id(id: int, session) -> None:
110-
group: DbGroup = DbGroup.get(id, session=db.session)
115+
group: DbGroup = DbGroup.get(id, session=session)
111116
if child := group.child:
112117
for children in child:
113118
children.parent_id = group.parent_id
114-
db.session.flush()
115-
DbGroup.delete(id, session=db.session)
116-
db.session.commit()
119+
session.flush()
120+
DbGroup.delete(id, session=session)
121+
session.commit()
117122

118123

119124
@groups.delete("/{id}", response_model=None)

0 commit comments

Comments
 (0)