在Django中进行数据库JOIN
我想做一个和这个 SQL 查询相同的操作:SELECT user_name, item_name FROM users, items WHERE users.favor_item_id = items.item_id
,也就是返回用户名字和物品名字的配对。在数据库里,一个用户可以有多个喜欢的物品。
我很好奇,这个 SQL 查询在 Django 中应该怎么写呢?
我最开始的想法是从 USERS 表里列出所有的(用户,喜欢的物品 ID)配对,然后根据物品 ID 去查找物品名字。但是这样似乎会对 ITEM 表进行 N 次查询(N 是配对的数量),这样复杂度是 O(NlogM)(M 是 ITEM 表里的物品数量),而用上面的 SQL 查询,复杂度是 O(N)。
有没有更高效的方法在 Django(或者任何 ORM 系统)中实现这个呢?
2 个回答
2
假设有这样的模型:
class User(models.Model):
name = models.CharField(max_length=32)
favorite = models.ForeignKey(Item)
class Item(models.Model):
name = models.CharField(max_length=32)
对应的Django模型会是:
usersFavorites = User.objects.all().values_list("name", "favorite__name")
这里没有使用你给出的字段的确切名称。要做到这一点,你只需要在模型中添加合适的 db_table
和 db_column
字段。
顺便说一下,这个特定的结构其实不是特别好。在我看来,User
和 Item
应该是多对多的关系:
class User(models.Model):
name = models.CharField(max_length=32)
favorites = models.ManyToManyField(Item, related_name="users")
class Item(models.Model):
name = models.CharField(max_length=32)
除非一个物品只能有一个用户,在这种情况下,ForeignKey
应该放在 Item
中。
然后你可以用双重循环来遍历:
for user in User.objects.all().select_related("favorites"):
for favorite in user.favorites:
# use `user` and `favorite` in some way.
由于调用了 select_related()
,所以在第一个 for
循环中查询就一次性完成了。
2
(或者任何ORM系统)?
真的吗?这是你在sqlalchemy中怎么做的。
假设你有合理的映射类(这里使用的是声明式方式):
Base = sqlalchemy.ext.declarative.declarative_base()
class Item(Base):
__tablename__ = 'items'
id = Column('item_id', Integer, primary_key=True)
name = Column('item_name', String)
class User(Base):
__tablename__ = 'users'
name = Column('user_name', String(50), primary_key=True)
favor_item_id = Column(Integer, ForeignKey(Item.id))
favor_item = relationship(Item, backref="favored_by")
这个查询非常简单。
>>> print sqlalchemy.orm.Query((User.name, Item.name)).join(Item.favored_by)
SELECT users.user_name AS users_user_name, items.item_name AS items_item_name
FROM items JOIN users ON items.item_id = users.favor_item_id