我将如何将这些多个连接作为 Django 查询集进行?

Posted

技术标签:

【中文标题】我将如何将这些多个连接作为 Django 查询集进行?【英文标题】:How would I do these multiple joins as a Django queryset? 【发布时间】:2018-10-07 10:31:04 【问题描述】:

我有一个将多个表连接在一起的查询:

select 
    p.player_id
    , d.player_data_1
    , l.year
    , l.league
    , s.stat_1
    , l.stat_1_league_average
from 
    stats s
inner join players p on p.player_id = s.player_id
left join player_data d on d.other_player_id = p.other_player_id
left join league_averages as l on l.year = s.year and l.league = s.year
where 
    p.player_id = 123

我的模型如下所示:

class Stats(models.Model):
    player_id = models.ForeignKey(Player)
    stat_1 = models.IntegerField()
    year = models.IntegerField()
    league = models.IntegerField()


class Player(models.Model):
    player_id = models.IntegerField(primary_key=True)
    other_player_id = models.ForeignKey(PlayerData)

class PlayerData(models.Model):
    other_player_id = models.IntegerField(primary_key=True)
    player_data_1 = models.TextField()

class LeagueAverages(models.Model):
    year = models.IntegerField()
    league = models.IntegerField()
    stat_1_league_average = models.DecimalField()

我可以这样做:

Stats.objects.filter(player_id=123).select_related('player')

进行第一次连接。对于第二次加入,我尝试了:

Stats.objects.filter(player_id=123).select_related('player').select_related('player_data')

但我收到了这个错误:

django.core.exceptions.FieldError:select_related 中给出的字段名称无效:'player_data'。选择是:播放器

考虑到yearleague 不是任何表中的外键,我将如何进行第三次连接?谢谢!

【问题讨论】:

【参考方案1】:

select_related(*fields) 返回一个将“遵循”外键关系的查询集,[...]

根据 django 文档select_related 遵循外键关系。 player_data 更接近外键,甚至不是Stats 的字段。如果你想INNER join PlayerDataPlayer 你可以按照它的外键。在你的情况下使用 double-underscore 转至PlayerData

Stats.objects.all()
    .select_related('player_id')
    .select_related('player_id__other_player_id')

关于加入LeagueAverages:没有合适的外键没有办法加入模型,只能使用原始sql。查看相关问题:Django JOIN query without foreign key。通过使用.raw(),您的LEFT join(顺便说一句,如果不使用raw 也不是那么容易:Django Custom Left Outer Join)也可以得到照顾。

关于您的模型的快速说明:

    默认情况下,每个模型都有一个自动递增的主键,可以通过.id.pk 访问。所以不需要添加例如player_id models.ForeignKey 字段引用了一个对象,而不是它的 id。因此,将player_id 重命名为player 更直观。如果您将字段命名为 player,django 允许您通过 player_id 自动访问它的 id

【讨论】:

太棒了,谢谢! PlayerData 的双下划线有效。我的第三次加入太糟糕了。至于自动递增的主键,我没有使用它,因为这些表中的数据来自第三方。 很高兴我能帮上一点忙。您仍然可以在创建时手动设置 id,即使它是自动递增的。但是,如果您选择故意重命名它,我认为这没有问题。我只是认为在这种特殊情况下会更加一致。

以上是关于我将如何将这些多个连接作为 Django 查询集进行?的主要内容,如果未能解决你的问题,请参考以下文章

Django 查询 - 将用户类与 Telegram Bot 的 OnetoOnefield 类连接起来

如何在 django 中将多个图像一起发布?

如何在不破坏SQL逻辑的情况下将JOINS转换为子查询

如何创建作为多个查询联合的 Django 模型字段,以实现覆盖字段?

如何在单个 JDBC 连接中执行多个 JPA 查询

如何使用 StringAgg 或 ArrayAgg 连接多个子行中的一列来注释 django 查询集?