【问题标题】:Can I have a one way HABTM relationship?我可以建立一种单向的 HABTM 关系吗?
【发布时间】:2018-03-28 10:16:17
【问题描述】:

假设我有一个模型项目,它有一个 Foo 和多个 Bars。

Foo 和 Bar 可以作为搜索 Item 时的参数,因此可以像这样搜索 Item:

www.example.com/search?foo=foovalue&bar[]=barvalue1&bar[]=barvalue2

我需要生成一个能够保存这些搜索参数的Query 对象。我需要以下关系:

  • 查询需要访问一个 Foo 和多个 Bars。
  • 一个 Foo 可以被许多不同的查询访问。
  • 许多不同的查询可以访问一个栏。
  • Bar 和 Foo 都不需要了解 Query。

我目前的这种关系是这样建立的:

class Query < ActiveRecord::Base
  belongs_to :foo
  has_and_belongs_to_many :bars
  ...
end

Query 还有一个方法可以返回这样的哈希值:{ foo: 'foovalue', bars: [ 'barvalue1', 'barvalue2' },它让我可以轻松地将这些值传递给 url 帮助程序并生成搜索查询。

这一切都很好。

我的问题是这是否是建立这种关系的最佳方式。我还没有看到任何其他单向 HABTM 关系的例子,所以我想我可能在这里做错了什么。

这是可接受的 HABTM 用法吗?

【问题讨论】:

  • 了解您为什么要这样做会很有用:当您偏离常规路径时,可能有更合适的方法来解决问题。
  • 绝对 - 我想知道是否有更合适的方法。至于为什么——我需要建立一个与其他模型相关的“已保存的搜索查询”。您需要有关用例的更多具体信息吗?
  • 看起来不错。只要不使用模型中的关系,它们就是可选的。如果您永远不会使用“bar.items”或“foo.items”或“foo.bars”,那么您可以将所有关系留在 Item 上。当然。
  • “您需要更多关于用例的具体信息吗?”是的
  • 我已将答案更新为更具体。

标签: ruby-on-rails activerecord


【解决方案1】:

功能上是,但语义上不是。以“片面”的方式使用 HABTM 将完全实现您想要的。不幸的是,HABTM 这个名字确实暗示了一种并非总是如此的互惠关系。同样,belongs_to :foo 在这里也没有什么直观意义。

不要被 HABTM 和其他关联的语义所困扰,而只需考虑您的 ID 需要放在哪里,以便适当且高效地查询数据。请记住,效率方面的考虑首先应该考虑到您的生产力。

我将冒昧地创建一个比您的 foos 和 bar 更具体的示例...假设我们有一个引擎可以让我们查询特定池塘中是否存在某些鸭子,并且我们想要跟踪这些查询。

可能性

在查询记录中存储鸭子有三种选择:

  1. 加入表
  2. 鸭 ID 的本机数组
  3. 鸭 ID 的序列化数组

您自己已经回答了连接表用例,如果“[Duck] 和 [Pond] 都不需要知道任何关于 Query 的事情”是真的,那么使用单向关联应该不会给您带来任何问题。您需要做的就是创建一个ducks_queries 表,ActiveRecord 将提供其余的。如果您需要做任何花哨的事情,您甚至可以选择使用has_many :through 关系。

有时数组比使用连接表更方便。您可以将数据存储为serialized 整数数组并添加处理程序以访问类似于以下的数据:

class Query
 serialize :duck_ids
 def ducks
   transaction do 
     Duck.where(id: duck_ids)
   end
 end
end

如果您的数据库中有原生数组支持,您可以在数据库中执行相同的操作。类似。

借助 Postgres 的原生数组支持,您可以进行如下查询:

SELECT * FROM ducks WHERE id=ANY(
  (SELECT duck_ids FROM queries WHERE id=1 LIMIT 1)::int[]
)

你可以在SQL Fiddle上玩上面的例子

权衡

  1. 加入表:
    • 优点:约定优于配置;您可以开箱即用地获得所有 Rails 好东西(例如 query.barsquery.bars=query.bars.where()
    • 缺点:您增加了数据层的复杂性(即另一个表,更密集的查询);没有什么直观意义
  2. 本机数组:
    • 优点:语义上不错;您可以开箱即用地获得所有与数组相关的数据库;性能可能更高
    • 缺点:您必须使用自己的 Ruby/SQL 或使用 ActiveRecord 扩展,例如 postgres_ext;不是数据库不可知论者;再见 Rails 好东西
  3. 序列化数组:
    • 优点:语义上不错;与数据库无关
    • 缺点:您必须使用自己的 Ruby;您将失去直接通过数据库进行某些查询的能力;序列化很恶心;再见 Rails 好东西

归根结底,您的用例决定一切。除此之外,我想说你应该坚持你的“片面”HABTM 实现:否则你会失去很多 Rails 赋予的礼物。

【讨论】:

  • 感谢您的回答!我一直认为在数据库中存储序列化的 ID 数组是不好的做法。你认为为了语义而做值得吗?
  • 不。刚刚在上面添加了一个部分,列举了相关的权衡。
  • 很好的答案。谢谢!
猜你喜欢
  • 2015-01-14
  • 1970-01-01
  • 2014-03-12
  • 1970-01-01
  • 1970-01-01
  • 2010-11-07
  • 2018-11-24
  • 1970-01-01
  • 2021-01-29
相关资源
最近更新 更多