added CRUD director, genre and movie models

parent 0779884b
from core.ctrl import BaseController
from db.repositories.movie import DirectorRepository
class DirectorController(BaseController):
repo = DirectorRepository()
import fastapi
from starlette import status
from exceptions import common as common_exc, http as http_exc
from .schemas import (
DirectorBaseSchema, DirectorListSchema, DirectorGetSchema,
DirectorPostSchema, DirectorPatchSchema
)
from .ctrl import DirectorController
router = fastapi.APIRouter(prefix='/directors', tags=['Director'])
ctrl = DirectorController()
@router.get('')
async def get_directors(query: DirectorBaseSchema = fastapi.Depends()):
directors = await ctrl.get_list(**query.model_dump(exclude_none=True))
return [DirectorListSchema.from_orm(director) for director in directors]
@router.get('/{id}')
async def get_director(id: int):
try:
director = await ctrl.get(id)
return DirectorGetSchema.from_orm(director)
except common_exc.NotFoundException as e:
raise http_exc.HTTPNotFoundException(detail=str(e))
@router.post('')
async def create_director(body: DirectorPostSchema):
try:
director = await ctrl.create(**body.model_dump(exclude_none=True))
return DirectorGetSchema.from_orm(director)
except common_exc.CreateException as e:
raise http_exc.HTTPBadRequestException(detail=str(e))
@router.patch('/{id}')
async def update_director(id: int, body: DirectorPatchSchema):
try:
director = await ctrl.update(id, **body.model_dump(exclude_none=True))
return DirectorGetSchema.from_orm(director)
except common_exc.UpdateException as e:
raise http_exc.HTTPBadRequestException(detail=str(e))
except common_exc.NotFoundException as e:
raise http_exc.HTTPNotFoundException(detail=str(e))
@router.delete('/{id}')
async def delete_director(id: int):
try:
await ctrl.delete(id)
return fastapi.responses.Response(status_code=status.HTTP_204_NO_CONTENT)
except common_exc.DeleteException as e:
raise http_exc.HTTPBadRequestException(detail=str(e))
except common_exc.NotFoundException as e:
raise http_exc.HTTPNotFoundException(detail=str(e))
from datetime import date
from pydantic import BaseModel
class DirectorBaseSchema(BaseModel):
first_name: str | None = None
last_name: str | None = None
class DirectorIdSchema(BaseModel):
id: int
class Config:
from_attributes = True
class DirectorListSchema(DirectorBaseSchema, DirectorIdSchema):
pass
class DirectorGetSchema(DirectorBaseSchema, DirectorIdSchema):
born: date
description: str | None
class DirectorPostSchema(DirectorBaseSchema):
born: date
description: str | None = None
class DirectorPatchSchema(DirectorBaseSchema):
born: date | None = None
description: str | None = None
from core.ctrl import BaseController
from db.repositories.movie import GenreRepository
class GenreController(BaseController):
repo = GenreRepository()
import fastapi
from starlette import status
from exceptions import common as common_exc, http as http_exc
from .schemas import GenreBaseSchema, GenreGetSchema
from .ctrl import GenreController
router = fastapi.APIRouter(prefix='/genres', tags=['Genre'])
ctrl = GenreController()
@router.get('')
async def get_genres(query: GenreBaseSchema = fastapi.Depends()):
genres = await ctrl.get_list(**query.model_dump(exclude_none=True))
return [GenreGetSchema.from_orm(genre) for genre in genres]
@router.get('/{id}')
async def get_genre(id: int):
try:
director = await ctrl.get(id)
return GenreGetSchema.from_orm(director)
except common_exc.NotFoundException as e:
raise http_exc.HTTPNotFoundException(detail=str(e))
@router.post('')
async def create_genre(body: GenreBaseSchema):
try:
director = await ctrl.create(**body.model_dump(exclude_none=True))
return GenreGetSchema.from_orm(director)
except common_exc.CreateException as e:
raise http_exc.HTTPBadRequestException(detail=str(e))
@router.patch('/{id}')
async def update_genre(id: int, body: GenreBaseSchema):
try:
director = await ctrl.update(id, **body.model_dump(exclude_none=True))
return GenreGetSchema.from_orm(director)
except common_exc.UpdateException as e:
raise http_exc.HTTPBadRequestException(detail=str(e))
except common_exc.NotFoundException as e:
raise http_exc.HTTPNotFoundException(detail=str(e))
@router.delete('/{id}')
async def delete_genre(id: int):
try:
await ctrl.delete(id)
return fastapi.responses.Response(status_code=status.HTTP_204_NO_CONTENT)
except common_exc.DeleteException as e:
raise http_exc.HTTPBadRequestException(detail=str(e))
except common_exc.NotFoundException as e:
raise http_exc.HTTPNotFoundException(detail=str(e))
from pydantic import BaseModel
class GenreBaseSchema(BaseModel):
name: str | None = None
class GenreIdSchema(BaseModel):
id: int
class Config:
from_attributes = True
class GenreGetSchema(GenreBaseSchema, GenreIdSchema):
pass
import fastapi
from starlette import status
from core.ctrl import BaseController
from exceptions import common as common_exc, http as http_exc
from db.repositories.movie import MovieRepository
from .services import MovieService
class MovieController(BaseController):
repo = MovieRepository()
movie_service = MovieService()
async def get_list(self, **kwargs):
return await self.movie_service.get_list_movie(**kwargs)
async def get(self, id: int):
try:
return await self.movie_service.get_movie(id)
except common_exc.NotFoundException as e:
raise http_exc.HTTPNotFoundException(detail=str(e))
async def create(self, **kwargs):
try:
return await self.movie_service.create_movie(**kwargs)
except common_exc.CreateException as e:
raise http_exc.HTTPBadRequestException(detail=str(e))
except common_exc.NotFoundException:
raise http_exc.HTTPBadRequestException(detail="Director and genre are required for creating a movie.")
async def update(self, id: int, **kwargs):
try:
return await self.movie_service.update_movie(id, **kwargs)
except common_exc.UpdateException as e:
raise http_exc.HTTPBadRequestException(detail=str(e))
except common_exc.NotFoundException as e:
raise http_exc.HTTPNotFoundException(detail=str(e))
async def delete(self, id: int):
try:
await self.movie_service.delete_movie(id)
return fastapi.responses.Response(status_code=status.HTTP_204_NO_CONTENT)
except common_exc.DeleteException as e:
raise http_exc.HTTPBadRequestException(detail=str(e))
except common_exc.NotFoundException as e:
raise http_exc.HTTPNotFoundException(detail=str(e))
import fastapi
from .schemas import (
MovieBaseSchema, MovieListSchema,
MoviePostSchema, MoviePatchSchema
)
from .ctrl import MovieController
router = fastapi.APIRouter(prefix='/movies', tags=['Movie'])
ctrl = MovieController()
@router.get('')
async def get_movies(query: MovieBaseSchema = fastapi.Depends()):
movies = await ctrl.get_list(**query.model_dump(exclude_none=True))
return [MovieListSchema.from_orm(movie) for movie in movies]
@router.get('/{id}')
async def get_movie(id: int):
return await ctrl.get(id)
@router.post('')
async def create_movie(body: MoviePostSchema):
return await ctrl.create(**body.model_dump(exclude_none=True))
@router.patch('/{id}')
async def update_movie(id: int, body: MoviePatchSchema):
return await ctrl.update(id, **body.model_dump(exclude_none=True))
@router.delete('/{id}')
async def delete_movie(id: int):
return await ctrl.delete(id)
from datetime import time
from pydantic import BaseModel
class MovieBaseSchema(BaseModel):
name: str | None = None
year: int | None = None
country: str | None = None
class MovieIdSchema(BaseModel):
id: int
class Config:
from_attributes = True
class MovieListSchema(MovieBaseSchema, MovieIdSchema):
pass
class MovieGetSchema(MovieBaseSchema, MovieIdSchema):
director: int
genre: int
description: str
duration: time
class MoviePostSchema(MovieBaseSchema):
director: int
genre: int
description: str
duration: time
class MoviePatchSchema(MovieBaseSchema):
director: int | None = None
genre: int | None = None
description: str | None = None
duration: time | None = None
from db.repositories.movie import MovieRepository, DirectorRepository, GenreRepository
class MovieService:
movie_repository = MovieRepository()
director_repository = DirectorRepository()
genre_repository = GenreRepository()
async def get_list_movie(self, **kwargs):
return await self.movie_repository.get_list(**kwargs)
async def get_movie(self, id: int):
return await self.movie_repository.get(id)
async def create_movie(self, **kwargs):
director_id = kwargs.get('director')
genre_id = kwargs.get('genre')
director = await self.director_repository.get(director_id)
genre = await self.genre_repository.get(genre_id)
kwargs['director'] = director
kwargs['genre'] = genre
return await self.movie_repository.create(**kwargs)
async def update_movie(self, id: int, **kwargs):
director_id = kwargs.get('director')
genre_id = kwargs.get('genre')
if director_id is not None:
director = await self.director_repository.get(director_id)
kwargs['director'] = director
if genre_id is not None:
genre = await self.genre_repository.get(genre_id)
kwargs['genre'] = genre
return await self.movie_repository.update(id, **kwargs)
async def delete_movie(self, id: int):
return await self.movie_repository.delete(id)
from fastapi import APIRouter
from .director.routes import router as director_route
from .genre.routes import router as genre_route
from .movie.routes import router as movie_route
router = APIRouter(prefix='/api')
@router.get('/greetings')
def get_greetings():
return {'text': 'Greetings!'}
router.include_router(director_route)
router.include_router(genre_route)
router.include_router(movie_route)
from exceptions import common as common_exc, http as http_exc
class BaseController:
repo = None
async def get_list(self, **kwargs) -> list[dict]:
return await self.repo.get_list(**kwargs)
async def get(self, id: int) -> dict:
try:
return await self.repo.get(id)
except common_exc.NotFoundException as e:
raise http_exc.HTTPNotFoundException(detail=str(e))
async def create(self, **data) -> dict:
try:
return await self.repo.create(**data)
except common_exc.CreateException as e:
raise http_exc.HTTPBadRequestException(detail=str(e))
async def update(self, id: int, **kwargs) -> dict:
try:
return await self.repo.update(id, **kwargs)
except common_exc.NotFoundException as e:
raise http_exc.HTTPNotFoundException(detail=str(e))
except common_exc.UpdateException as e:
raise http_exc.HTTPBadRequestException(detail=str(e))
async def delete(self, id: int) -> None:
try:
return await self.repo.delete(id)
except common_exc.NotFoundException as e:
raise http_exc.HTTPNotFoundException(detail=str(e))
except common_exc.DeleteException as e:
raise http_exc.HTTPBadRequestException(detail=str(e))
......@@ -3,7 +3,6 @@ import os
from fastapi import FastAPI
from tortoise.contrib.fastapi import register_tortoise
TORTOISE_ORM = {
'connections': {'default': os.environ.get('database_url')},
'apps': {
......@@ -18,7 +17,8 @@ TORTOISE_ORM = {
def init_db(app: FastAPI) -> None:
register_tortoise(
app,
config=TORTOISE_ORM,
generate_schemas=True,
db_url=os.environ.get('database_url'),
modules={"models": ["db.models.movie"]},
generate_schemas=False,
add_exception_handlers=True,
)
)
......@@ -6,6 +6,7 @@ class Director(BaseModel):
first_name = fields.CharField(max_length=255)
last_name = fields.CharField(max_length=255)
born = fields.DateField()
description = fields.TextField(null=True)
def __str__(self):
return self.first_name
......@@ -23,10 +24,12 @@ class Movie(BaseModel):
year = fields.IntField()
country = fields.CharField(max_length=255)
director = fields.ForeignKeyField(
'models.Director', related_name='director_movies', on_delete=fields.OnDelete.CASCADE
'models.Director', related_name='director_movies',
on_delete=fields.OnDelete.CASCADE
)
genre = fields.ForeignKeyField(
'models.Genre', related_name='genre_movies', on_delete=fields.OnDelete.CASCADE
'models.Genre', related_name='genre_movies',
on_delete=fields.OnDelete.CASCADE
)
description = fields.TextField()
duration = fields.TimeField()
......
import tortoise
from exceptions import common as common_exc
class BaseRepository:
model: tortoise.Model
async def get_list(self, **kwargs) -> list[tortoise.Model]:
return await self.model.filter(**kwargs)
async def get(self, id: int) -> tortoise.Model:
try:
return await self.model.get(id=id)
except tortoise.exceptions.DoesNotExist as e:
raise common_exc.NotFoundException(str(e))
async def create(self, **kwargs) -> tortoise.Model:
try:
return await self.model.create(**kwargs)
except tortoise.exceptions.IntegrityError as e:
raise common_exc.CreateException(str(e))
async def update(self, id: int, **kwargs) -> tortoise.Model:
try:
instance = await self.get(id=id)
await instance.update_from_dict(kwargs).save()
return instance
except tortoise.exceptions.IntegrityError as e:
raise common_exc.UpdateException(str(e))
async def delete(self, id: int) -> None:
try:
instance = await self.get(id=id)
await instance.delete()
except tortoise.exceptions.IntegrityError as e:
raise common_exc.DeleteException(str(e))
from .base import BaseRepository
from db.models import movie
class DirectorRepository(BaseRepository):
model = movie.Director
class GenreRepository(BaseRepository):
model = movie.Genre
class MovieRepository(BaseRepository):
model = movie.Movie
......@@ -25,5 +25,6 @@ async def internal_exc_handler(
request: fastapi.Request, exc: Exception,
) -> JSONResponse:
return JSONResponse(
{'detail': 'Internal Server Error'}, status.HTTP_500_INTERNAL_SERVER_ERROR,
{'detail': 'Internal Server Error'},
status.HTTP_500_INTERNAL_SERVER_ERROR,
)
......@@ -10,13 +10,15 @@ class BaseHTTPException(HTTPException):
class HTTPBadRequestException(BaseHTTPException):
def __init__(
self, status_code: int, detail: Any = None, headers: Dict[str, str] = None
self, status_code: int = status.HTTP_400_BAD_REQUEST, detail: Any = None,
headers: Dict[str, str] | None = None
) -> None:
super().__init__(status.HTTP_400_BAD_REQUEST, detail, headers)
super().__init__(status_code, detail, headers)
class HTTPNotFoundException(BaseHTTPException):
def __init__(
self, status_code: int, detail: Any = None, headers: Dict[str, str] = None
self, status_code: int = status.HTTP_404_NOT_FOUND, detail: Any = None,
headers: Dict[str, str] | None = None
) -> None:
super().__init__(status.HTTP_404_NOT_FOUND, detail, headers)
super().__init__(status_code, detail, headers)
......@@ -3,6 +3,7 @@ import pydantic
from api.router import router
from exceptions import handlers as exc_handlers, http as http_exc
from db.conf import init_db
def setup():
......@@ -18,6 +19,8 @@ def setup():
app = setup()
init_db(app)
if __name__ == '__main__':
import uvicorn
......
from tortoise import BaseDBAsyncClient
async def upgrade(db: BaseDBAsyncClient) -> str:
return """
ALTER TABLE "director" ADD "description" TEXT;"""
async def downgrade(db: BaseDBAsyncClient) -> str:
return """
ALTER TABLE "director" DROP COLUMN "description";"""
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment