tortoise / tortoise/tortoise-orm

Add execute method to QuerySetSingle and AwaitableQuery for sync contexts

Open
#2,028 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
5.6k
Forks
516
Avg merge
2d 21h
Merged PRs (30d)
9

Description

I often work with Tortoise ORM in environments where I need to run queries synchronously, such as in scripts or synchronous functions. Currently, QuerySetSingle and AwaitableQuery only support await, which makes integrating with sync code cumbersome. And seeing as all the other query types are subclass's of AwaitableQuery it would make it implement a sync query system quite simply

I propose adding an execute method to both classes that allows running queries in a blocking (sync) or non-blocking (fire-and-forget) manner. Here's a sample implementation:

def execute(self, blocking: bool = True) -> Any | Future[Any] | None:
    """
    Execute the QuerySet in a sync context or fire-and-forget.

    Parameters
    ----------
    blocking : bool
        If True (default), waits for the result and returns it.
        If False, schedules execution and returns a Future immediately.

    Returns
    -------
    If blocking=True: the query result.
    If blocking=False: concurrent.futures.Future
    """
    import asyncio
    import nest_asyncio

    try:
        loop = asyncio.get_event_loop()
    except RuntimeError:
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
    nest_asyncio.apply(loop=loop)
    if blocking:
        return loop.run_until_complete(self)
    else:
        async def _run() -> Coroutine[Any, Any, None]:
            return await self
        return asyncio.run_coroutine_threadsafe(_run(), loop)

This addition would allow seamless use of Tortoise ORM in synchronous code without requiring major refactoring or the use of workarounds like asyncio.run().

Benefits:

  • Easier integration with sync scripts or legacy code.
  • Provides a simple fire-and-forget option with blocking=False.
  • Avoids repetitive boilerplate for creating event loops in sync contexts.

I’d be happy to contribute a PR if the idea is acceptable.

Thanks for considering!

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by locating the QuerySetSingle and AwaitableQuery definitions and reviewing how their await behavior is implemented. Determine how blocking and fire-and-forget execution should behave across synchronous contexts and existing query types. Done means both classes expose the proposed execute behavior without breaking asynchronous usage; the issue does not name tests to run.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
backend, database
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.