has_many :through : 你如何访问连接表属性? [英] has_many :through : How do you access join table attributes?

查看:13
本文介绍了has_many :through : 你如何访问连接表属性?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在 Rails 4 中有以下模型,带有简单的 has_many :through 关联:

I have the following models in Rails 4 with a simple has_many :through association:

class Model < ActiveRecord::Base
  has_many :model_options
  has_many :options, through: :model_options
end

class Option < ActiveRecord::Base
  has_many :model_options
  has_many :models, through: :model_options
end

class ModelOption < ActiveRecord::Base
  belongs_to :model
  belongs_to :option
end

我希望能够迭代模型实例的选项:

I want to be able to iterate over a Model instance's Options:

  model = Model.find.first
  model.options.each {}

并访问连接表上的属性.

and access the attributes on the join table.

在 Rails 3 中,你可以这样做:

In Rails 3 you could do this:

class Model < ActiveRecord::Base
  has_many :model_options
  has_many :options, through: :model_options , select: 'options.*, model_options.*'
end

但是 select: 已被弃用,这会产生弃用警告.

But select: is deprecated and this produces a deprecation warning.

也就是说,生成的 SQL 包含链接表数据:

That said, the SQL generated contains the link table data:

SELECT options.*, model_options.* FROM "options"
INNER JOIN "model_options" ON "options"."id" = "model_options"."option_id"
WHERE "model_options"."model_id" = $1 ORDER BY "options".name ASC  [["model_id", 1]]

但是 AR 从 model.options 返回的集合删除了链接表数据.

But the collection returned by AR from model.options removes the link table data.

为了删除 Rails 4 中的弃用警告,并仍然生成相同的 SQL,我这样做了:

To remove the deprecations warning in Rails 4, and still produce the same SQL, I did this:

class Model < ActiveRecord::Base
  has_many :model_options
  has_many :options, -> { select('options.*, model_options.*') }, through: :model_options
end

所以,查询是正确的,但我正在努力寻找访问链接表数据的正确方法.

So, the query is correct, but I am struggling to find the correct way to access the link table data.

我尝试了各种方法:

 model options
 model.options.joins(:model_options)
 model.options.select('options.*, model_options.*')
 model.model_options.joins(:option)
 ...

不包括连接表数据.

谢谢.

推荐答案

关于您想要实现的目标,答案可能会有所不同.您是要检索这些属性还是将它们用于查询?

The answer may be different regarding what you want to achieve. Do you want to retrieve those attributes or to use them for querying ?

ActiveRecord 是关于将表行映射到对象,因此您不能将属性从一个对象转换为另一个对象.

ActiveRecord is about mapping table rows to objects, so you can't have attributes from one object into an other.

让我们举一个更具体的例子:有房子、人和狗.一个人属于房子.狗属于一个人.一个房子有很多人.一个房子有很多狗穿过人.

Let use a more concrete example : There are House, Person and Dog. A person belongs_to house. A dog belongs_to a person. A house has many people. A house has many dogs through people.

现在,如果你必须找回一只狗,你不希望它有人物属性.在狗属性中使用 car_id 属性是没有意义的.

Now, if you have to retrieve a dog, you don't expect to have person attributes in it. It wouldn't make sense to have a car_id attribute in dog attributes.

话虽如此,这不是问题:我认为您真正想要的是避免在此处进行大量数据库查询.Rails 支持您:

That being said, it's not a problem : what you really want, I think, is to avoid making a lot of db queries, here. Rails has your back on that :

# this will generate a sql query, retrieving options and model_options rows
options = model.options.includes( :model_options )

# no new sql query here, all data is already loaded
option = options.first

# still no new query, everything is already loaded by `#includes`
additional_data = option.model_options.first

它在控制台中的行为是这样的.在实际的应用程序代码中,sql 查询将在第二个命令上触发,因为第一个没有使用结果(sql 查询仅在我们需要它的结果时触发).但这并没有改变这里的任何内容:它只触发了一次.

Edit : It will behaves like this in console. In actually app code, the sql query will be fired on second command, because first one didn't use the result (the sql query is triggered only when we need its results). But this does not change anything here : it's only fired a single time.

#includes 就是这样做的:从结果集中的外部表加载所有属性.然后,一切都被映射为有意义的面向对象表示.

#includes does just that : loading all attributes from a foreign table in the result set. Then, everything is mapped to have a meaningful object oriented representation.

如果您想基于 Options 和 ModelOptions 进行查询,则必须使用 #references.假设您的 ModelOption 有一个 active 属性:

If you want to make query based on both Options and ModelOptions, you'll have to use #references. Let say your ModelOption has an active attribute :

# This will return all Option related to model 
# for which ModelOption is active
model.options.references( :model_options ).where( model_options: { active: true })

结论

#includes 将加载结果集中的所有外部行,以便您以后可以使用它们而无需进一步查询数据库.#references 还允许您在查询中使用该表.

Conclusion

#includes will load all foreign rows in result set so that you can use them later without further querying the database. #references will also allow you to use the table in queries.

在任何情况下,您的对象都不会包含来自其他模型的数据,但这是一件好事.

In no case will you have an object containing data from an other model, but that's a good thing.

这篇关于has_many :through : 你如何访问连接表属性?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆