fastapi / fastapi

FastAPI framework, high performance, easy to learn, fast to code, ready for production
https://fastapi.tiangolo.com/
MIT License
76.81k stars 6.52k forks source link

Router set query params from model dynamicly #5604

Closed shurshilov closed 1 year ago

shurshilov commented 1 year ago

First Check

Commit to Help

Example Code

# any pydantic model
        Model 
        args1 = Model.dict()
        async def route(
            response: Response,
            req: Request,
            _end: int = 25,
            _order="DESC",
            _sort="id",
            _start: int = 0,
            **args1
        ):

#and now i do like this 
        async def route(
            response: Response,
            req: Request,
            _end: int = 25,
            _order="DESC",
            _sort="id",
            _start: int = 0,
        ):
            try:
                fields_store = Model.dict()
                filter = {
                    key: req.query_params._dict[key]
                    for key in req.query_params._dict.keys()
                    if key in fields_store
                }

# second variant i change React admin frontend to send me filter param
        async def route(
            response: Response,
            req: Request,
            _end: int = 25,
            _order="DESC",
            _sort="id",
            _start: int = 0,
            _filter: Json = None
        ):
But how can i parse value received from frontend in this case?
May be I need do additional field ModelFiter on any models inside ? and use it?

Description

I try do that filter sended to query parametrs as field=values work. Its standart REST function for filter data for example in REACT ADMIN and I can catch all query params from request compare it with model fields and if equal that build filter for frontend But i want use dynamicly set all query params in router from model with additional params as sort paginations and other Because describe all params in each router take long time and also i use CRUD generator fast API and its not compatible. just set **Model.dict() not work because need describe annotation also

Operating System

Windows

Operating System Details

No response

FastAPI Version

0.78

Python Version

3.9.1

Additional Context

No response

tiangolo commented 1 year ago

Hey there, please provide a self contained example that shows your use case and allows me to copy paste and replicate it here, otherwise it's a bit difficult to understand your intention and your problem. ☕

shurshilov commented 1 year ago

Hello, yes, I solved the problem by changing the frontend. But in principle, such functionality would probably come in handy. Once again, when a request arrives at the endpoint with a filter parameter, say filter={id:1, name:aaa, age:25} naturally encoded in the query string, then it's all easy to extract, BUT some JS libraries send not a filter object, but unpack parameter and it turns out a request of this type www.example.com/myendpoint?id=1&name=aaa&age=25 and it turns out that the parameters are already unpacked and you have to write a little code to validate them. But in my case, I just packed in filter

from fastapi import APIRouter, Depends, Response, Request
from typing import Any, Type, Union, TypeVar
from pydantic import Json

from ..const import LOG
from ..managers.QueryManager import QueryManagerObject
from ..external.auth import (
    verify_token_header_auth_bearer_scheme,
    header_auth_bearer_scheme,
)
from ..models_orm.base import Model as BaseModel

T = TypeVar("T", bound=BaseModel)

class CRUDRouterGenerator(APIRouter):
    Model = None

    def __init__(
        self,
        Model: Type[T],
        search_route: bool = True,
        create_route: bool = True,
        update_route: bool = True,
        delete_route: bool = True,
        get_route: bool = True,
        **kwargs: Any,
    ) -> None:
        self.Model = Model

        super().__init__(**kwargs)

        if search_route:
            self.add_api_route(
                f"{self.Model.__route__}",
                self.search(),
                response_model=list[self.Model],
                methods=["GET"],
            )
        if create_route:
            self.add_api_route(f"{Model.__route__}", self.create(), methods=["POST"])
        if update_route:
            self.add_api_route(
                f"{Model.__route__}/{{id}}", self.update(), methods=["PUT"]
            )
        if delete_route:
            self.add_api_route(
                f"{Model.__route__}/{{id}}", self.delete(), methods=["DELETE"]
            )
        if get_route:
            self.add_api_route(
                f"{Model.__route__}/{{id}}",
                self.get(),
                methods=["GET"],
                response_model=Model,
                response_model_exclude_none=True,
            )

    def search(self, *args: Any, **kwargs: Any) -> callable:
        Model = self.Model

        async def route(
            response: Response,
            req: Request,
            _end: int = 25,
            _order="DESC",
            _sort="id",
            _start: int = 0,
            _filter: Json = None,
        ):
            try:
                if _filter:
                    #do validation

                records = await QueryManagerObject.search(
                    Model,
                    Model.get_store_fields(),
                    _end,
                    _order,
                    _sort,
                    _start,
                    _filter,
                )
                count_total = await QueryManagerObject.table_len(Model)
                response.headers["X-Total-Count"] = str(count_total)
                return records
            except Exception as e:
                LOG.exception(f"Error when {Model.__name__} fetching: {e.__context__}")

        return route
tiangolo commented 1 year ago

Thanks for reporting back and closing the issue 👍

github-actions[bot] commented 1 year ago

Assuming the original need was handled, this will be automatically closed now. But feel free to add more comments or create new issues or PRs.