在Django中进行数据库JOIN

0 投票
2 回答
2899 浏览
提问于 2025-04-16 20:46

我想做一个和这个 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_tabledb_column 字段。

顺便说一下,这个特定的结构其实不是特别好。在我看来,UserItem 应该是多对多的关系:

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

撰写回答