Ruby on rails Rails从联接表联接和包含列
我不知道如何从rails获取我想要的列。我有两个模型-一个用户和一个配置文件。用户:拥有多个配置文件(因为用户可以恢复到其配置文件的早期版本): 在SQL中,我可以运行以下查询:Ruby on rails Rails从联接表联接和包含列,ruby-on-rails,activerecord,ruby-on-rails-3,Ruby On Rails,Activerecord,Ruby On Rails 3,我不知道如何从rails获取我想要的列。我有两个模型-一个用户和一个配置文件。用户:拥有多个配置文件(因为用户可以恢复到其配置文件的早期版本): 在SQL中,我可以运行以下查询: > SELECT * FROM profiles JOIN users ON profiles.user_id = users.id LIMIT 1; +----+-----------+----------+---------------------+---------+---------------+---
> 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 | ... |
+----+-----------+----------+---------------------+---------+---------------+-----+
查看如何将两个表的所有列连接在一起?然而,当我在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
rails控制台中的#
>>p=Profile.joins(:user).limit(1)
>> [#]
>>p.名字
>>NoMethodError:from/Library/Ruby/Gems/1.8/Gems/activerecord-3.0.1/lib/active\u record/relation的未定义方法“first\u name”。rb:373:in'method\u missing'from(irb):8
#我不想这样做(即我不想使用“包含”)
>>p.user
>>NoMethodError:from/Library/Ruby/Gems/1.8/Gems/activerecord-3.0.1/lib/active\u record/relation的未定义方法“user”。rb:373:in-method\u-missing”from(irb):9
我希望(高效地)返回一个对象,该对象同时具有Profile和User的所有属性。我不想:包括用户,因为它没有意义。用户应该始终是最新配置文件的一部分,就像它们是配置文件模型中的字段一样。我如何做到这一点
我认为问题与配置文件模型没有用户属性这一事实有关…我不认为可以使用join-in Rails加载用户和配置文件。我认为在早期版本的Rails(<2.1)中,关联模型的加载是通过连接完成的,但效率不高。你有一些解释和其他材料的链接 因此,即使您明确表示希望加入它,Rails也不会将其映射到相关模型。所以,如果你说
Profile。不管这里有什么
,它都会被映射到Profile
对象
如果您仍然想执行您所说的,那么您可以自己调用自定义sql查询并处理结果:
p = ActiveRecord::Base.connection.execute("SELECT * FROM profiles JOIN users ON profiles.user_id = users.id LIMIT 1")
并使用以下命令逐行获取结果:
p.fetch_row
它将已经映射到一个数组
您的错误是因为您正在调用AciveRecord::Relation
对象上的first\u name
和user
方法,并且它存储了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图片不完全匹配;most:永远不会标记权限,并且标记完全是任意的(用户通过标记/编辑输入)
我可以尝试复制: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控制器在传递给视图的@rights对象中包含这些列。因为不是每个:right都有:标记,所以我想使用外部联接:
@rights = Right.joins("LEFT OUTER JOIN tags ON rights.Tagid = tags.id")
但是,正如每个人都发现的那样,单凭这一点是行不通的:对标记名的块引用会产生一个服务器错误。但是,如果在末尾添加一个select,则一切正常:
@rights = Right.joins("LEFT OUTER JOIN tags ON rights.Tagid = tags.id").select("rights.*,tags.TagName as TagName,tags.TagColor as TagColor")
注:2013年6月7日添加:select子句不需要别名-这也适用于:
.select("rights.*,tags.TagName,tags.TagColor")
现在我可以在视图中引用标记名和标记颜色:
<% @rights.each do |right| %>
<tr ALIGN=Left <%=
# color background if this is tagged
" BGCOLOR=#{right.TagColor}" if right.TagColor
%> > ...
<% end %>
...
我已经解决了这个问题,在数据库中创建了一个视图作为连接,然后在代码中引用它,就好像它是一个普通的ActiveRecord表一样。这对于从数据库中获取数据是很好的,但是如果需要更新它,则需要返回表示“真实”表的基类。我发现这种方法在编写使用大型表格的报告时非常方便——你可以一次就把数据全部拿出来。我很惊讶,这似乎并没有内置到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
在RUBY模型文件中:
class UserProfile < ActiveRecord::Base
self.primary_key = :id
#same dependencies as profiles
end
class-UserProfile
**提示。。。我总是忘记设置视图的所有者(我使用postgres),因此它会立刻爆发出许多咒骂和自责 尝试使用选择(“*”)。联接(:表)
在这种情况下,您可以键入:
User.select("*").joins(:profile)
希望这对你有用。在阅读了这些技巧之后,我通过阅读将所有连接加载到一个查询中 我正在使用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 :)
为什么要在概要文件模型中包含用户字段?很明显,您有两个独立的模型,profile和user。通过使用include,您可以在一个查询中获取两个实体的数据,然后只执行profile.users,因为它不是一个查询…:include生成两个查询请注意,在控制台中键入
@rights
将不会显示额外的标记元素。但是键入@rights.TagName
将显示这些值;在需要特定列值之前,不会实际读取数据库。您知道在这种情况下,是否有任何方法可以使用includes
而不是joins
?我想做同样的事情,但是用左
而不是内
连接,我不确定这是否完全回答了问题。只有左表的数据被打印到
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 :)