
This commit implements a simple movie database backend inspired by IMDb. It includes: - API endpoints for movies, actors, directors and genres - SQLAlchemy models with relationships - Alembic migrations - Pydantic schemas for request/response validation - Search and filtering functionality - Health check endpoint - Complete documentation
120 lines
3.1 KiB
Python
120 lines
3.1 KiB
Python
from typing import Any, List, Optional
|
|
from fastapi import APIRouter, Depends, HTTPException, Query, Path
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.api import crud
|
|
from app.api.schemas.movie import (
|
|
Movie, MovieCreate, MovieUpdate, MovieDetails, MovieList
|
|
)
|
|
from app.db.session import get_db
|
|
|
|
router = APIRouter(prefix="/movies")
|
|
|
|
|
|
@router.get("", response_model=MovieList)
|
|
def read_movies(
|
|
db: Session = Depends(get_db),
|
|
skip: int = Query(0, ge=0),
|
|
limit: int = Query(100, ge=1, le=100),
|
|
title: Optional[str] = None,
|
|
director_id: Optional[int] = None,
|
|
genre_id: Optional[int] = None,
|
|
actor_id: Optional[int] = None,
|
|
min_rating: Optional[float] = Query(None, ge=0, le=10),
|
|
year: Optional[int] = None,
|
|
) -> Any:
|
|
"""
|
|
Retrieve all movies with optional filtering.
|
|
"""
|
|
# Build filter dict from query parameters
|
|
filters = {}
|
|
if title:
|
|
filters["title"] = title
|
|
if director_id:
|
|
filters["director_id"] = director_id
|
|
if genre_id:
|
|
filters["genre_id"] = genre_id
|
|
if actor_id:
|
|
filters["actor_id"] = actor_id
|
|
if min_rating is not None:
|
|
filters["min_rating"] = min_rating
|
|
if year:
|
|
filters["year"] = year
|
|
|
|
movies, total = crud.movie.get_multi(db, skip=skip, limit=limit, filters=filters)
|
|
return {"data": movies, "total": total}
|
|
|
|
|
|
@router.post("", response_model=Movie, status_code=201)
|
|
def create_movie(
|
|
*,
|
|
db: Session = Depends(get_db),
|
|
movie_in: MovieCreate,
|
|
) -> Any:
|
|
"""
|
|
Create a new movie.
|
|
"""
|
|
movie = crud.movie.create(db, movie_in=movie_in)
|
|
return movie
|
|
|
|
|
|
@router.get("/{movie_id}", response_model=MovieDetails)
|
|
def read_movie(
|
|
*,
|
|
db: Session = Depends(get_db),
|
|
movie_id: int = Path(..., ge=1),
|
|
) -> Any:
|
|
"""
|
|
Get a specific movie by ID.
|
|
"""
|
|
movie = crud.movie.get(db, movie_id=movie_id)
|
|
if not movie:
|
|
raise HTTPException(status_code=404, detail="Movie not found")
|
|
return movie
|
|
|
|
|
|
@router.put("/{movie_id}", response_model=Movie)
|
|
def update_movie(
|
|
*,
|
|
db: Session = Depends(get_db),
|
|
movie_id: int = Path(..., ge=1),
|
|
movie_in: MovieUpdate,
|
|
) -> Any:
|
|
"""
|
|
Update a movie.
|
|
"""
|
|
movie = crud.movie.get(db, movie_id=movie_id)
|
|
if not movie:
|
|
raise HTTPException(status_code=404, detail="Movie not found")
|
|
|
|
movie = crud.movie.update(db, db_movie=movie, movie_in=movie_in)
|
|
return movie
|
|
|
|
|
|
@router.delete("/{movie_id}", status_code=204, response_model=None)
|
|
def delete_movie(
|
|
*,
|
|
db: Session = Depends(get_db),
|
|
movie_id: int = Path(..., ge=1),
|
|
) -> Any:
|
|
"""
|
|
Delete a movie.
|
|
"""
|
|
success = crud.movie.delete(db, movie_id=movie_id)
|
|
if not success:
|
|
raise HTTPException(status_code=404, detail="Movie not found")
|
|
return None
|
|
|
|
|
|
@router.get("/search/{query}", response_model=List[Movie])
|
|
def search_movies(
|
|
*,
|
|
db: Session = Depends(get_db),
|
|
query: str = Path(..., min_length=2),
|
|
limit: int = Query(10, ge=1, le=100),
|
|
) -> Any:
|
|
"""
|
|
Search for movies by title or overview.
|
|
"""
|
|
movies = crud.movie.search(db, query=query, limit=limit)
|
|
return movies |