【问题标题】:Relation (x > 1)-to-many关系 (x > 1)-to-many
【发布时间】:2012-03-16 00:58:15
【问题描述】:

这可能是一个关于数据库的通用问题,而不是 django 问题,但我们走吧。

我经常有可以被视为 2 对多的关系。 例如,我的项目中有一个类 Match,这是两个团队之间的相遇。

起初,我使用的是多对多:

Match(Model):
  teams = ManyToManyField('Team', related_name='matches') #Always two teams

它最终对于匹配相关的页面非常低效。特别是在 django 1.3 中,因为对于 many_to_many 关系没有等价于 select_related。当你现在正好有两个元素时,不得不进行迭代有点痛苦。

然后我切换到这个模型:

Match(Model):
  teams = ManyToManyField('Team', related_name='matches') #Always two teams
  team1 = ForeignKey('Team')
  team2 = ForeignKey('Team')

当我想显示比赛相关页面时,我可以使用 select_related 并非常有效地显示两个球队。 当我在团队页面上时,我可以像以前一样按照“比赛”关系获取所有比赛。 但是我发现处理一个关系的 3 个字段是完全可怕的。

我做得对吗?你会推荐什么?

【问题讨论】:

  • 你能详细说明你的缓存组织吗?
  • 标题中的两个标签,都有不同的分隔符...... ARGH!
  • @jpic 我不明白

标签: django postgresql django-models django-queryset


【解决方案1】:

我在那里看到的解决方案很少。

1.

如果您不预计将来会在其他数量的球队之间进行比赛而不是两个,那么最好从您的第二个设计中删除一个 ManyToMany 字段:

Match(Model):
  team1 = ForeignKey('Team')
  team2 = ForeignKey('Team')

它将删除数据库中的冗余并使Match 修改更容易(您不必担心team 字段和相应的team1team2 字段之间的内聚)。这也使得无法与其他数量的对应团队创建匹配,而不是两个,这导致数据库一致性。

另一方面,您正在失去灵活性并且编写查询更加困难,因为要检查两个字段而不是一个。

2.

您可能想要使用缓存。事实上 - 你拥有的这两个字段仅此而已,但将其移至专用缓存数据库可以提高效率并将主数据库与缓存值分开,从而使数据库设计更加清晰。

3.

编写您自己的 ManyToMany 提取器。这不是很难做到,但相当丑陋。您必须选择您感兴趣的团队,然后以某种方式将它们附加到 Match 对象,例如:

matches # Match QuerySet
related_teams = Team.objects.filter(match__in=matches)
matches_map = {}
for t in related_teams:
    matches_map[t.match_id]= matches_map.get(t.match_id, []).append(t)

for m in matches:
    m.fetched_teams = matches_map[m.pk]

3.5.

新版本的 Django 将有一个 prefetch_related() 方法,用于处理多对多查询,但我想你没有那么多时间。

【讨论】:

  • 1.我们处理两对两的比赛(这在我们的情况下非常罕见)由两支球队组成的临时球队,所以一场比赛总是有两支球队。 “冗余”根本不是问题,值永远不会改变,因此在创建过程中确保团队、团队 1 和团队 2 的正确性并不难。
  • 2.我不明白,您正在处理缓存中的所有多对多关系?喜欢 redis 吗?这听起来像很多一致性问题。我很想看一个关于这个的教程。
  • 3.我已经为很多关系这样做了,我建议使用允许选择中间关系(即使它没有明确确定)+ select_related。但是当总是有 2 个关系时,我发现迭代非常难看。
  • 3.5 是的,我正在关注开发者博客,我在晚上做着 prefect_related 的梦想 ^^。我只是害怕在 prod 上推出测试版,但我迫不及待地想使用该功能。
  • 广告 2。我想了一会儿,似乎我在这里错了。想法是将整个相关对象存储在某种访问成本非常低的缓存中,例如 memcache 左右。当您需要相关对象时 - 您只需从缓存中获取它们。只需在对其 ManyToMany 字段进行任何修改期间清除对象缓存,就可以轻松处理一致性问题——它将在下一次调用时重建。但我不确定你会从中获得多少提升,以及是否值得尝试。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-09-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多