Skip to content

Commit 29a4216

Browse files
authored
Improve README and Documentation (#1888)
1 parent 6ca73e0 commit 29a4216

File tree

9 files changed

+276
-346
lines changed

9 files changed

+276
-346
lines changed

README.rst

Lines changed: 85 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,13 @@ Introduction
2020

2121
Tortoise ORM is an easy-to-use ``asyncio`` ORM *(Object Relational Mapper)* inspired by Django.
2222

23-
Tortoise ORM was built with relations in mind and admiration for the excellent and popular Django ORM.
24-
It's engraved in its design that you are working not with just tables, you work with relational data.
25-
2623
You can find the docs at `Documentation <https://tortoise.github.io>`_
2724

2825
.. note::
2926
Tortoise ORM is a young project and breaking changes are to be expected.
3027
We keep a `Changelog <https://tortoise.github.io/CHANGELOG.html>`_ and it will have possible breakage clearly documented.
3128

32-
Tortoise ORM is supported on CPython >= 3.9 for SQLite, MySQL and PostgreSQL and Microsoft SQL Server and Oracle.
29+
Tortoise ORM supports CPython 3.9 and later for SQLite, MySQL, PostgreSQL, Microsoft SQL Server, and Oracle.
3330

3431
Why was Tortoise ORM built?
3532
---------------------------
@@ -51,48 +48,47 @@ It also performs well when compared to other Python ORMs. In `our benchmarks <ht
5148
How is an ORM useful?
5249
---------------------
5350

54-
When you build an application or service that uses a relational database, there is a point where you can't get away with just using parameterized queries or even query builder. You just keep repeating yourself, writing slightly different code for each entity.
55-
Code has no idea about relations between data, so you end up concatenating your data almost manually.
56-
It is also easy to make mistakes in how you access your database, which can be exploited by SQL-injection attacks.
57-
Your data rules are also distributed, increasing the complexity of managing your data, and even worse, could lead to those rules being applied inconsistently.
51+
An Object-Relational Mapper (ORM) abstracts database interactions, allowing developers to work with databases using high-level, object-oriented code instead of raw SQL.
5852

59-
An ORM (Object Relational Mapper) is designed to address these issues, by centralising your data model and data rules, ensuring that your data is managed safely (providing immunity to SQL-injection) and keeping track of relationships so you don't have to.
53+
* Reduces boilerplate SQL, allowing faster development with cleaner, more readable code.
54+
* Helps prevent SQL injection by using parameterized queries.
55+
* Centralized schema and relationship definitions make code easier to manage and modify.
56+
* Handles schema changes through version-controlled migrations.
6057

6158
Getting Started
6259
===============
6360

6461
Installation
6562
------------
66-
First you have to install Tortoise ORM like this:
67-
68-
.. code-block:: bash
69-
70-
pip install tortoise-orm
71-
72-
You can also install with your db driver (`aiosqlite` is builtin):
73-
74-
.. code-block:: bash
75-
76-
pip install "tortoise-orm[asyncpg]"
77-
78-
79-
For `MySQL`:
8063

81-
.. code-block:: bash
64+
The following table shows the available installation options for different databases (note that there are multiple options of clients for some databases):
65+
66+
.. list-table:: Available Installation Options
67+
:header-rows: 1
68+
:widths: 30 70
69+
70+
* - Database
71+
- Installation Command
72+
* - SQLite
73+
- ``pip install tortoise-orm``
74+
* - PostgreSQL (psycopg)
75+
- ``pip install tortoise-orm[psycopg]``
76+
* - PostgreSQL (asyncpg)
77+
- ``pip install tortoise-orm[asyncpg]``
78+
* - MySQL (aiomysql)
79+
- ``pip install tortoise-orm[aiomysql]``
80+
* - MySQL (asyncmy)
81+
- ``pip install tortoise-orm[asyncmy]``
82+
* - MS SQL
83+
- ``pip install tortoise-orm[asyncodbc]``
84+
* - Oracle
85+
- ``pip install tortoise-orm[asyncodbc]``
8286

83-
pip install "tortoise-orm[asyncmy]"
84-
85-
For `Microsoft SQL Server`/`Oracle` (**not fully tested**):
86-
87-
.. code-block:: bash
88-
89-
pip install "tortoise-orm[asyncodbc]"
9087

9188
Quick Tutorial
9289
--------------
9390

94-
The primary entity of tortoise is ``tortoise.models.Model``.
95-
You can start writing models like this:
91+
Define the models by inheriting from ``tortoise.models.Model``.
9692

9793

9894
.. code-block:: python3
@@ -104,35 +100,26 @@ You can start writing models like this:
104100
id = fields.IntField(primary_key=True)
105101
name = fields.TextField()
106102
107-
def __str__(self):
108-
return self.name
109-
110103
111104
class Event(Model):
112105
id = fields.IntField(primary_key=True)
113106
name = fields.TextField()
114107
tournament = fields.ForeignKeyField('models.Tournament', related_name='events')
115108
participants = fields.ManyToManyField('models.Team', related_name='events', through='event_team')
116109
117-
def __str__(self):
118-
return self.name
119-
120110
121111
class Team(Model):
122112
id = fields.IntField(primary_key=True)
123113
name = fields.TextField()
124114
125-
def __str__(self):
126-
return self.name
127-
128-
129-
After you defined all your models, tortoise needs you to init them, in order to create backward relations between models and match your db client with the appropriate models.
130115
131-
You can do it like this:
116+
After defining the models, Tortoise ORM needs to be initialized to establish the relationships between models and connect to the database.
117+
The code below creates a connection to a SQLite DB database with the ``aiosqlite`` client. ``generate_schema`` sets up schema on an empty database.
118+
``generate_schema`` is for development purposes only, check out ``aerich`` or other migration tools for production use.
132119

133120
.. code-block:: python3
134121
135-
from tortoise import Tortoise
122+
from tortoise import Tortoise, run_async
136123
137124
async def init():
138125
# Here we connect to a SQLite DB file.
@@ -145,64 +132,66 @@ You can do it like this:
145132
# Generate the schema
146133
await Tortoise.generate_schemas()
147134
135+
run_async(main())
148136
149-
Here we create a connection to an SQLite database in the local directory called ``db.sqlite3``. Then we discover and initialise the models.
137+
``run_async`` is a helper function to run simple Tortoise scripts. Check out `Documentation <https://tortoise.github.io>`_ for FastAPI, Sanic and other integrations.
150138

151-
Tortoise ORM currently supports the following databases:
139+
With the Tortoise initialized, the models are available for use:
152140

153-
* `SQLite` (requires ``aiosqlite``)
154-
* `PostgreSQL` (requires ``asyncpg``)
155-
* `MySQL` (requires ``asyncmy`` or ``aiomysql``)
156-
* `Microsoft SQL Server`/`Oracle` (requires ``asyncodbc``)
141+
.. code-block:: python3
142+
143+
async def main():
144+
await Tortoise.init(
145+
db_url='sqlite://db.sqlite3',
146+
modules={'models': ['app.models']}
147+
)
148+
await Tortoise.generate_schemas()
157149
158-
``generate_schema`` generates the schema on an empty database. Tortoise generates schemas in safe mode by default which
159-
includes the ``IF NOT EXISTS`` clause, so you may include it in your main code.
150+
# Creating an instance with .save()
151+
tournament = Tournament(name='New Tournament')
152+
await tournament.save()
160153
154+
# Or with .create()
155+
await Event.create(name='Without participants', tournament=tournament)
156+
event = await Event.create(name='Test', tournament=tournament)
157+
participants = []
158+
for i in range(2):
159+
team = await Team.create(name='Team {}'.format(i + 1))
160+
participants.append(team)
161161
162-
After that you can start using your models:
162+
# Many to Many Relationship management is quite straightforward
163+
# (there are .remove(...) and .clear() too)
164+
await event.participants.add(*participants)
163165
164-
.. code-block:: python3
166+
# Iterate over related entities with the async context manager
167+
async for team in event.participants:
168+
print(team.name)
169+
170+
# The related entities are cached and can be iterated in the synchronous way afterwards
171+
for team in event.participants:
172+
pass
173+
174+
# Use prefetch_related to fetch related objects
175+
selected_events = await Event.filter(
176+
participants=participants[0].id
177+
).prefetch_related('participants', 'tournament')
178+
for event in selected_events:
179+
print(event.tournament.name)
180+
print([t.name for t in event.participants])
181+
182+
# Prefetch multiple levels of related entities
183+
await Team.all().prefetch_related('events__tournament')
184+
185+
# Filter and order by related models too
186+
await Tournament.filter(
187+
events__name__in=['Test', 'Prod']
188+
).order_by('-events__participants__name').distinct()
189+
190+
run_async(main())
191+
192+
193+
Learn more at the `documentation site <https://tortoise.github.io>`_
165194

166-
# Create instance by save
167-
tournament = Tournament(name='New Tournament')
168-
await tournament.save()
169-
170-
# Or by .create()
171-
await Event.create(name='Without participants', tournament=tournament)
172-
event = await Event.create(name='Test', tournament=tournament)
173-
participants = []
174-
for i in range(2):
175-
team = await Team.create(name='Team {}'.format(i + 1))
176-
participants.append(team)
177-
178-
# M2M Relationship management is quite straightforward
179-
# (also look for methods .remove(...) and .clear())
180-
await event.participants.add(*participants)
181-
182-
# You can query a related entity with async for
183-
async for team in event.participants:
184-
pass
185-
186-
# After making a related query you can iterate with regular for,
187-
# which can be extremely convenient when using it with other packages,
188-
# for example some kind of serializers with nested support
189-
for team in event.participants:
190-
pass
191-
192-
193-
# Or you can make a preemptive call to fetch related objects
194-
selected_events = await Event.filter(
195-
participants=participants[0].id
196-
).prefetch_related('participants', 'tournament')
197-
198-
# Tortoise supports variable depth of prefetching related entities
199-
# This will fetch all events for Team and in those events tournaments will be prefetched
200-
await Team.all().prefetch_related('events__tournament')
201-
202-
# You can filter and order by related models too
203-
await Tournament.filter(
204-
events__name__in=['Test', 'Prod']
205-
).order_by('-events__participants__name').distinct()
206195

207196
Migration
208197
=========

docs/CONTRIBUTING.rst

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,6 @@ If not you are welcome to open one.
2424
If you have an incomplete change, but won't/can't continue working on it, please create a PR in any case and mark it as ``(WIP)`` so we can help each other.
2525

2626

27-
Have a chat
28-
===========
29-
30-
We have a chatroom on `Gitter <https://gitter.im/tortoise/community>`_
31-
32-
3327
Project structure
3428
=================
3529

0 commit comments

Comments
 (0)