diff --git a/aplicativo.py b/aplicativo.py new file mode 100644 index 00000000..bf5e25b9 --- /dev/null +++ b/aplicativo.py @@ -0,0 +1,74 @@ +# Main class +from typing import List + +from fastapi import Depends, FastAPI, HTTPException +from sqlalchemy.orm import Session + +import crud +import model +import schema +from db_handler import SessionLocal, engine + +model.Base.metadata.create_all(bind=engine) + +# Initializing the app +app = FastAPI(title="CRUD Operations using Python FastAPI") + + +# Database dependency +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() + + +# Get all movies +@app.get('/get-all', response_model=List[schema.Movie]) +def get_all(db: Session = Depends(get_db)): + return crud.get_all(db=db) + + +# Save new movie +@app.post('/add', response_model=schema.MovieAdd) +def add(movie: schema.MovieAdd, db: Session = Depends(get_db)): + movie_id = crud.get_by_id(db=db, sl_id=movie.movie_id) + if movie_id: + print("Resource conflict") + raise HTTPException(status_code=409, detail=f"Resource id {movie_id} already exist") + + return crud.add(db=db, movie=movie) + + +# Delete a movie by id +@app.delete('/delete') +def delete(sl_id: int, db: Session = Depends(get_db)): + details = crud.get_by_id(db=db, sl_id=sl_id) + if not details: + print("Entity not found") + raise HTTPException(status_code=404, detail=f"Resource not found") + try: + crud.delete(db=db, sl_id=sl_id) + except Exception as e: + raise HTTPException(status_code=400, detail=f"Unable to delete: {e}") + + return {"status": "accepted", "code": "202", "message": "Resource deleted"} + + +# Update a movie by id +@app.put('/update', response_model=schema.Movie) +def update(sl_id: int, update_param: schema.UpdateMovie, db: Session = Depends(get_db)): + details = crud.get_by_id(db=db, sl_id=sl_id) + if not details: + print("Entity not found") + raise HTTPException(status_code=404, detail=f"Resource not found") + + return crud.update(db=db, details=update_param, sl_id=sl_id) + + +# Driver code +if __name__ == '__main__': + import uvicorn + + uvicorn.run(app, host="localhost", port=7001, log_level="debug") diff --git a/crud.py b/crud.py new file mode 100644 index 00000000..f05f53ce --- /dev/null +++ b/crud.py @@ -0,0 +1,48 @@ +# Crud operation methods +from sqlalchemy.orm import Session + +import model +import schema + + +def get_all(db: Session): + print("fetching records") + return db.query(model.Movies).all() + + +def get_by_id(db: Session, sl_id: int): + print("fetching record id={}".format(sl_id)) + return db.query(model.Movies).filter(model.Movies.id == sl_id).first() + + +def add(db: Session, movie: schema.MovieAdd): + mv_details = model.Movies( + movie_id=movie.movie_id, + movie_name=movie.movie_name, + director=movie.director, + geners=movie.geners, + membership_required=movie.membership_required, + cast=movie.cast, + streaming_platform=movie.streaming_platform) + print("saving new record") + db.add(mv_details) + db.commit() + db.refresh(mv_details) + return model.Movies(**movie.dict()) + + +def delete(db: Session, sl_id: int): + try: + print("deleting record id={}".format(sl_id)) + db.query(model.Movies).filter(model.Movies.id == sl_id).delete() + db.commit() + except Exception as e: + raise Exception(e) + + +def update(db: Session, sl_id: int, details: schema.UpdateMovie): + print("updating record id={}".format(sl_id)) + db.query(model.Movies).filter(model.Movies.id == sl_id).update(vars(details)) + db.commit() + return db.query(model.Movies).filter(model.Movies.id == sl_id).first() + diff --git a/db_handler.py b/db_handler.py new file mode 100644 index 00000000..5774ec9e --- /dev/null +++ b/db_handler.py @@ -0,0 +1,20 @@ +# Handling database +from sqlalchemy import create_engine +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker + +DATABASE_URL = "sqlite:///./movie_database.db" + +# creating engine +# Setting check_same_thread to False so that the returned connection may be shared across multiple threads +engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False}) + +# bind – An optional Connectable, will assign the bind attribute on the MetaData instance +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) + +# A simple constructor that allows initialization +Base = declarative_base() + + + + diff --git a/model.py b/model.py new file mode 100644 index 00000000..cc55a9c9 --- /dev/null +++ b/model.py @@ -0,0 +1,19 @@ +# Model class +from sqlalchemy import Boolean, Column, Integer, String + +from db_handler import Base + + +class Movies(Base): + # Setting constraints on the table structure + __tablename__ = "movie" + id = Column(Integer, primary_key=True, autoincrement=True, index=True, nullable=False) + movie_id = Column(String) + movie_name = Column(String(255)) + director = Column(String(100)) + geners = Column(String) + membership_required = Column(Boolean) + cast = Column(String(255)) + streaming_platform = Column(String) + + diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..6e9df326 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,29 @@ +fastapi~=0.75.2 +sqlalchemy~=1.4.37 +pydantic~=1.8.2 + +pytest~=7.1.2 +h11~=0.12.0 +pip~=22.1.2 +attrs~=21.4.0 +toml~=0.10.2 +wheel~=0.37.1 +Jinja2~=3.1.2 +anyio~=3.6.1 +sniffio~=1.2.0 +starlette~=0.17.1 +requests~=2.27.1 +uvloop~=0.16.0 +click~=8.1.3 +uvicorn~=0.17.6 +asgiref~=3.5.2 +PyYAML~=6.0 +wsproto~=1.1.0 +greenlet~=1.1.2 +decorator~=5.1.1 +python-multipart~=0.0.5 +httpx~=0.22.0 +setuptools~=62.5.0 +packaging~=21.3 +pyparsing~=3.0.9 +zipp~=3.8.0 \ No newline at end of file diff --git a/schema.py b/schema.py new file mode 100644 index 00000000..760b38bc --- /dev/null +++ b/schema.py @@ -0,0 +1,45 @@ +# Schema class +from typing import Optional + +from pydantic import BaseModel + + +class MovieBase(BaseModel): + movie_name: str + director: str + geners: str + cast: str + + +class MovieAdd(MovieBase): + movie_id: int + # Optional[str] is just a shorthand or alias for Union[str, None]. + streaming_platform: Optional[str] = None + membership_required: bool + + # Behaviour of pydantic can be controlled via the Config class on a model + # to support models that map to ORM objects. Config property orm_mode must be set to True + class Config: + orm_mode = True + + +class Movie(MovieAdd): + id: int + + # Behaviour of pydantic can be controlled via the Config class on a model + # to support models that map to ORM objects. Config property orm_mode must be set to True + class Config: + orm_mode = True + + +class UpdateMovie(BaseModel): + # Optional[str] is just a shorthand or alias for Union[str, None]. + streaming_platform: Optional[str] = None + membership_required: bool + + # Behaviour of pydantic can be controlled via the Config class on a model + # to support models that map to ORM objects. Config property orm_mode must be set to True + class Config: + orm_mode = True + +