1. ホーム
  2. ruby-on-rails

[解決済み] 複数の外部キーを持つRailsのアソシエーション

2023-04-30 04:06:21

質問

1つのテーブルで2つのカラムを使用して関係を定義できるようにしたいです。例としてタスクアプリを使用します。

試み1。

class User < ActiveRecord::Base
  has_many :tasks
end

class Task < ActiveRecord::Base
  belongs_to :owner, class_name: "User", foreign_key: "owner_id"
  belongs_to :assignee, class_name: "User", foreign_key: "assignee_id"
end

では Task.create(owner_id:1, assignee_id: 2)

これによって Task.first.owner を返します。 ユーザー1 そして Task.first.assignee を返すと ユーザ2 しかし User.first.task は何も返しません。これはタスクがユーザーに属していないためです。 オーナー に属しており 譲受人 . だから

試み2。

class User < ActiveRecord::Base
  has_many :tasks, foreign_key: [:owner_id, :assignee_id]
end

class Task < ActiveRecord::Base
  belongs_to :user
end

2つの外部キーがサポートされていないようなので、これは完全に失敗しています。

ですから、私が欲しいのは、次のように言えるようにすることです。 User.tasks と入力して、所有するユーザーと割り当てられたタスクの両方を取得することです。

基本的にどうにかして、以下のクエリに等しいリレーションを構築します。 Task.where(owner_id || assignee_id == 1)

それは可能ですか?

更新情報

を使用することは考えていません。 finder_sql を使いたいわけではないのですが、この問題の未受理回答は私が欲しいものに近そうです。 Rails - 複数のインデックスキーの関連付け

ということで、このメソッドは以下のような感じになります。

試み3。

class Task < ActiveRecord::Base
  def self.by_person(person)
    where("assignee_id => :person_id OR owner_id => :person_id", :person_id => person.id
  end 
end

class Person < ActiveRecord::Base

  def tasks
    Task.by_person(self)
  end 
end

で動作させることはできるのですが Rails 4 では動作するのですが、以下のようなエラーが発生します。

ActiveRecord::PreparedStatementInvalid: missing value for :owner_id in :donor_id => :person_id OR assignee_id => :person_id

どのように解決するのですか?

TL;DR

class User < ActiveRecord::Base
  def tasks
    Task.where("owner_id = ? OR assigneed_id = ?", self.id, self.id)
  end
end

削除 has_many :tasksUser クラスで使用されます。


使用方法 has_many :tasks という名前のカラムがないため、全く意味をなしません。 user_id という名前のカラムがないからです。 tasks .

私の場合、問題を解決するために行ったことは

class User < ActiveRecord::Base
  has_many :owned_tasks,    class_name: "Task", foreign_key: "owner_id"
  has_many :assigned_tasks, class_name: "Task", foreign_key: "assignee_id"
end

class Task < ActiveRecord::Base
  belongs_to :owner,    class_name: "User", foreign_key: "owner_id"
  belongs_to :assignee, class_name: "User", foreign_key: "assignee_id"
  # Mentioning `foreign_keys` is not necessary in this class, since
  # we've already mentioned `belongs_to :owner`, and Rails will anticipate
  # foreign_keys automatically. Thanks to @jeffdill2 for mentioning this thing 
  # in the comment.
end

この方法では User.first.assigned_tasks と同様に User.first.owned_tasks .

というメソッドを定義することができます。 tasks の組み合わせを返す assigned_tasksowned_tasks .

読みやすさに関しては良い解決策かもしれませんが、パフォーマンスの観点からは、今までのように tasks を取得するために、1回ではなく2回のクエリを発行し、その2つのクエリの結果も結合する必要があるからです。

したがって、あるユーザーに属するタスクを取得するために、カスタムな tasks メソッドを User クラスを以下のように変更します。

def tasks
  Task.where("owner_id = ? OR assigneed_id = ?", self.id, self.id)
end

この方法では、1つのクエリですべての結果を取得するため、結果をマージしたり結合したりする必要はありません。