|
1 |
| -============= |
2 |
| -Relationships |
3 |
| -============= |
| 1 | +======================== |
| 2 | +Loaders and Relationship |
| 3 | +======================== |
4 | 4 |
|
5 |
| -As for now (April 2018) GINO has no full support for relationships. For one |
6 |
| -thing, we are still trying to find a decent way implementing relationships, for |
7 |
| -another, we insist explicit code style in asynchronous programming and that |
8 |
| -conflicts with some usual ORM relationship patterns. Still, GINO doesn't stop |
9 |
| -you from using relationships in the database through foreign keys or whatever |
10 |
| -magic, and gradually provides more features to support doing so. |
| 5 | +Loaders are used to load database row results into objects. |
| 6 | + |
| 7 | +GINO doesn't support automated relationship. We insist explicit code style in |
| 8 | +asynchronous programming and that conflicts with some usual ORM relationship |
| 9 | +patterns. Instead, GINO provides a rich loader system to assist you with manual |
| 10 | +relationships through foreign keys or whatever magic. That means, you are |
| 11 | +responsible for writing the queries, and GINO could assemble objects for you |
| 12 | +from the database result with loaders you defined. |
11 | 13 |
|
12 | 14 |
|
13 | 15 | Model Loader
|
@@ -68,12 +70,6 @@ there're also other loaders that could turn the database rows into different
|
68 | 70 | results like based on your definition. GINO provides the Loader Expression
|
69 | 71 | feature for you to easily assemble complex loaders.
|
70 | 72 |
|
71 |
| - |
72 |
| -.. tip:: |
73 |
| - |
74 |
| - This is less relevant to relationships, please skip to the next section if |
75 |
| - it's not helpful for you. |
76 |
| - |
77 | 73 | Here is an example using all loaders at once::
|
78 | 74 |
|
79 | 75 | uid, user, sep, cols = await db.select([User]).gino.load(
|
@@ -389,3 +385,78 @@ Similarly, you can build many-to-many relationships in the same way::
|
389 | 385 |
|
390 | 386 | Likewise, there is for now no way to modify the relationships automatically,
|
391 | 387 | you'll have to manually create, delete or modify ``ParentXChild`` instances.
|
| 388 | + |
| 389 | + |
| 390 | +Advanced Usage of Loaders |
| 391 | +------------------------- |
| 392 | + |
| 393 | +You could use combined loaders flexibly in complex queries - loading |
| 394 | +relationships is just one special use case. For `example |
| 395 | +<https://github.com/fantix/gino/issues/308>`_, you could load the count of |
| 396 | +visits at the same time of loading each user, by using a tuple loader with two |
| 397 | +items - model loader for the user, and column loader for the count:: |
| 398 | + |
| 399 | + import asyncio |
| 400 | + import random |
| 401 | + import string |
| 402 | + |
| 403 | + import gino |
| 404 | + from gino.loader import ColumnLoader |
| 405 | + |
| 406 | + db = gino.Gino() |
| 407 | + |
| 408 | + |
| 409 | + class User(db.Model): |
| 410 | + __tablename__ = 'users' |
| 411 | + |
| 412 | + id = db.Column(db.Integer(), primary_key=True) |
| 413 | + name = db.Column(db.Unicode()) |
| 414 | + |
| 415 | + |
| 416 | + class Visit(db.Model): |
| 417 | + __tablename__ = 'visits' |
| 418 | + |
| 419 | + id = db.Column(db.Integer(), primary_key=True) |
| 420 | + time = db.Column(db.DateTime(), server_default='now()') |
| 421 | + user_id = db.Column(db.ForeignKey('users.id')) |
| 422 | + |
| 423 | + |
| 424 | + async def main(): |
| 425 | + async with db.with_bind('postgresql://localhost/gino'): |
| 426 | + await db.gino.create_all() |
| 427 | + |
| 428 | + for i in range(random.randint(5, 10)): |
| 429 | + u = await User.create( |
| 430 | + name=''.join(random.choices(string.ascii_letters, k=10))) |
| 431 | + for v in range(random.randint(10, 20)): |
| 432 | + await Visit.create(user_id=u.id) |
| 433 | + |
| 434 | + visits = db.func.count(Visit.id) |
| 435 | + q = db.select([ |
| 436 | + User, |
| 437 | + visits, |
| 438 | + ]).select_from( |
| 439 | + User.outerjoin(Visit) |
| 440 | + ).group_by( |
| 441 | + *User, |
| 442 | + ).gino.load((User, ColumnLoader(visits))) |
| 443 | + async with db.transaction(): |
| 444 | + async for user, visits in q.iterate(): |
| 445 | + print(user.name, visits) |
| 446 | + |
| 447 | + await db.gino.drop_all() |
| 448 | + |
| 449 | + |
| 450 | + asyncio.run(main()) |
| 451 | + |
| 452 | +Using alias to get ID-ascending pairs from the same table:: |
| 453 | + |
| 454 | + ua1 = User.alias() |
| 455 | + ua2 = User.alias() |
| 456 | + join_query = select([ua1, ua2]).where(ua1.id < ua2.id) |
| 457 | + loader = ua1.load('id'), ua2.load('id') |
| 458 | + result = await join_query.gino.load(loader).all() |
| 459 | + print(result) # e.g. [(1, 2), (1, 3), (2, 3)] |
| 460 | + |
| 461 | +Potentially there could be a lot of different use cases of loaders. We'll add |
| 462 | +more inspiration here in the future. |
0 commit comments