Rails连接并包含连接表中的列

我不明白如何从rails获取我想要的列。 我有两个模型 – 用户和个人资料。 用户:has_many配置文件(因为用户可以恢复到其配置文件的早期版本):

> DESCRIBE users; +----------------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +----------------+--------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | username | varchar(255) | NO | UNI | NULL | | | password | varchar(255) | NO | | NULL | | | last_login | datetime | YES | | NULL | | +----------------+--------------+------+-----+---------+----------------+ 

 > DESCRIBE profiles; +----------------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +----------------+--------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | user_id | int(11) | NO | MUL | NULL | | | first_name | varchar(255) | NO | | NULL | | | last_name | varchar(255) | NO | | NULL | | | . . . . . . | | . . . . . . | | . . . . . . | +----------------+--------------+------+-----+---------+----------------+ 

在SQL中,我可以运行查询:

 > SELECT * FROM profiles JOIN users ON profiles.user_id = users.id LIMIT 1; +----+-----------+----------+---------------------+---------+---------------+-----+ | id | username | password | last_login | user_id | first_name | ... | +----+-----------+----------+---------------------+---------+---------------+-----+ | 1 | john | ****** | 2010-12-30 18:04:28 | 1 | John | ... | +----+-----------+----------+---------------------+---------+---------------+-----+ 

看看我如何将BOTH表的所有列连接在一起? 但是,当我在Rails中运行相同的查询时,我没有得到我想要的所有列 – 我只从Profile中获取这些列:

 # in rails console >> p = Profile.joins(:user).limit(1) >> [#] >> p.first_name >> NoMethodError: undefined method `first_name' for # from /Library/Ruby/Gems/1.8/gems/activerecord-3.0.1/lib/active_record/relation.rb:373:in `method_missing' from (irb):8 # I do NOT want to do this (AKA I do NOT want to use "includes") >> p.user >> NoMethodError: undefined method `user' for # from /Library/Ruby/Gems/1.8/gems/activerecord-3.0.1/lib/active_record/relation.rb:373:in method_missing' from (irb):9 

我想(有效地)返回一个具有Profile和User的所有属性的对象。 我不想:包括用户,因为它没有意义。 用户应始终是最新配置文件的一部分,就好像它们是配置文件模型中的字段一样。 我该如何做到这一点?

我认为这个问题与Profile模型没有User的属性这一事实有关…

我不认为您可以通过加入Rails加载用户和配置文件。 我认为在早期版本的Rails(<2.1)中,关联模型的加载是通过连接完成的,但效率不高。 这里有一些解释和其他材料的链接。

因此,即使您明确表示要加入它,Rails也不会将其映射到关联模型。 因此,如果您说Profile.whatever_here ,它将始终映射到Profile对象。

如果你仍想做你所说的话,你可以自己调用自定义sql查询并处理结果:

 p = ActiveRecord::Base.connection.execute("SELECT * FROM profiles JOIN users ON profiles.user_id = users.id LIMIT 1") 

并逐行获得结果:

 p.fetch_row 

它已经是一个arrays。

您的错误是因为您在AciveRecord::Relation对象上调用first_nameuser方法,它存储的是Profile对象的数组,而不是单个对象。 所以

 p = Profile.joins(:user).limit(1) p[0].first_name 

干得好。

获取一条记录的更好方法是调用:

 p = Profile.joins(:user).first p.first_name p.user 

但是当你调用p.user它会查询数据库。 要避免它,您可以使用include ,但如果只加载一个配置文件对象,则它是无用的。 如果您一次加载多个配置文件并希望包含用户表,则会有所不同。

使用select()命名所需的列。 至少这在Rails 3.0.9中有效。

背景:我的应用程序有一个名为:rights的主表。 我希望能够将标签和颜色归因于给定的:正确的记录,以便我可以轻松地从索引列表中选择它。 这并不完全符合相关记录的Rails图片; 大多数:权限永远不会被标记,标签完全是任意的(用户通过标签/编辑输入)。

我可以尝试复制:right记录中的标记数据,但这违反了正常forms。 或者我可以尝试查询:每个标签:正确的记录,但这是一种非常低效的方法。 我希望能够加入这些表格。

MySQL控制台显示:

 mysql> describe rights; +------------+---------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------+---------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | ... | Tagid | int(11) | YES | | NULL | | +------------+---------------+------+-----+---------+----------------+ mysql> describe tags; +------------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------+--------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | TagName | varchar(255) | YES | | NULL | | | TagColor | varchar(255) | YES | | NULL | | | created_at | datetime | YES | | NULL | | | updated_at | datetime | YES | | NULL | | +------------+--------------+------+-----+---------+----------------+ 

我将在views / rights / index.html.erb中使用TagName和TagColor,因此我希望权限控制器将这些列包含在它传递给视图的@rights对象中。 由于不是每个:right都有:tag,我想使用外连接:

 @rights = Right.joins("LEFT OUTER JOIN tags ON rights.Tagid = tags.id") 

但是,正如每个人都发现的那样,仅此一点不起作用:对TagName的块引用会产生服务器错误。 但是,如果我在最后添加一个选择,一切都很好:

 @rights = Right.joins("LEFT OUTER JOIN tags ON rights.Tagid = tags.id").select("rights.*,tags.TagName as TagName,tags.TagColor as TagColor") 

注意添加6/7/13:select子句不需要别名 – 这也适用:

 .select("rights.*,tags.TagName,tags.TagColor") 

现在我可以在我的视图中引用TagName和TagColor:

 <% @rights.each do |right| %>  > ... <% end %> 

尝试使用select("*").joins(:table)

在这种情况下,您可以键入:

 User.select("*").joins(:profile) 

希望对你有用。

阅读完这些技巧后,我通过阅读3种方式在Rails 3和4中进行预先加载(预加载),将所有联接加载到一个查询中。

我正在使用Rails 4,这对我来说就像一个魅力:

 refs = Referral.joins(:job) .joins(:referee) .joins(:referrer) .where("jobs.poster_id= ?", user.contact_id) .order(created_at: :desc) .eager_load(:job, :referee, :referrer) 

这是我的其他尝试。

 #first attempt #refs = Referral.joins(:job) # .where("jobs.poster_id= ?", user.contact_id) # .select("referrals.*, jobs.*") # works, but each column needs to be explicitly referenced to be used later. # also there are conflicts for columns with the same name like id #second attempt #refs = ActiveRecord::Base.connection.exec_query("SELECT jobs.id AS job_id, jobs.*, referrals.id as referral_id, referrals.* FROM referrals INNER JOIN jobs ON job_id = referrals.job_id WHERE (jobs.poster_id=#{user.contact_id});") # this worked OK, but returned back a funky object, plus the column name # conflict from the previous method remains an issue. #third attempt using a view + rails_db_views #refs = JobReferral.where(:poster_id => user.contact_id) # this worked well. Unfortunately I couldn't use the SQL statement from above # instead of jobs.* I had to explicitly alias and name each column. # Additionally it brought back a ton of duplicate data that I was putting # into an array when really it is nice to work with ActiveRecord objects. #eager_load #refs = Referral.joins(:job) # .where("jobs.poster_id= ?", user.contact_id) # .eager_load(:job) # this was my base attempt that worked before I added in two more joins :) 

我通过在数据库中创建一个VIEW来解决这个问题,该数据库是连接,然后引用它就好像它是代码中的普通ActiveRecord表一样。 这对于从数据库中获取数据很好,但如果需要更新它,那么您将需要返回代表“真实”表的基类。 我发现这种方法在使用biggish表的报表时很方便 – 你可以在一次点击中获取数据。 我很惊讶这似乎没有内置在ActiveRecord中,对我来说似乎是显而易见的事情!

所以对你来说:

在SQL中:

 CREATE VIEW User_Profiles AS SELECT P.*, U.first_name FROM Users U inner join Profiles P on U.id=P.user_id 

IN RUBY模型文件:

 class UserProfile < ActiveRecord::Base self.primary_key = :id #same dependencies as profiles end 

**提示......我总是忘记设置视图的所有者(我使用postgres),所以它会随着诅咒和自我指责直接爆发。