1960


ActiveRecordで複数の関連を持つcollectionに、新たなレコードを追加させたい場合の実装について悩んでいます。

まず、テーブル構成を示します。

* * *

## テーブル

  * users … ユーザの情報を保持するテーブル
  * entries … 記事の情報を保持するテーブル
  * laters … "あとで読む"に設定した記事とユーザを紐付ける中間テーブル
  * checks … "既読"に設定した記事とユーザを紐付ける中間テーブル

## アソシエーション

  * user → laters → entrys  
※ userはlatersを中間テーブルとして`has_many: entries`な関連を持つ

  * user → checks → entrys  
※ userはchecksを中間テーブルとして`has_many: entries`な関連を持つ

* * *

## 質問

下記のように既読記事の追加（user-check-entryの紐付けの追加）の実装を行ったのですが意図どおりに動きません。

[code]

    entry = Entry.new
      ：
    entry.save
    User.find(1).entries<<entry
    
[/code]

意図するところは、checksテーブルに紐付けのレコードがinsertされることですが、実際にはlatersテーブルにinsertされてしまいます。

複数の関連を持つcollectionに、明示的に「こっちの紐付けを追加」というような操作はできますでしょうか。

* * *

## ソースコード

Github  
<https://github.com/hogesuke/tech-book/tree/master/model>


CC BY-SA 3.0
2014-12-18T01:27:54.957
1955
2014-12-18T02:38:23.130
2014-12-18T01:46:50.330
3968
3968
question
ruby ruby-on-rails
ActiveRecordで複数の関連を持つcollectionに対する追加の方法


`laters` に入ってしまうのは <https://github.com/hogesuke/tech-
book/blob/master/model/user.rb#L4> で

[code]

    has_many :entries, :through => :laters
    
[/code]

しているからでしょう。

対応方法は少なくとも以下の2つが考えられそうです。

  1. `User` に `Checks` 用 の `has_many' を追加定義する
  2. 直接 `Checks` に追加する

前者のモデルへの追加はこんな感じです。

[code]

    has_many :checked_entries, :through => :later
    
[/code]

で実際に追加するコードは以下のとおりです。

[code]

    entry = Entry.new
    ：
    entry.save
    User.find(1).checked_entries << entry
    
[/code]

それに対して後者のコードはこんな感じになるでしょうか。

[code]

    entry = Entry.new
    ：
    entry.save
    check = Check.new({user: User.find(1), entry: entry})
    check.save
    
[/code]

例示したコードはざっくり書いただけで、細かいチェックはしてないので適宜修正していただければ。

* * *

### 追記

上記は、いまのDBスキーマを変えずにやるなら、という前提です。

> 複数の関連を持つcollectionに、明示的に「こっちの紐付けを追加」というような操作はできますでしょうか。

これにこたえるにはDBスキーマを変える必要があります。`Checks` と `Laters` に代えて、`ChecksOrLaters`
みたいな中間テーブルを追加し、どちらの関連性であるかを `relationType` といったフィールドで記録するという方法は考えられます。


CC BY-SA 3.0
2014-12-18T01:58:27.830
1959
2014-12-18T02:15:46.877
2014-12-18T02:15:46.877
208
208
1955
answer


質問のアソシエーションの記述から、User モデルは以下の様な記述を試されたと仮定して話を進めます。 （前提が違っていたら申し訳ありません。）

[code]

    class User < ActiveRecord::Base
      has_many :entries, :through => :laters
      has_many :entries, :through => :checks
    end
    
[/code]

上記のような場合、entries
に、（違うテーブルを介して）2つのリレーションを貼ろうとしていますが、名前が同じになるため、衝突しているのだと思います。（なので、質問のようにlatersしか更新されないし、どちらかを選んで更新をかけることが出来ません）

このような場合、アクセスするための名前を分けることで、問題が解決するかと思います。 具体的には以下の様な感じです。

[code]

    class User < ActiveRecord::Base
      has_many :later_entries, :through => :laters, :source => 'entry'
      has_many :check_entries, :through => :checks, :source => 'entry'
    end
    
[/code]

その後、

[code]

    entry = Entry.new
      # ：
    entry.save
    User.find(1).check_entries << entry
    
[/code]

などとする事で、checksテーブルに紐付けのレコードがinsertされるかと思います。

## 参考URL

  * [Rails マニュアル: has_many](http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_many "ActiveRecord::Associations::ClassMethods")
  * [Coma's Tech Blog: has_many :through の関連に同一モデルを含む場合](http://www.coma-tech.com/archives/223/ "has_many :through の関連に同一モデルを含む場合")


CC BY-SA 3.0
2014-12-18T02:03:40.550
1960
2014-12-18T02:38:23.130
2014-12-18T02:38:23.130
2944
2944
1955
answer