여러 외래 키와 Rails 연관
한 테이블에 두 개의 열을 사용하여 관계를 정의 할 수 있기를 원합니다. 따라서 작업 앱을 예로 사용합니다.
시도 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반환하는 사용자를 하고 Task.first.assignee반환하는 사용자이 있지만 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
두 개의 외래 키가 지원되지 않는 것처럼 보이므로 모두 실패합니다.
그래서 내가 원하는 것은 User.tasks사용자가 소유하고 할당 된 작업을 모두 말하고 얻을 수있는 것입니다.
기본적으로 어떻게 든 쿼리와 동일한 관계를 구축합니다. Task.where(owner_id || assignee_id == 1)
가능합니까?
최신 정보
나는 사용하지 않으려 고 finder_sql하지만이 문제의 받아 들일 수없는 대답은 내가 원하는 것에 가깝습니다 : Rails-Multiple Index Key Association
따라서이 방법은 다음과 같습니다.
시도 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 :tasks에서 제거하십시오 User.
table에 has_many :tasks이름이 지정된 열이 없기 때문에 사용 은 전혀 의미가 없습니다 .user_idtasks
내 경우 문제를 해결하기 위해 내가 한 일은 다음과 같습니다.
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한 번이 아닌 두 개의 쿼리가 실행되고 결과가 이 두 쿼리 중 또한 조인되어야합니다.
따라서 사용자에게 속한 작업을 가져 오기 위해 다음과 같은 방식으로 클래스에서 사용자 지정 tasks메서드를 정의합니다 User.
def tasks
Task.where("owner_id = ? OR assigneed_id = ?", self.id, self.id)
end
이렇게하면 단일 쿼리로 모든 결과를 가져 오므로 결과를 병합하거나 결합 할 필요가 없습니다.
위의 @ dre-hh의 답변을 확장하면 Rails 5에서 더 이상 예상대로 작동하지 않는 것으로 나타났습니다. Rails 5에는 이제의 효과에 대한 기본 where 절이 포함되어 있으며이 시나리오 WHERE tasks.user_id = ?에는 user_id열 이 없으므로 실패합니다 .
I've found it is still possible to get it working with a has_many association, you just need to unscope this additional where clause added by Rails.
class User < ApplicationRecord
has_many :tasks, ->(user) { unscope(:where).where("owner_id = :id OR assignee_id = :id", id: user.id) }
end
Rails 5:
you need to unscope the default where clause see @Dwight answer if you still want a has_many associaiton.
Though User.joins(:tasks) gives me
ArgumentError: The association scope 'tasks' is instance dependent (the scope block takes an argument). Preloading instance dependent scopes is not supported.
As it is no longer possible you can use @Arslan Ali solution as well.
Rails 4:
class User < ActiveRecord::Base
has_many :tasks, ->(user){ where("tasks.owner_id = :user_id OR tasks.assignee_id = :user_id", user_id: user.id) }
end
Update1: Regarding @JonathanSimmons comment
Having to pass the user object into the scope on the User model seems like a backwards approach
You don't have to pass the user model to this scope. The current user instance is passed automatically to this lambda. Call it like this:
user = User.find(9001)
user.tasks
Update2:
if possible could you expand this answer to explain what's happening? I'd like to understand it better so I can implement something similar. thanks
Calling has_many :tasks on ActiveRecord class will store a lambda function in some class variable and is just a fancy way to generate a tasks method on its object, which will call this lambda. The generated method would look similar to following pseudocode:
class User
def tasks
#define join query
query = self.class.joins('tasks ON ...')
#execute tasks_lambda on the query instance and pass self to the lambda
query.instance_exec(self, self.class.tasks_lambda)
end
end
I worked out a solution for this. I'm open to any pointers on how I can make this better.
class User < ActiveRecord::Base
def tasks
Task.by_person(self.id)
end
end
class Task < ActiveRecord::Base
scope :completed, -> { where(completed: true) }
belongs_to :owner, class_name: "User", foreign_key: "owner_id"
belongs_to :assignee, class_name: "User", foreign_key: "assignee_id"
def self.by_person(user_id)
where("owner_id = :person_id OR assignee_id = :person_id", person_id: user_id)
end
end
This basically overrides the has_many association but still returns the ActiveRecord::Relation object I was looking for.
So now I can do something like this:
User.first.tasks.completed and the result is all completed task owned or assigned to the first user.
My answer to Associations and (multiple) foreign keys in rails (3.2) : how to describe them in the model, and write up migrations is just for you!
As for your code,here are my modifications
class User < ActiveRecord::Base
has_many :tasks, ->(user) { unscope(where: :user_id).where("owner_id = ? OR assignee_id = ?", user.id, user.id) }, class_name: 'Task'
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
Warning: If you are using RailsAdmin and need to create new record or edit existing record,please don't do what I've suggested.Because this hack will cause problem when you do something like this:
current_user.tasks.build(params)
The reason is that rails will try to use current_user.id to fill task.user_id,only to find that there is nothing like user_id.
So,consider my hack method as an way outside the box,but don't do that.
참고URL : https://stackoverflow.com/questions/24642005/rails-association-with-multiple-foreign-keys
'Programing' 카테고리의 다른 글
| 대상 Google API와 대상 Android의 차이점 (0) | 2020.10.24 |
|---|---|
| HTTP API 속도 제한 HTTP 응답 헤더의 예 (0) | 2020.10.24 |
| Postgresql을 다시 시작하는 방법 (0) | 2020.10.24 |
| Linux : 주어진 폴더 및 콘텐츠에 대해 단일 해시를 계산합니까? (0) | 2020.10.24 |
| 암시 적 인터페이스와 명시 적 인터페이스 구현 (0) | 2020.10.24 |