tortoise / tortoise-orm

Familiar asyncio ORM for python, built with relations in mind
https://tortoise.github.io
Apache License 2.0
4.55k stars 374 forks source link

can you create a example for fastapi? #188

Closed sylvoslee closed 4 years ago

sylvoslee commented 5 years ago

Is your feature request related to a problem? Please describe. A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

Describe the solution you'd like A clear and concise description of what you want to happen.

Describe alternatives you've considered A clear and concise description of any alternative solutions or features you've considered.

Additional context Add any other context about the feature request here.

grigi commented 5 years ago

As I see it FastAPI is basically starlette with Pydantic for serialisation, and then some extra spice. So a prerequisite to make the integration seamless, we need Pydantic serialisation support.

Wow, this can get quite big, it would end up to be a DRF-like framework when all is done, which would be awesome.

mojimi commented 5 years ago

If we're going to have big plans in mind, might as well consider graphql support.

FastAPI already has integrations with Graphene, so it's only natural!

Since neither Graphene or FastAPI have any database dependency, shouldn't be too hard to get them working

grigi commented 5 years ago

Isn't the whole point of GraphQL so that one can request a compound object, or a partial one? How does Graphene ensure that performs well? Surely it would need to know about relationships etc, and the DB layer needs to be able to use the hints for performance optimization (e.g. prefetch/select related?)

Sounds like a naïve implementation would leave a LOT of room for optimization to me?

But still, baby steps. I still have to actually use Pydantic to see how it works... Or you can help us get there faster :grin:

mojimi commented 5 years ago

@grigi Graphene is merely an interface, you implement all the object-gathering methods, the example in their website make it quite clear.

As for relationship performance, there are some clever things you can do by making use of how tortoise-orm defers QuerySet execution through await

Basically, you build your coroutines without awaiting them, and keep adding to them, and then you await at the end. I'm working on a big product that is using tortoise-orm but we're still at early stages, when we get to heavy production we plan to try to implement this sharded concept.

But you'd definitely need to extend Graphene base classes to accomodate await/async.

And you can also use transactions

Here's a simple pseudocode-ish concept:

class Query(TortoiseGraphQL):

    queryset = None

    def resolve_book(self, filter):
      self.queryset = Book.filter(filter)

    def resolve_author(self, filter):
      self.queryset.prefetch_related('author')

    async def resolve_all(self):
      results = await self.queryset
      return results.to_json()

schema = graphene.Schema(query=Query)

schema.execute('''
  query {
    Book {
       Author
    }
  }
''')

Disclaimer : never actually used Graphene or GraphQL 😄

grigi commented 5 years ago

I used GraphQL from an consumer POV, and the thing that got me was how to make partial matches performant? Because the API I used didn't speed up if I requested a small portion. So obviously it was not very "smart" about it.

I have never used Graphene either. Still I'm more interested in Pydantic at this stage. I wonder if one could map Graphene over Pydantic? (for an initial naïve implementation) It's always easier to work on optimizations after you have working repeatable benchmarks. And since the GraphQL interface is well defined, we can do anything we want as long as the tests pass.

I did a talk about my experiences in optimization once. Where the first step after setting up a a benchmark suite and profiling the data was to remove nearly all the "obvious optimizations" as they actually made it slower…

Please report back with your experiences re building a GraphQL interface using this proposed stack?

leandrob13 commented 4 years ago

I tested it in FastAPI and it was as straight forward as shown in the docs:

def create_app(r: APIRouter) -> FastAPI:
    app = FastAPI()
    app.include_router(r)
    register_tortoise(
        app, db_url=DATABASE_URL, modules={"models": ["app.db.postgres.models"]}, generate_schemas=True
    )
    return app

Probably what needs to be specified is that FastAPI is in fact a Starlette app and it can be initialized the same way.

grigi commented 4 years ago

Yes, we can do that. Also, we now have pydantic serialisation support, so we could also build an example that provides a more complete fastapi experience.