2010-12-30 11 views
24

レールから必要なカラムを取得する方法がわかりません。私は2つのモデル - ユーザーとプロファイルを持っています。ユーザー:(ユーザーが自分のプロフィールの以前のバージョンに戻すことができるので)has_manyのプロフィール:Railsジョイントテーブルのジョインとインクルードテーブルを含む

> 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   | ... | 
+----+-----------+----------+---------------------+---------+---------------+-----+ 

> 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では、私は、クエリを実行することができます

BOTHテーブルのすべての列を一緒にJOINEDする方法を参照してください。私は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のに参加して、ユーザーとプロファイルを読み込むことができないと思います

+0

なぜユーザーモデルをプロファイルモデル内に入れたいのですか?あなたは明らかに2つの別々のモデル、プロファイルとユーザーを持っています。 includeを使用すると、両方のエンティティのデータを1つのクエリでフェッチしてから、単にprofile.userを実行することができます。 – aromero

+0

その1つのクエリではないために... includeは2つのクエリを生成します – sethvargo

答えて

10

...問題はProfileモデルがユーザーの属性を持っていないという事実とは何かを持っていると思います。私は、以前のバージョンのRails(< 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 

これはすでに配列にマッペットされます。あなたはAciveRecord::Relationオブジェクト上first_nameuserメソッドを呼び出しているので、

あなたのエラーがあると、それはProfileオブジェクトの配列ではなく、単一のオブジェクトを格納します。従って

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

shoud workです。 1つのレコードのみを取得する

良い方法は呼び出すことです:

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

をしかし、あなたはp.userを呼び出すときには、データベースを照会します。これを避けるにはincludeを使用できますが、1つのプロファイルオブジェクトだけを読み込んだ場合は役に立たなくなります。一度に多くのプロファイルをロードして、ユーザーテーブルをインライン化したい場合は、違いがあります。

17

select()を使用して、必要な列に名前を付けます。少なくともこれはRails 3.0.9で動作します。

背景:私のアプリケーションには、権限という名前のプライマリテーブルがあります。私はタグと色を与えられた:右のレコードに帰すことができたかったので、私はそれを簡単に索引リストから選ぶことができました。これは関連するレコードの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 |    | 
+------------+--------------+------+-----+---------+----------------+ 

私は、ビュー/権利/ index.html.erbでのTagNameとTagColorを使用するつもりですので、私は@rightsでそれらの列を含むように権利コントローラがそれをオブジェクトたいですビューに渡されます。必ずしもすべてがあるので:権利を有する:タグを、私は外を使いたいが参加:誰もが見つかったとして、

@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 %> 
+2

コンソールに '@ rights'余分なタグ要素。しかし、 '@ rights.TagName'と打つと値が表示されます。 – Doug

+0

私はそれがRails遅延ロードのためだと思います。特定の列の値が必要になるまで、データベースは実際には読み取られません。 –

2

私が参加しているデータベースでビューを作成することで、この問題はラウンドだし、それがコード内で通常のActiveRecordのテーブルであるかのようにそれを参照しています。これはデータベースからデータを取り出すためには問題ありませんが、更新が必要な場合は、 '実際の'テーブルを表す基本クラスに戻る必要があります。大きなテーブルを使用するレポートを作成するときにこの方法が便利であることがわかりました。データをすべて1回のヒットで得ることができます。私はこれがActiveRecordに組み込まれていないように見えることに驚いています。あなたのためにそう

:SQLのIN

RUBYモデルファイルで
CREATE VIEW User_Profiles 
AS 
SELECT P.*, U.first_name 
FROM Users U 
inner join Profiles P on U.id=P.user_id 

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

**ヒント...私は常にビューの所有者を設定することを忘れ(私はポストグルを使用する)、それは多くの呪いと自己犠牲を払ってすぐに爆破する。

3

この場合select("*").joins(:table)

を使用してみてください、次のように入力します。あなたのために働く

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

希望。

3

これらのヒントを読み終えたら、私はすべてのクエリを1つのクエリでロードするために、3 ways to do eager loading (preloading) in 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 :) 
関連する問題