Skip to content

Commit 19dd917

Browse files
committed
feat: add student_sheet API #7
1 parent 1eb6bf1 commit 19dd917

File tree

12 files changed

+275
-0
lines changed

12 files changed

+275
-0
lines changed

fastapi/app/api/v1/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
from .auth import AuthAPI
2+
from .student_sheet import StudentSheetAPI
23
from .user import UserAPI
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
from typing import List, Optional
2+
from uuid import UUID
3+
4+
from app.core.exceptions import (
5+
ApiException,
6+
NotFoundObjectMatchingUuid,
7+
PermissionDenied,
8+
)
9+
from app.crud import StudentSheetCRUD
10+
from app.models import StudentSheet
11+
from app.schemas import UpdateStudentSheetSchema
12+
13+
from fastapi import Request
14+
15+
16+
class StudentSheetAPI:
17+
@classmethod
18+
def gets(cls, request: Request) -> List[StudentSheet]:
19+
return StudentSheetCRUD(request.state.db_session).gets_by_user_uuid(
20+
request.user.uuid
21+
)
22+
23+
@classmethod
24+
def get(cls, request: Request, uuid: UUID) -> Optional[StudentSheet]:
25+
obj = StudentSheetCRUD(request.state.db_session).get_by_uuid(uuid)
26+
if obj is None:
27+
raise ApiException(NotFoundObjectMatchingUuid(StudentSheet, uuid))
28+
if obj.user_uuid != request.user.uuid:
29+
raise ApiException(PermissionDenied())
30+
return obj
31+
32+
@classmethod
33+
def create(cls, request: Request, schema: UpdateStudentSheetSchema) -> StudentSheet:
34+
data = schema.dict()
35+
data["user_uuid"] = request.user.uuid
36+
return StudentSheetCRUD(request.state.db_session).create(data)
37+
38+
@classmethod
39+
def update(
40+
cls, request: Request, uuid: UUID, schema: UpdateStudentSheetSchema
41+
) -> StudentSheet:
42+
obj = StudentSheetCRUD(request.state.db_session).get_by_uuid(uuid)
43+
if not obj:
44+
raise ApiException(NotFoundObjectMatchingUuid(StudentSheet, uuid))
45+
if obj.user_uuid != request.user.uuid:
46+
raise ApiException(PermissionDenied)
47+
data = schema.dict()
48+
data["user_uuid"] = request.user.uuid
49+
return StudentSheetCRUD(request.state.db_session).update(uuid, data)
50+
51+
@classmethod
52+
def delete(cls, request: Request, uuid: UUID) -> None:
53+
obj = StudentSheetCRUD(request.state.db_session).get_by_uuid(uuid)
54+
if not obj:
55+
raise ApiException(NotFoundObjectMatchingUuid(StudentSheet, uuid))
56+
if obj.user_uuid != request.user.uuid:
57+
raise ApiException(PermissionDenied)
58+
return StudentSheetCRUD(request.state.db_session).delete_by_uuid(uuid)

fastapi/app/crud/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
from .student_sheet import StudentSheetCRUD
12
from .user import UserCRUD

fastapi/app/crud/student_sheet.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
from typing import List, Optional
2+
from uuid import UUID
3+
4+
from app.core.exceptions import ApiException, NotFoundObjectMatchingUuid, create_error
5+
from app.db.database import get_db_session
6+
from app.models import StudentSheet
7+
from sqlalchemy.orm import scoped_session
8+
9+
from .base import BaseCRUD
10+
11+
db_session = get_db_session()
12+
13+
14+
class StudentSheetCRUD(BaseCRUD):
15+
def __init__(self, db_session: scoped_session):
16+
super().__init__(db_session, StudentSheet)
17+
18+
def gets_by_user_uuid(self, user_uuid: str) -> List[StudentSheet]:
19+
return self.get_query().filter_by(user_uuid=user_uuid).all()
20+
21+
def get_by_user_uuid_and_name(
22+
self, user_uuid: str, name: str
23+
) -> Optional[StudentSheet]:
24+
return self.get_query().filter_by(user_uuid=user_uuid, name=name).first()
25+
26+
def create(self, data: dict = {}) -> StudentSheet:
27+
if self.get_by_user_uuid_and_name(data["user_uuid"], data["name"]) is not None:
28+
raise ApiException(
29+
create_error(f"StudentSheet with name {data['name']} already exists")
30+
)
31+
return super().create(data)
32+
33+
def update(self, uuid: UUID, data: dict = {}) -> StudentSheet:
34+
update_obj = self.get_by_uuid(uuid)
35+
if update_obj is None:
36+
raise ApiException(NotFoundObjectMatchingUuid(self.model, uuid))
37+
38+
new_data_obj = self.get_by_user_uuid_and_name(data["user_uuid"], data["name"])
39+
if new_data_obj is not None and new_data_obj.uuid != update_obj.uuid:
40+
raise ApiException(
41+
create_error(f"StudentSheet with name {data['name']} already exists")
42+
)
43+
return super().update(uuid, data)
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
"""student_sheet
2+
3+
Revision ID: 8bf6f6f70d43
4+
Revises: c2dc0f9395d6
5+
Create Date: 2022-01-03 17:55:31.902638+09:00
6+
7+
"""
8+
import sqlalchemy as sa
9+
from alembic import op
10+
from sqlalchemy.dialects import postgresql
11+
12+
# revision identifiers, used by Alembic.
13+
revision = "8bf6f6f70d43"
14+
down_revision = "c2dc0f9395d6"
15+
branch_labels = None
16+
depends_on = None
17+
18+
19+
def upgrade():
20+
# ### commands auto generated by Alembic - please adjust! ###
21+
op.create_table(
22+
"student_sheets",
23+
sa.Column("uuid", postgresql.UUID(as_uuid=True), nullable=False),
24+
sa.Column(
25+
"created_at",
26+
postgresql.TIMESTAMP(timezone=True),
27+
server_default=sa.text("CURRENT_TIMESTAMP"),
28+
nullable=False,
29+
comment="登録日時",
30+
),
31+
sa.Column(
32+
"updated_at",
33+
postgresql.TIMESTAMP(timezone=True),
34+
nullable=True,
35+
comment="最終更新日時",
36+
),
37+
sa.Column("is_active", sa.BOOLEAN(), server_default="true", nullable=False),
38+
sa.Column("name", sa.VARCHAR(length=100), nullable=False),
39+
sa.Column("user_uuid", postgresql.UUID(as_uuid=True), nullable=False),
40+
sa.ForeignKeyConstraint(["user_uuid"], ["users.uuid"], ondelete="CASCADE"),
41+
sa.PrimaryKeyConstraint("uuid"),
42+
sa.UniqueConstraint("name", "user_uuid"),
43+
)
44+
# ### end Alembic commands ###
45+
46+
47+
def downgrade():
48+
# ### commands auto generated by Alembic - please adjust! ###
49+
op.drop_table("student_sheets")
50+
# ### end Alembic commands ###

fastapi/app/models/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
from .student_sheet import StudentSheet
12
from .user import User
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from sqlalchemy import Column, ForeignKey, VARCHAR
2+
from sqlalchemy.dialects.postgresql import UUID
3+
from sqlalchemy.schema import UniqueConstraint
4+
5+
from .base import BaseModelMixin
6+
7+
8+
class StudentSheet(BaseModelMixin):
9+
__tablename__ = "student_sheets"
10+
__table_args__ = UniqueConstraint("name", "user_uuid"), {}
11+
12+
MAX_LENGTH_NAME = 100
13+
name = Column(VARCHAR(MAX_LENGTH_NAME), nullable=False)
14+
15+
user_uuid = Column(
16+
UUID(as_uuid=True),
17+
ForeignKey("users.uuid", ondelete="CASCADE"),
18+
nullable=False,
19+
)

fastapi/app/models/user.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from sqlalchemy import BOOLEAN, VARCHAR, Column, String
2+
from sqlalchemy.orm import relationship
23

34
from .base import BaseModelMixin
45

@@ -14,3 +15,5 @@ class User(BaseModelMixin):
1415
firebase_uid = Column(VARCHAR(MAX_LENGTH_FIREBASE_UID), unique=True, nullable=True)
1516

1617
is_admin = Column(BOOLEAN, nullable=False, default=False)
18+
19+
student_sheets = relationship("StudentSheet", backref="user", cascade="all")

fastapi/app/routers/v1/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
from fastapi import APIRouter
22

33
from .auth import auth_router
4+
from .studnet_sheet import student_sheet_router
45
from .user import user_router
56

67
api_v1_router = APIRouter()
78
api_v1_router.include_router(user_router, prefix="/users", tags=["users"])
89
api_v1_router.include_router(auth_router, prefix="/auth", tags=["auth"])
10+
api_v1_router.include_router(
11+
student_sheet_router, prefix="/student-sheets", tags=["student sheets"]
12+
)
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
from typing import List, Optional
2+
from uuid import UUID
3+
4+
from app.api.v1 import StudentSheetAPI
5+
from app.dependencies.auth import login_required
6+
from app.models import StudentSheet
7+
from app.schemas import (
8+
CreateStudentSheetSchema,
9+
ReadStudentSheetSchema,
10+
UpdateStudentSheetSchema,
11+
)
12+
13+
from fastapi import APIRouter, Depends, Request
14+
15+
student_sheet_router = APIRouter()
16+
17+
18+
@student_sheet_router.get(
19+
"/",
20+
response_model=List[ReadStudentSheetSchema],
21+
dependencies=[Depends(login_required)],
22+
)
23+
async def gets(request: Request) -> List[StudentSheet]:
24+
return StudentSheetAPI.gets(request)
25+
26+
27+
@student_sheet_router.get(
28+
"/{uuid}",
29+
response_model=ReadStudentSheetSchema,
30+
dependencies=[Depends(login_required)],
31+
)
32+
async def get(request: Request, uuid: UUID) -> Optional[StudentSheet]:
33+
return StudentSheetAPI.get(request, uuid)
34+
35+
36+
@student_sheet_router.post(
37+
"/", response_model=ReadStudentSheetSchema, dependencies=[Depends(login_required)]
38+
)
39+
async def create(request: Request, schema: CreateStudentSheetSchema) -> StudentSheet:
40+
return StudentSheetAPI.create(request, schema)
41+
42+
43+
@student_sheet_router.put(
44+
"/{uuid}",
45+
response_model=ReadStudentSheetSchema,
46+
dependencies=[Depends(login_required)],
47+
)
48+
async def update(
49+
request: Request,
50+
uuid: UUID,
51+
schema: UpdateStudentSheetSchema,
52+
) -> StudentSheet:
53+
return StudentSheetAPI.update(request, uuid, schema)
54+
55+
56+
@student_sheet_router.delete("/{uuid}", dependencies=[Depends(login_required)])
57+
async def delete(request: Request, uuid: UUID) -> None:
58+
return StudentSheetAPI.delete(request, uuid)

0 commit comments

Comments
 (0)