Django RelatedManager 的 .create() 用法?

Posted

技术标签:

【中文标题】Django RelatedManager 的 .create() 用法?【英文标题】:Django RelatedManager's .create() usage? 【发布时间】:2010-11-09 06:03:54 【问题描述】:

我有两个模型:PlayPlayParticipant,定义(部分)为:

class PlayParticipant(models.Model):
    player = models.ForeignKey('Player')
    play = models.ForeignKey('Play')
    note = models.CharField(max_length=100, blank=True)

我的一段代码有一个游戏p,它的 ID 为 8581,我想向其中添加参与者。我正在尝试使用 RelatedManager 的 .create() 来做到这一点,例如:

p.playparticipant_set.create(player_id=2383)

Django 从中构造:

INSERT INTO `api_playparticipant` (`player_id`, `play_id`, `note`) VALUES (2383, 2383, '')

这是 Django 中的错误,还是我误用了.create()

复制粘贴 shell 以进行完整性检查:

In [17]: p = Play.objects.get(id=8581)

In [18]: p.id
Out[18]: 8581L

In [19]: p.playparticipant_set.create(player_id=2383)
...
IntegrityError: (1452, 'Cannot add or update a child row: a foreign key constraint fails (`gc/api_playparticipant`, CONSTRAINT `play_id_refs_id_60804ffd462e0029` FOREIGN KEY (`play_id`) REFERENCES `api_play` (`id`))')

来自 query.log:

5572 Query       INSERT INTO `api_playparticipant` (`player_id`, `play_id`, `note`) VALUES (2383, 2383, '')

【问题讨论】:

【参考方案1】:

我不明白您的第一个示例应该是一个错误吗?这种行为是完全直观的。您的 p 是一个 ID 为 2383 的 play,您正在调用其相关集的 create 方法。您还指定了一个名为 player_id 的额外字段,并将其值设为 2383。两个播放 id 均为 2383 是合乎逻辑的(因为这是包含此相关集的播放的 id)并且该玩家 id 也将是 2383(因为您明确传递了该值)。

您的第二个示例似乎表明存在错误,但我无法重现它。这是我的模型:

class Player(models.Model):
    name = models.CharField(max_length=100)

class Play(models.Model):
    title = models.CharField(max_length=100)

class PlayParticipant(models.Model):
    player = models.ForeignKey('Player')
    play = models.ForeignKey('Play')
    note = models.CharField(max_length=100, blank=True)

这是外壳输出:

>>> player1 = Player.objects.create()
>>> player2 = Player.objects.create()
>>> player1.id
2
>>> player2.id
3
>>> play1 = Play.objects.create()
>>> play2 = Play.objects.create()
>>> play1.id
3
>>> play2.id
4
>>> pp1 = play1.playparticipant_set.create(player_id=2)
>>> pp1.play.id
3
>>> pp1.player.id
2

无论如何,您为什么要将此发布给 SO? Django 有一个 bugtracker、几个活跃的官方邮件列表和几个 IRC 频道。

【讨论】:

但他的帖子说:“我的一段代码有一个 id 为 2383 的 play p”。 @oggy:这是一个错字,已更正。抱歉 :-/ 你用的是什么版本的 Django? 在主干和 1.0.2 上测试,使用 sqlite3 后端。 我当时正在运行 Django 1.0.2 和 mysql 5.0.81...当我有机会时会自行重现,如果仍然可以重现,则提交错误报告。 感谢所有的帮助,奥吉 :)【参考方案2】:

您应该在 Play 模型中使用与零个或多个参与者相关的 ManyToManyField。在该代码中,您将只是在做

play.players.add(player)

【讨论】:

嗯,不能,因为关系中有一个额外的字段 - 一个注释(更新的问题包括它)。示例等同于:djangoproject.com/documentation/models/m2m_intermediary 另外,我想弄清楚错误 是什么 而不是解决它(我使用 PlayParticipant.objects.create(...)) 【参考方案3】:

这不是错误;检查documentation;特别是关于多对多关系的额外字段部分。它指定:

与普通的多对多字段不同,您不能使用addcreateassignment(即beatles.members = [...])来创建关系。

创建这种类型关系的唯一方法是创建中间模型的实例。

【讨论】:

这不是多对多的关系,尤其是。不是带有属性的多对多,而是简单的一对多;该文档说明了具有属性的多对多关系的中间模型;您引用的部分与问题完全无关。【参考方案4】:

我无法重现这种行为。使用类似的模型,我可以运行:

m = Manufacturer.objects.get(1)
p = m.product_set.create(name='23',category_id=1,price=23,delivery_cost=2)

我得到Product 的新实例p,其p.manufacturer 外键等于m

这是在带有 PostgreSQL 的 Django-1.0.2 上;您使用哪个版本的 Django,以及哪个 SQL 后端?

尝试在其中一个上使用两个带有 ForeignKey 字段的新最小模型,这应该可以;然后使这些模型逐渐接近表现出失败行为的模型。通过这种方式,您可以隔离导致其行为不端的单个更改;这种变化是原因的关键。

【讨论】:

以上是关于Django RelatedManager 的 .create() 用法?的主要内容,如果未能解决你的问题,请参考以下文章

Django RelatedManager 的 .create() 用法?

django ORM中的RelatedManager(关联管理器)

django2.0+反向查询抛异常处理

Django Models

TypeError:“RelatedManager”对象不可迭代

关联管理器(RelatedManager)