Ruby-on-rails has_many :through : 你如何访问连接表属性?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18799934/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 22:42:56  来源:igfitidea点击:

has_many :through : How do you access join table attributes?

ruby-on-railsactiverecord

提问by auxbuss

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

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

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.

并访问连接表上的属性。

In Rails 3 you could do this:

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

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

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

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

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

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

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

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

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

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

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

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.

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

I have tried various ways:

我尝试了各种方法:

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

None include the join table data.

没有包括连接表数据。

Thanks.

谢谢。

回答by kik

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

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

Loading results

加载结果

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

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

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.

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

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.

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

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 :

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

# 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

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.

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

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

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

Using attributes in query

在查询中使用属性

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

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

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

Conclusion

结论

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

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

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

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

回答by Alfredo Cobo

Just like Olivier said you have to eager load the association.

就像奥利维尔说的,你必须急切地加载关联。

For some reason the association is not returning the single model_options data when you use includes.

出于某种原因,当您使用includes时,关联不会返回单个 model_options 数据。

It works for me when i force AR to do a single query with eager_load.

当我强制 AR 使用Eager_load执行单个查询时,它对我有用

options = model.options.eager_load( :model_options )

# then
data = options.first.model_options.first

回答by NinjaCode

your edit your model :

你编辑你的模型:

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

  def all_options 
        model_options + options
  end

end

Now can access all atribute from joining ("model_options") table and "options" table, like this:

现在可以从 join ("model_options") 表和 "options" 表访问所有属性,如下所示:

001 >>model = Model.first

001 >>model = Model.first

002 >>model.all_options

002 >>model.all_options

More: https://blog.codedge.io/rails-join-table-with-extra-attributes/

更多:https: //blog.codedge.io/rails-join-table-with-extra-attributes/