【问题标题】:How do you join two tables on a foreign key field using django ORM?如何使用 django ORM 在外键字段上连接两个表?
【发布时间】:2012-10-17 00:49:35
【问题描述】:

假设我有以下模型:

class Position(models.Model):
    name = models.CharField()

class PositionStats(models.Model):
    position = models.ForeignKey(Position)
    averageYards = models.CharField()
    averageCatches = models.CharField()

class PlayerStats(models.Model):
    player = models.ForeignKey(Player)
    averageYards = models.CharField()
    averageCatches = models.CharField()

class Player(models.Model):
    name = models.CharField()
    position = models.ForeignKey(Position)

我想使用 django 的 ORM 执行等效的 SQL 查询:

SELECT *

FROM PlayerStats

JOIN Player ON player

JOIN PositionStats ON PositionStats.position = Player.position

我将如何使用 django 的 ORM 来做到这一点?该查询并不完全正确,但我的想法是我想要一个使用 django 的 ORM 的单一查询,它根据玩家的位置给我 PlayerStatsPositionStats 连接。

【问题讨论】:

  • 所有答案都是针对 2 个表连接,问题是仅使用 Django ORM 连接 3 个表。

标签: python database django postgresql orm


【解决方案1】:

这不是一个查询,但它非常有效。这对涉及的每个表执行一个查询,并在 Python 中将它们连接起来。更多关于prefetch_related的信息在这里:https://docs.djangoproject.com/en/dev/ref/models/querysets/#prefetch-related

Player.objects.filter(name="Bob").prefetch_related(
        'position__positionstats_set', 'playerstats_set')

【讨论】:

    【解决方案2】:

    From django.db import connection 在你看来包括以下语句:

    cursor = connection.cursor()
    cursor.execute("select * From Postion ON Position.name = Player.position JOIN
    PlayerStats ON Player.name = 
    PlayerStats.player JOIN PositionStats ON Position.name = PositionStats.player")
    solution = cursor.fetchall()
    

    【讨论】:

    • OP 特别提到“我想使用 django 的 ORM 执行等效的 SQL 查询”。这个答案给出了原始的 SQL 方法,而不使用 ORM。
    • 是的,请阅读实际问题。更正@NishantGeorgeAgrwal 所说的内容。
    【解决方案3】:

    我已经使用 django 有一段时间了,我在弄清楚表连接方面经历了一段艰难的时期,但我想我终于明白了,我想把它传递给其他人,这样他们就可以避免沮丧我有它。

    考虑以下model.py:

    class EventsMeetinglocation(models.Model):
        id = models.IntegerField(primary_key=True)
        name = models.CharField(max_length=100)
        address = models.CharField(max_length=200)
    
        class Meta:
            managed = True
            db_table = 'events_meetinglocation'
    
    class EventsBoardmeeting(models.Model):
        id = models.IntegerField(primary_key=True)
        date = models.DateTimeField()
        agenda_id = models.IntegerField(blank=True, null=True)
        location_id = models.ForeignKey(EventsMeetinglocation)
        minutes_id = models.IntegerField(blank=True, null=True)
    
        class Meta:
           managed = True
           db_table = 'events_boardmeeting'
    

    这里我们可以看到EventsBoardmeeting中的location_idEventsMeetinglocation中id的外键。这意味着我们应该可以通过EventsBoardmeeting查询EventsMeetinglocation中的信息。

    现在考虑以下views.py:

    def meetings(request):
        meetingData = EventsBoardmeeting.objects.all()
        return render(request, 'board/meetings.html', {'data': meetingData })
    

    正如之前在其他帖子中多次提到的,django 会自动处理连接。当我们查询EventsBoardmeeting 中的所有内容时,我们也可以通过外键获取任何相关信息,但是我们在 html 中访问它的方式有点不同。我们必须通过用作外键的变量来访问与该连接相关的信息。例如:

    {% for x in data %}
       {{ x.location_id.name }}
    {% endfor %}
    

    以上引用了表中作为外键连接结果的所有名称。 x 本质上是EventsBoardmeeting 表,所以当我们访问x.location_id 时,我们访问的是外键,它使我们能够访问EventsMeetinglocation 中的信息。

    【讨论】:

    • 我无法向您描述这篇文章对您的帮助有多大。我已经在互联网上搜索了将近一周,试图弄清楚如何从 html 中的连接数据集中调用变量。如果我打印它,我可以在我的查询集中看到我想要的数据,但我一生都无法弄清楚如何让它在页面中显示。谢谢!
    • 你的答案@connastone class Meta: managed = True db_table = 'events_meetinglocation' 和 class Meta: managed = True db_table = 'events_boardmeeting' 中以下代码的作用是什么
    • 在 SQL DB 中,当您执行连接(如 OP 所述)时,您会获得(或可以获得)1 个大表,其列是连接中两个表的字段,而在我的古代perl 经验,你可以只做 1 个循环并渲染 1 个大表。在这里,如果我想要模板中的 1 个大表,我必须执行一系列嵌套循环来重现这样一个非规范化表(其中任一表的记录可能会根据关系重复)。有人在 django 中告诉我你不必做嵌套循环,但我不知道怎么做。我错过了什么?
    • 如果涉及 3 个具有多对多关系的表,您可以扩展它吗?
    【解决方案4】:

    select_related()prefetch_related() 是您的解决方案。它们的工作方式几乎相同,但有一些区别。

    select_related() 通过创建 SQL 连接并在 SELECT 语句中包含相关对象的字段来工作。为此,select_related 在同一个数据库查询中获取相关对象。但它只适用于一对一或一对多的关系。示例如下-

    entry = Entry.objects.select_related('blog').get(id=5)
    or
    entries = Entry.objects.filter(foo='bar').select_related('blog')
    

    另一方面,prefetch_related() 对每个关系进行单独的查找,并在 Python 中进行“连接”。这允许它预取多对多和多对一对象,这是使用select_related 无法完成的。所以prefetch_related 将只为每个关系执行一个查询。示例如下-

    Pizza.objects.all().prefetch_related('toppings')
    

    【讨论】:

    【解决方案5】:

    在 Django 3.2 中,框架在使用方法 QuerySet.filter() 时自动遵循关系

    # The API automatically follows relationships as far as you need.
    # Use double underscores to separate relationships.
    # This works as many levels deep as you want; there's no limit.
    # Find all Choices for any question whose pub_date is in this year
    # (reusing the 'current_year' variable we created above).
    >>> Choice.objects.filter(question__pub_date__year=current_year)
    

    这将编译为以下 SQL 查询:

    SELECT
        "polls_choice"."id",
        "polls_choice"."question_id",
        "polls_choice"."choice_text",
        "polls_choice"."votes"
    FROM
        "polls_choice"
    INNER JOIN "polls_question" ON
        ("polls_choice"."question_id" = "polls_question"."id")
    WHERE
        "polls_question"."pub_date" BETWEEN 2020-12-31 23:00:00 AND 2021-12-31 22:59:59.999999
    

    在此处查看教程:https://docs.djangoproject.com/en/3.2/intro/tutorial02/

    【讨论】:

      猜你喜欢
      • 2018-06-24
      • 2020-07-15
      • 2020-12-27
      • 1970-01-01
      • 2022-08-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多