rails has_one,has_many에서 dynamic하게 class_name을 지정하는 방법

Posted by negabaro kim on Saturday, December 24, 2022 Tags: rails/active_record/association   2 minute read

What I want to do

노래라는 모델이 있고 이 노래모델을 수정하는 사람이 작곡가, 가수, 작사가가 있다고 해보자

이 노래 모델을 마지막에 수정한 사람이 누구인지 체크하기 위해

latest_modified_by라는 테이블이 있고

modified_by_user_type
modified_by_user_id

라는 정보가 있다고 해보자

modified_by모델에 has_one :modified_by_user를 지정하고 수정한 사람이 누구냐에 따라(modified_by_user_type) 다른 모델을 참고하게 할 수 있는 방법이 없을까?

선결론

has_one에 지정하는 class_name을 조건에 따라 동적으로 바꿔주면 가능하다고 생각했다.

proc등을 선언하면 왠지 될법 싶었는데 의외로 class_name에는 proc사용이 안됨

결론 class_name을 동적으로 바꾸는건 불가능

대안으로 아래와 같이 3개의 has_one을 선언하고 modified_by_user메소드를 선언해서 type에 따라 어느 has_one을 선택할지 바꿔주도록 했다.

has_one :가수, primary_key: :modified_by_user_id, foreign_key: :id
has_one :작사가, primary_key: :modified_by_user_id, foreign_key: :id
has_one :작곡가, primary_key: :modified_by_user_id, foreign_key: :id
private :가수, :작사가, :작곡가  # 직접 참조 못하게끔 강제하기 위해

def modified_by_user
  return nil if modified_by_user_type.nil?
  return 가수 if modified_by_user_type == '가수'
  return 작사가 if modified_by_user_type == '작사가' 
  return 작곡가 if modified_by_user_type == '작곡가'
  nil
end

위와같이 선언후

노래.first.latest_modified_by.modified_by_user방법으로 다이나믹하게 수정한 유저의 모델을 가져올 수 있었다.(가수 or 작사가 or 작곡가)

여러가지 삽질

삼항연산자는 사용가능

  has_one :test5, primary_key: :modified_by_user_id, foreign_key: :id, class_name: false  ? '가수' : '작곡가'

삼항연산자 자체는 사용이 가능한데 false부분에 self지정이 불가능..(의미가 없잖아)

선언한 메소드를 참조 못함

has_one :test3, primary_key: :modified_by_user_id, foreign_key: :id, class_name: xx
def xx
end

와 같은 방법으로는 xx를 찾을 수 없다고 나옴. class_name은 모델만 참조하기 때문

lambda

has_one :test7, primary_key: :modified_by_user_id, foreign_key: :id, :class_name -> klass { klass.is_a?(User) ? :phone_number : :mobile_number

x같은 문법이라고 혼남

class_name은 안돼지만..

has_one :yy
def yy
end

참고

이런 방법은 선언이가능