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

时间:2010-12-30 21:47:27

标签: ruby-on-rails activerecord 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    |                |
+----------------+--------------+------+-----+---------+----------------+

> 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中运行相同的查询时,我没有得到我想要的所有列 - 我只从配置文件中获取这些:

# 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

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

我认为这个问题与Profile模型没有User ...

的属性这一事实有关

5 个答案:

答案 0 :(得分:17)

使用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    |                |
+------------+--------------+------+-----+---------+----------------+

我将在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| %>
  <tr ALIGN=Left <%=
  # color background if this is tagged
  " BGCOLOR=#{right.TagColor}" if right.TagColor
  %> > ...
<% end %>

答案 1 :(得分:10)

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

因此,即使您明确表示要加入它,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

它已经成为一个数组。

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

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

工作。

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

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

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

答案 2 :(得分:4)

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

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

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

希望对你有用。

答案 3 :(得分:3)

阅读完这些提示后,通过阅读3 ways to do eager loading (preloading) in Rails 3 & 4,我将所有联接加载到一个查询中。

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

kCLLocationAccuracyBest

这是我的其他尝试。

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

答案 4 :(得分:2)

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

所以对你:

IN 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),所以它会随着诅咒和自我反复而直接爆发。