How to use join?
Closed this issue · 2 comments
I have a problem now, Let's say I have
class User(Table):
id = Integer(primary_key=True)
username = Varchar()
......
class Posts(Table):
id = Integer(primary_key=True)
poster = ForeignKey(references=User)
title = Varchar()
class Comments(Table):
id = Integer(primary_key=True)
commenter = ForeignKey(references=User)
content = Text()
......
I want to get the corresponding number of Comments directly through the Posts table, but I checked the documentation and didn't see the relevant usage.
But it’s not complicated to implement in SQL, I just don’t know how to use piccolo-orm to implement it.
SQL:
SELECT p.*, COUNT(c.id) AS comment_count
FROM posts p
LEFT JOIN comments c ON p.id = c.post_id
GROUP BY p.id
ORDER BY comment_count DESC
LIMIT 10;
What I mean is, instead of writing multiple entries, do it in one
Can you help me answer this question? thx
Yeah, it's not super easy with Piccolo at the moment - it abstracts away joins, which helps in most situations, but can be harder in others.
I usually solve these problems using a sub select:
from piccolo.columns import Integer, Varchar, ForeignKey,Text
from piccolo.engine.postgres import PostgresEngine
from piccolo.table import Table, create_db_tables, drop_db_tables
from piccolo.query import SelectRaw
DB = PostgresEngine({'database': 'piccolo_left_join_test'})
class User(Table, db=DB):
id = Integer(primary_key=True)
username = Varchar()
class Posts(Table, db=DB):
id = Integer(primary_key=True)
poster = ForeignKey(references=User)
title = Varchar()
class Comments(Table, db=DB):
id = Integer(primary_key=True)
commenter = ForeignKey(references=User)
content = Text()
async def main():
await drop_db_tables(User, Posts, Comments)
await create_db_tables(User, Posts, Comments)
###########################################################################
# Create some test data
await User(id=1, username='Bob').save()
await User(id=2, username='Alice').save()
await Posts(id=1, poster=1, title='Bob post 1').save()
await Posts(id=2, poster=1, title='Bob post 2').save()
await Posts(id=3, poster=2, title='Alice post 1').save()
await Comments(id=1, commenter=1, content='Bob comment 1').save()
await Comments(id=2, commenter=1, content='Bob comment 2').save()
await Comments(id=3, commenter=2, content='Alice comment 1').save()
###########################################################################
# Example query
response = await Posts.select(
Posts.all_columns(),
SelectRaw(
"(SELECT COUNT(*) FROM comments WHERE commenter = posts.poster) AS comment_count"
)
)
print(response)
if __name__ == '__main__':
import asyncio
asyncio.run(main())
是的,目前 Piccolo 并不是那么容易 - 它抽象了连接,这在大多数情况下都有帮助,但在其他情况下可能会更困难。
我通常使用子选择来解决这些问题:
from piccolo.columns import Integer, Varchar, ForeignKey,Text from piccolo.engine.postgres import PostgresEngine from piccolo.table import Table, create_db_tables, drop_db_tables from piccolo.query import SelectRaw DB = PostgresEngine({'database': 'piccolo_left_join_test'}) class User(Table, db=DB): id = Integer(primary_key=True) username = Varchar() class Posts(Table, db=DB): id = Integer(primary_key=True) poster = ForeignKey(references=User) title = Varchar() class Comments(Table, db=DB): id = Integer(primary_key=True) commenter = ForeignKey(references=User) content = Text() async def main(): await drop_db_tables(User, Posts, Comments) await create_db_tables(User, Posts, Comments) ########################################################################### # Create some test data await User(id=1, username='Bob').save() await User(id=2, username='Alice').save() await Posts(id=1, poster=1, title='Bob post 1').save() await Posts(id=2, poster=1, title='Bob post 2').save() await Posts(id=3, poster=2, title='Alice post 1').save() await Comments(id=1, commenter=1, content='Bob comment 1').save() await Comments(id=2, commenter=1, content='Bob comment 2').save() await Comments(id=3, commenter=2, content='Alice comment 1').save() ########################################################################### # Example query response = await Posts.select( Posts.all_columns(), SelectRaw( "(SELECT COUNT(*) FROM comments WHERE commenter = posts.poster) AS comment_count" ) ) print(response) if __name__ == '__main__': import asyncio asyncio.run(main())
Thanks for the reply, I think I know how I should encapsulate it