[ruby-on-rails] 이 ActiveRecord :: ReadOnlyRecord 오류의 원인은 무엇입니까?

다음은 이전 질문에 대한 답변입니다. 실제로 해당 쿼리에서 조인을 제거 할 수 있음을 발견 했으므로 이제 작동중인 쿼리는

start_cards = DeckCard.find :all, :joins => [:card], :conditions => ["deck_cards.deck_id = ? and cards.start_card = ?", @game.deck.id, true]  

이것은 작동하는 것 같습니다. 그러나 이러한 DeckCard를 다른 연결로 이동하려고하면 ActiveRecord :: ReadOnlyRecord 오류가 발생합니다.

코드는 다음과 같습니다

for player in @game.players 
  player.tableau = Tableau.new
  start_card = start_cards.pop 
  start_card.draw_pile = false
  player.tableau.deck_cards << start_card  # the error occurs on this line
end

관련 모델 (테이블은 테이블의 플레이어 카드입니다)

class Player < ActiveRecord::Base
  belongs_to :game
  belongs_to :user
  has_one :hand
  has_one :tableau
end

class Tableau < ActiveRecord::Base
  belongs_to :player
  has_many :deck_cards
end  

class DeckCard < ActiveRecord::Base
  belongs_to :card
  belongs_to :deck  
end

이 코드 직후에 비슷한 작업을 수행 DeckCards하여 플레이어 손에 추가 하면 해당 코드가 제대로 작동합니다. belongs_to :tableauDeckCard 모델에 필요한지 궁금 했지만 플레이어의 손에 추가하는 것이 좋습니다. DeckCard 테이블에 tableau_idhand_id열 이 있습니다 .

Rails API에서 ReadOnlyRecord를 찾았으며 설명을 넘어서는 말은 많지 않습니다.



답변

레일 2.3.3 이하

로부터 액티브 CHANGELOG(v1.12.0 년 10 월 16 일, 2005) :

읽기 전용 레코드를 소개하십시오. object.readonly를 호출하면! 그런 다음 객체를 읽기 전용으로 표시하고 object.save를 호출하면 ReadOnlyRecord를 발생시킵니다. object.readonly? 객체가 읽기 전용인지 여부를보고합니다. 파인더 메소드에 : readonly => true를 전달하면 리턴 된 레코드가 읽기 전용으로 표시됩니다. : joins 옵션은 이제 : readonly를 의미하므로이 옵션을 사용하면 동일한 레코드를 저장할 수 없습니다. find_by_sql을 사용하여 해결하십시오.

를 사용 find_by_sql하는 것은 실제로 원시 행 / 열 데이터를 반환하므로 대안이 아닙니다 ActiveRecords. 두 가지 옵션이 있습니다.

  1. @readonly레코드에서 인스턴스 변수 를 false로 설정 (해킹)
  2. :include => :card대신에 사용:join => :card

레일 2.3.4 이상

위의 대부분은 2012 년 9 월 10 일 이후 더 이상 적용되지 않습니다.

  • 사용하는 Record.find_by_sql 것입니다 가능한 옵션
  • :readonly => true자동으로 유추되는 경우에만 경우 :joins지정 하지 않고 명시 적으로 :select (상속 범위 파인더 나) 명시 적 :readonly옵션은 (의 구현 볼 수 set_readonly_option!있는 active_record/base.rb레일 2.3.4에 대한, 또는의 구현 to_a에서 active_record/relation.rb와의 custom_join_sql에서 active_record/relation/query_methods.rb레일 3.0.0를 들어)
  • 그러나 조인 테이블에 두 개 이상의 외래 키 열이 있고 명시 적으로 지정되지 않은 경우 (예 : 사용자 제공 값은 무시 됩니다 . 참조 :readonly => true) 항상 자동으로 추론됩니다 .has_and_belongs_to_many:joins:select:readonlyfinding_with_ambiguous_select?active_record/associations/has_and_belongs_to_many_association.rb
  • 결론적으로, 특별한 취급하지 않는 테이블에 가입하고 has_and_belongs_to_many다음 @aaronrustad의 대답은 레일 2.3.4과 3.0.0에서 잘 적용됩니다.
  • 를 달성하려는 경우 사용 하지 마십시오 (을 의미하며 이는 선택보다 덜 효율적 입니다.):includesINNER JOIN:includesLEFT OUTER JOININNER JOIN

답변

또는 Rails 3에서는 읽기 전용 방법을 사용할 수 있습니다 ( “…”을 조건으로 대체).

( Deck.joins(:card) & Card.where('...') ).readonly(false)


답변

최근 Rails 릴리스에서 변경되었을 수 있지만이 문제를 해결하는 적절한 방법은 : readonly => false 를 찾기 옵션 에 추가 하는 것입니다.


답변

select ( ‘*’)는 Rails 3.2에서 이것을 고치는 것 같습니다 :

> Contact.select('*').joins(:slugs).where('slugs.slug' => 'the-slug').first.readonly?
=> false

select ( ‘*’)를 생략하면 읽기 전용 레코드가 생성됩니다.

> Contact.joins(:slugs).where('slugs.slug' => 'the-slug').first.readonly?
=> true

이론적 근거를 이해한다고 말할 수는 없지만 적어도 빠르고 깨끗한 해결 방법입니다.


답변

find_by_sql 대신 파인더에서 : select를 지정하면 모든 것이 행복합니다 …

start_cards = DeckCard.find :all,
:select => 'deck_cards.*',
:joins => [:card],
:conditions => ["deck_cards.deck_id = ? and cards.start_card = ?", @game.deck.id, true]


답변

비활성화하려면 …

module DeactivateImplicitReadonly
  def custom_join_sql(*args)
    result = super
    @implicit_readonly = false
    result
  end
end
ActiveRecord::Relation.send :include, DeactivateImplicitReadonly


답변