cavargov
cavargov

Reputation: 445

One path operation is ignored if the paths are the same, but one of them has query parameters

I'd like to have two path operations with the same path @app.get("/movies/", ..):

@app.get("/movies/", response_model=schemas.Movie)
def read_movie_by_title(title: str, db: Session = Depends(get_db)):
    db_movie = crud.get_movie_by_title(db, title=title)
    return db_movie


@app.get("/movies/", response_model=List[schemas.Movie])
def read_movies(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
    db_movies = crud.get_movies(db, skip=skip, limit=limit)
    return db_movies

As You can see, the first one is for getting a movie by its title and has a query parameter (title) and the second one is for getting a list. When I check the generated docs, the one for read_movie_by_title is missing.

I tried to solve this by changing the path for read_movie_by_title to /movies by removing /, but I don't like this solution at all.

So the question is: Is there a way to have two equal paths, but one with query parameters, or do I need to do this in a different way? Any suggestions?

Upvotes: 0

Views: 76

Answers (2)

Jason Rebelo Neves
Jason Rebelo Neves

Reputation: 1281

The answer to your question (Is there a way to have two equal paths, but one with query parameters) is no, the only way to differentiate a path is by its method (GET, PUT, POST, DELETE, ...).

You do however have multiple ways of achieving your desired result.

REST approach

The standard REST approach to this would be to define a different path for the title based retrieval, such as /movies/{title}, like in the first example shown in the documentation for path parameters @ FastAPI.

If you want to implement a search feature instead of a direct retrieval by unique title method, you can do something like this:

@app.get("/movies", response_model=List[schemas.Movie])
def read_movies(title: Optional[str] = None, skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
    # list of filters
    filters = []
    if title: # if it's not none
        filters.append(models.Movie.title.ilike(f"%{title}%"))
    
    db_movies = crud.get_movies(db, filters=filters, skip=skip, limit=limit)
    return db_movies

and within your crud operation you would query it something like:

    return session.query(models.Movie).filter(*filters).all()

Simpler approach that does what you want with one method

@app.get("/movies", response_model=List[schemas.Movie])
def read_movies(title: Optional[str] = None, skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
    if title: # if it's not none
        return crud.get_movie_by_title(db, title=title)
    
    return crud.get_movies(db, filters=filters, skip=skip, limit=limit)

Though the latter might confuse the consumer of your API, hence the REST approach will be the most recommended one.

Upvotes: 0

aneroid
aneroid

Reputation: 15987

You could use Path Parameters instead of Query parameters for the first one, by changing the endpoint to "/movies/{title}":

@app.get("/movies/{title}", response_model=schemas.Movie)
def read_movie_by_title(title: str, db: Session = Depends(get_db)):
    db_movie = crud.get_movie_by_title(db, title=title)
    return db_movie

# 2nd remains the same
@app.get("/movies/", response_model=List[schemas.Movie])
def read_movies(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
    db_movies = crud.get_movies(db, skip=skip, limit=limit)
    return db_movies

Upvotes: 1

Related Questions