Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tortoise ORM tutorial missing context? #1560

Open
ZackPlauche opened this issue Feb 25, 2024 · 4 comments
Open

Tortoise ORM tutorial missing context? #1560

ZackPlauche opened this issue Feb 25, 2024 · 4 comments

Comments

@ZackPlauche
Copy link

ZackPlauche commented Feb 25, 2024

Describe the bug
I'm going through the tortoise-orm tutorial and I'm getting bugs at every step, and some parts are confusing.

In this part, I didn't understand where run_async was coming from or where I was supposed to be putting it, so I just put it at the bottom of this script once I finally found out

from tortoise import Tortoise, run_async

async def init():
    # Here we create a SQLite DB using file "db.sqlite3"
    #  also specify the app name of "models"
    #  which contain models from "app.models"
    await Tortoise.init(
        db_url='sqlite://db.sqlite3',
        modules={'models': ['app.models']}
    )
    # Generate the schema
    await Tortoise.generate_schemas()


run_async(init())

Then it said that after you run this you should be able to run the following, so I added it below that part:

# Create instance by save
tournament = Tournament(name='New Tournament')
await tournament.save()

# Or by .create()
await Event.create(name='Without participants', tournament=tournament)
event = await Event.create(name='Test', tournament=tournament)
participants = []
for i in range(2):
    team = await Team.create(name='Team {}'.format(i + 1))
    participants.append(team)

# M2M Relationship management is quite straightforward
# (look for methods .remove(...) and .clear())
await event.participants.add(*participants)

# You can query related entity just with async for
async for team in event.participants:
    pass

# After making related query you can iterate with regular for,
# which can be extremely convenient for using with other packages,
# for example some kind of serializers with nested support
for team in event.participants:
    pass


# Or you can make preemptive call to fetch related objects,
# so you can work with related objects immediately
selected_events = await Event.filter(
    participants=participants[0].id
).prefetch_related('participants', 'tournament')
for event in selected_events:
    print(event.tournament.name)
    print([t.name for t in event.participants])

# Tortoise ORM supports variable depth of prefetching related entities
# This will fetch all events for team and in those team tournament will be prefetched
await Team.all().prefetch_related('events__tournament')

# You can filter and order by related models too
await Tournament.filter(
    events__name__in=['Test', 'Prod']
).order_by('-events__participants__name').distinct()

First thing it says is that "await can't be used outside of a function".

What are the docs missing for me to be able to use this properly?
To Reproduce
Steps to reproduce the behavior, preferably a small code snippet.
Follow the getting tutorial from scratch as a beginner who knows nothing about using async in Python and type the code in a file.

Expected behavior
A clear and concise description of what you expected to happen.
That I could use the code in the tutorial anywhere.

@vlakius
Copy link

vlakius commented Apr 24, 2024

This is not related to tortoise orm.

from the python docs:

6.4. Await expression
Suspend the execution of coroutine on an awaitable object. Can only be used inside a coroutine function.

@ZackPlauche
Copy link
Author

This is not related to tortoise orm.

from the python docs:

6.4. Await expression
Suspend the execution of coroutine on an awaitable object. Can only be used inside a coroutine function.

I get that the error might be from somewhere else... But I would expect the example code from the tutorial to work and be useful otherwise it's useless.

The code I wrote is directly from the docs at the time of me posting it.

How can I make it work? What did I do wrong?

@vlakius
Copy link

vlakius commented Apr 24, 2024

ok

  1. Create a folder structure like this:
├── app
│   ├── models.py
├── db.sqlite3
├── main.py

in models.py

from tortoise import fields
from tortoise.models import Model


class Tournament(Model):
    # Defining `id` field is optional, it will be defined automatically
    # if you haven't done it yourself
    id = fields.IntField(pk=True)
    name = fields.CharField(max_length=255)

    # Defining ``__str__`` is also optional, but gives you pretty
    # represent of model in debugger and interpreter
    def __str__(self):
        return self.name


class Event(Model):
    id = fields.IntField(pk=True)
    name = fields.CharField(max_length=255)
    # References to other models are defined in format
    # "{app_name}.{model_name}" - where {app_name} is defined in tortoise config
    tournament = fields.ForeignKeyField("models.Tournament", related_name="events")
    participants = fields.ManyToManyField(
        "models.Team", related_name="events", through="event_team"
    )

    def __str__(self):
        return self.name


class Team(Model):
    id = fields.IntField(pk=True)
    name = fields.CharField(max_length=255)

    def __str__(self):
        return self.name

in main.py

from tortoise import Tortoise, run_async

from app.models import Event, Team, Tournament


async def init():
    # Here we create a SQLite DB using file "db.sqlite3"
    #  also specify the app name of "models"
    #  which contain models from "app.models"
    await Tortoise.init(
        db_url="sqlite://db.sqlite3", modules={"models": ["app.models"]}
    )
    # Generate the schema
    await Tortoise.generate_schemas()


async def test_task():
    # Create instance by save
    tournament = Tournament(name="New Tournament")
    await tournament.save()

    # Or by .create()
    await Event.create(name="Without participants", tournament=tournament)
    event = await Event.create(name="Test", tournament=tournament)
    participants = []
    for i in range(2):
        team = await Team.create(name="Team {}".format(i + 1))
        participants.append(team)

    # M2M Relationship management is quite straightforward
    # (look for methods .remove(...) and .clear())
    await event.participants.add(*participants)

    # You can query related entity just with async for
    async for team in event.participants:
        pass

    # After making related query you can iterate with regular for,
    # which can be extremely convenient for using with other packages,
    # for example some kind of serializers with nested support
    for team in event.participants:
        pass

    # Or you can make preemptive call to fetch related objects,
    # so you can work with related objects immediately
    selected_events = await Event.filter(
        participants=participants[0].id
    ).prefetch_related("participants", "tournament")
    for event in selected_events:
        print(event.tournament.name)
        print([t.name for t in event.participants])

    # Tortoise ORM supports variable depth of prefetching related entities
    # This will fetch all events for team and in those team tournament will be prefetched
    await Team.all().prefetch_related("events__tournament")

    # You can filter and order by related models too
    await Tournament.filter(events__name__in=["Test", "Prod"]).order_by(
        "-events__participants__name"
    ).distinct()


async def main():
    await init()
    await test_task()


if __name__ == "__main__":
    run_async(main())

I agree that the documentation examples can be improved

@ZackPlauche
Copy link
Author

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants