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

set*_*rgo 27 activerecord ruby-on-rails ruby-on-rails-3

我不明白如何从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    |                |
+----------------+--------------+------+-----+---------+----------------+
Run Code Online (Sandbox Code Playgroud)

 

> 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    |                |
|      .                .          .      .       .             .       |
|      .                .          .      .       .             .       |
|      .                .          .      .       .             .       |
+----------------+--------------+------+-----+---------+----------------+
Run Code Online (Sandbox Code Playgroud)

在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          | ... |
+----+-----------+----------+---------------------+---------+---------------+-----+
Run Code Online (Sandbox Code Playgroud)

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

# in rails console
>> p = Profile.joins(:user).limit(1)
>> [#<Profile ...>]
>> p.first_name
>> NoMethodError: undefined method `first_name' for #<ActiveRecord::Relation:0x102b521d0> 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 #<ActiveRecord::Relation:0x102b521d0> from /Library/Ruby/Gems/1.8/gems/activerecord-3.0.1/lib/active_record/relation.rb:373:in method_missing' from (irb):9
Run Code Online (Sandbox Code Playgroud)

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

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

Lex*_*sey 18

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

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

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

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    |                |
+------------+--------------+------+-----+---------+----------------+
Run Code Online (Sandbox Code Playgroud)

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

@rights = Right.joins("LEFT OUTER JOIN tags ON rights.Tagid = tags.id")
Run Code Online (Sandbox Code Playgroud)

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

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

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

.select("rights.*,tags.TagName,tags.TagColor")
Run Code Online (Sandbox Code Playgroud)

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

<% @rights.each do |right| %>
  <tr ALIGN=Left <%=
  # color background if this is tagged
  " BGCOLOR=#{right.TagColor}" if right.TagColor
  %> > ...
<% end %>
Run Code Online (Sandbox Code Playgroud)

  • 请注意,在控制台中键入`@ rights`将不会显示额外的标记元素.但输入`@ rights.TagName`将显示值. (2认同)

kle*_*lew 11

我不认为您可以通过加入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")
Run Code Online (Sandbox Code Playgroud)

并逐行获得结果:

p.fetch_row
Run Code Online (Sandbox Code Playgroud)

它已经成为一个阵列.

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

p = Profile.joins(:user).limit(1)
p[0].first_name
Run Code Online (Sandbox Code Playgroud)

干了工作.

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

p = Profile.joins(:user).first
p.first_name
p.user
Run Code Online (Sandbox Code Playgroud)

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


小智 8

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

在这种情况下,您将键入:

User.select("*").joins(:profile)
Run Code Online (Sandbox Code Playgroud)

希望这对你有用。


Dav*_*ith 5

阅读这些技巧后,我通过阅读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)
Run Code Online (Sandbox Code Playgroud)

这是我的其他尝试。

#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 :)
Run Code Online (Sandbox Code Playgroud)