Skip to content

Commit a055a30

Browse files
committed
feature: book genre model created & book genre create, update and list routes added
1 parent e242a3a commit a055a30

File tree

7 files changed

+238
-6
lines changed

7 files changed

+238
-6
lines changed

migrations/env.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
from pkg.config import Config
88
from src.auth.models import PasswordResetLog, TokenBlacklist, User
9-
from src.books.models import BookCategory
9+
from src.books.models import BookCategory, BookGenre
1010
from src.profile.models import UserProfile
1111

1212
config = context.config
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
"""book_genres
2+
3+
Revision ID: 80993e702d0c
4+
Revises: 851d8f474df1
5+
Create Date: 2024-10-10 18:21:20.261148
6+
7+
"""
8+
9+
from typing import Sequence, Union
10+
11+
import sqlalchemy as sa
12+
from alembic import op
13+
from sqlalchemy.dialects import postgresql
14+
15+
revision: str = "80993e702d0c"
16+
down_revision: Union[str, None] = "851d8f474df1"
17+
branch_labels: Union[str, Sequence[str], None] = None
18+
depends_on: Union[str, Sequence[str], None] = None
19+
20+
21+
def upgrade() -> None:
22+
op.create_table(
23+
"book_genres",
24+
sa.Column("uid", sa.UUID(), nullable=False),
25+
sa.Column("genre", sa.VARCHAR(), nullable=False),
26+
sa.Column("description", sa.TEXT(), nullable=False),
27+
sa.Column("created_by", sa.UUID(), nullable=False),
28+
sa.Column("created_at", postgresql.TIMESTAMP(), nullable=False),
29+
sa.Column("updated_at", postgresql.TIMESTAMP(), nullable=False),
30+
sa.PrimaryKeyConstraint("uid"),
31+
sa.UniqueConstraint("genre"),
32+
)
33+
34+
35+
def downgrade() -> None:
36+
op.drop_table("book_genres")

src/__init__.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from pkg.middleware import register_middleware
44
from src.auth.routes import auth_router
5-
from src.books.routes import book_category_router
5+
from src.books.routes import book_category_router, book_genre_router
66
from src.profile.routes import profile_router
77

88
version = "v1"
@@ -42,3 +42,8 @@
4242
prefix=f"{version_prefix}/books/category",
4343
tags=["book_category"],
4444
)
45+
app.include_router(
46+
book_genre_router,
47+
prefix=f"{version_prefix}/books/genre",
48+
tags=["book_genre"],
49+
)

src/books/models.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,24 @@ class BookCategory(SQLModel, table=True):
2424
pg.TIMESTAMP, nullable=False, default=datetime.now, onupdate=datetime.now
2525
)
2626
)
27+
28+
29+
class BookGenre(SQLModel, table=True):
30+
__tablename__ = "book_genres"
31+
32+
uid: uuid.UUID = Field(
33+
sa_column=Column(pg.UUID, nullable=False, primary_key=True, default=uuid.uuid4)
34+
)
35+
genre: str = Field(sa_column=Column(pg.VARCHAR, nullable=False, unique=True))
36+
description: str = Field(sa_column=Column(pg.TEXT, nullable=False))
37+
created_by: uuid.UUID = Field(
38+
sa_column=Column(pg.UUID, nullable=False, default=uuid.uuid4)
39+
)
40+
created_at: datetime = Field(
41+
sa_column=Column(pg.TIMESTAMP, nullable=False, default=datetime.now)
42+
)
43+
updated_at: datetime = Field(
44+
sa_column=Column(
45+
pg.TIMESTAMP, nullable=False, default=datetime.now, onupdate=datetime.now
46+
)
47+
)

src/books/routes.py

Lines changed: 94 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,19 @@
77
from pkg.db import get_session
88
from pkg.utils import get_current_user_uid
99

10-
from .schemas import BookCategoryCreateSchema, BookCategoryResponseSchema
11-
from .service import BookCategoryService
10+
from .schemas import (
11+
BookCategoryCreateSchema,
12+
BookCategoryResponseSchema,
13+
BookGenreCreateSchema,
14+
BookGenreResponseSchema,
15+
)
16+
from .service import BookCategoryService, BookGenreService
1217

1318
book_category_router = APIRouter()
19+
book_genre_router = APIRouter()
1420

1521
book_category_service = BookCategoryService()
22+
book_genre_service = BookGenreService()
1623

1724

1825
@book_category_router.post("/create", status_code=status.HTTP_201_CREATED)
@@ -62,7 +69,7 @@ async def update_book_category(
6269
content={"message": "Book category not found"},
6370
)
6471

65-
if book_category.created_by != user_uid:
72+
if str(book_category.created_by) != user_uid:
6673
return JSONResponse(
6774
status_code=status.HTTP_403_FORBIDDEN,
6875
content={"message": "You are not allowed to update this book category"},
@@ -99,3 +106,87 @@ async def list_book_categories(session: AsyncSession = Depends(get_session)):
99106
],
100107
},
101108
)
109+
110+
111+
@book_genre_router.post("/create", status_code=status.HTTP_201_CREATED)
112+
async def create_book_genre(
113+
genre_data: BookGenreCreateSchema,
114+
session: AsyncSession = Depends(get_session),
115+
user_uid: str = Depends(get_current_user_uid),
116+
):
117+
book_genre = await book_genre_service.get_book_genre_by_genre(
118+
genre_data.genre, session
119+
)
120+
if book_genre:
121+
return JSONResponse(
122+
status_code=status.HTTP_400_BAD_REQUEST,
123+
content={"message": "Book genre already exists"},
124+
)
125+
126+
book_genre = await book_genre_service.create_book_genre(
127+
user_uid, genre_data, session
128+
)
129+
130+
return JSONResponse(
131+
status_code=status.HTTP_201_CREATED,
132+
content={
133+
"message": "Book genre created successfully",
134+
"book_genre": BookGenreResponseSchema(
135+
**json.loads(book_genre.model_dump_json())
136+
).model_dump(),
137+
},
138+
)
139+
140+
141+
@book_genre_router.patch("/update/{genre_uid}", status_code=status.HTTP_200_OK)
142+
async def update_book_genre(
143+
genre_uid: str,
144+
genre_data: BookGenreCreateSchema,
145+
session: AsyncSession = Depends(get_session),
146+
user_uid: str = Depends(get_current_user_uid),
147+
):
148+
book_genre = await book_genre_service.get_book_genre_by_uid(genre_uid, session)
149+
150+
if not book_genre:
151+
return JSONResponse(
152+
status_code=status.HTTP_404_NOT_FOUND,
153+
content={"message": "Book genre not found"},
154+
)
155+
156+
if str(book_genre.created_by) != user_uid:
157+
return JSONResponse(
158+
status_code=status.HTTP_403_FORBIDDEN,
159+
content={"message": "You are not allowed to update this book genre"},
160+
)
161+
162+
book_genre = await book_genre_service.update_book_genre(
163+
book_genre, genre_data.model_dump(), session
164+
)
165+
166+
return JSONResponse(
167+
status_code=status.HTTP_200_OK,
168+
content={
169+
"message": "Book genre updated successfully",
170+
"book_genre": BookGenreResponseSchema(
171+
**json.loads(book_genre.model_dump_json())
172+
).model_dump(),
173+
},
174+
)
175+
176+
177+
@book_genre_router.get("/list", status_code=status.HTTP_200_OK)
178+
async def list_book_genres(session: AsyncSession = Depends(get_session)):
179+
book_genres = await book_genre_service.list_book_genres(session)
180+
181+
return JSONResponse(
182+
status_code=status.HTTP_200_OK,
183+
content={
184+
"message": "List of book genres",
185+
"book_genres": [
186+
BookGenreResponseSchema(
187+
**json.loads(book_genre.model_dump_json())
188+
).model_dump()
189+
for book_genre in book_genres
190+
],
191+
},
192+
)

src/books/schemas.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,38 @@ class BookCategoryResponseSchema(BaseModel):
3434
}
3535
}
3636
}
37+
38+
39+
class BookGenreCreateSchema(BaseModel):
40+
genre: str = Field(
41+
max_length=50,
42+
description="The name of the book genre. This field must be unique.",
43+
)
44+
description: str = Field(
45+
max_length=500, description="A brief description of the book genre."
46+
)
47+
48+
model_config = {
49+
"json_schema_extra": {
50+
"example": {
51+
"genre": "Fantasy",
52+
"description": "Books that feature magic, mythical creatures, and epic adventures.",
53+
}
54+
}
55+
}
56+
57+
58+
class BookGenreResponseSchema(BaseModel):
59+
uid: str
60+
genre: str
61+
description: str
62+
63+
model_config = {
64+
"json_schema_extra": {
65+
"example": {
66+
"uid": "123e4567-e89b-12d3-a456-426614174000",
67+
"genre": "Fantasy",
68+
"description": "Books that feature magic, mythical creatures, and epic adventures.",
69+
}
70+
}
71+
}

src/books/service.py

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from sqlalchemy import select
22
from sqlmodel.ext.asyncio.session import AsyncSession
33

4-
from .models import BookCategory
4+
from .models import BookCategory, BookGenre
55

66

77
class BookCategoryService:
@@ -48,3 +48,47 @@ async def list_book_categories(self, session: AsyncSession):
4848
result = await session.execute(select(BookCategory))
4949
book_categories = result.scalars().all()
5050
return book_categories
51+
52+
53+
class BookGenreService:
54+
async def create_book_genre(
55+
self, user_uid: str, genre_data: dict, session: AsyncSession
56+
):
57+
book_genre = BookGenre(
58+
genre=genre_data.genre,
59+
description=genre_data.description,
60+
created_by=user_uid,
61+
)
62+
session.add(book_genre)
63+
await session.commit()
64+
await session.refresh(book_genre)
65+
66+
return book_genre
67+
68+
async def update_book_genre(
69+
self, book_genre: BookGenre, genre_data: dict, session: AsyncSession
70+
):
71+
for field, value in genre_data.items():
72+
setattr(book_genre, field, value)
73+
74+
await session.commit()
75+
await session.refresh(book_genre)
76+
77+
return book_genre
78+
79+
async def get_book_genre_by_uid(self, uid: str, session: AsyncSession):
80+
result = await session.execute(select(BookGenre).where(BookGenre.uid == uid))
81+
book_genre = result.scalars().first()
82+
return book_genre
83+
84+
async def get_book_genre_by_genre(self, genre: str, session: AsyncSession):
85+
result = await session.execute(
86+
select(BookGenre).where(BookGenre.genre == genre)
87+
)
88+
book_genre = result.scalars().first()
89+
return book_genre
90+
91+
async def list_book_genres(self, session: AsyncSession):
92+
result = await session.execute(select(BookGenre))
93+
book_genres = result.scalars().all()
94+
return book_genres

0 commit comments

Comments
 (0)