Что вызывает эту ошибку ActiveRecord :: ReadOnlyRecord? - PullRequest
201 голосов
/ 12 марта 2009

Это следует за этим предыдущим вопросом, на который был дан ответ. Я обнаружил, что могу удалить соединение из этого запроса, поэтому теперь рабочий запрос

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 :tableau в модели DeckCard, но она прекрасно работает для добавления в руку игрока. У меня есть столбцы tableau_id и hand_id в таблице DeckCard.

Я посмотрел ReadOnlyRecord в rails api, и это не говорит о многом за пределами описания.

Ответы [ 6 ]

281 голосов
/ 12 марта 2009

Рельсы 2.3.3 и ниже

Из ActiveRecord CHANGELOG (v1.12.0, 16 октября 2005 г.) :

Введение записей только для чтения. Если вы вызываете object.readonly! тогда это будет пометить объект только для чтения и поднять ReadOnlyRecord, если вы звоните object.save. object.readonly? отчеты является ли объект доступным только для чтения. Передача: readonly => true для любого метод поиска пометит возвращенный записи только для чтения. : присоединяется Опция теперь подразумевает: только чтение, так что если Вы используете эту опцию, сохраняя то же самое запись теперь не удастся. Используйте find_by_sql обойти.

Использование find_by_sql на самом деле не является альтернативой, поскольку оно возвращает необработанные данные строки / столбца, а не ActiveRecords. У вас есть два варианта:

  1. Принудительно установить для переменной экземпляра @readonly значение false в записи (взломать)
  2. Используйте :include => :card вместо :join => :card

Рельсы 2.3.4 и выше

Большинство вышеперечисленного больше не действует после 10 сентября 2012 года:

  • использование Record.find_by_sql является жизнеспособным вариантом
  • :readonly => true автоматически выводится только , если было указано :joins без явного :select или явного (или наследуемого областью видоискателя) ) :readonly (см. Реализацию set_readonly_option! в active_record/base.rb для Rails 2.3.4 или реализацию to_a в active_record/relation.rb и custom_join_sql в active_record/relation/query_methods.rb для Rails 3.0.0)
  • однако, :readonly => true всегда автоматически выводится в has_and_belongs_to_many, если в таблице объединения больше двух столбцов внешних ключей и :joins было указано без явного :select (т. Е. Предоставленные пользователем значения :readonly игнорируется - см. finding_with_ambiguous_select? в active_record/associations/has_and_belongs_to_many_association.rb.)
  • В заключение, если только вы не имеете дело со специальной таблицей соединений и has_and_belongs_to_many, тогда ответ @aaronrustad отлично подходит для Rails 2.3.4 и 3.0.0.
  • делать не использовать :includes, если вы хотите получить INNER JOIN (:includes подразумевает LEFT OUTER JOIN, что менее избирательно и менее эффективно, чем INNER JOIN.)
169 голосов
/ 10 августа 2010

Или в Rails 3 вы можете использовать метод readonly (замените «...» на ваши условия):

( Deck.joins(:card) & Card.where('...') ).readonly(false)
44 голосов
/ 07 июня 2009

Возможно, это изменилось в последнем выпуске Rails, но подходящий способ решения этой проблемы - добавить : readonly => false в параметры поиска.

15 голосов
/ 02 марта 2013

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

Не могу сказать, что понимаю обоснование, но, по крайней мере, это быстрый и чистый обходной путь.

5 голосов
/ 27 марта 2009

Вместо find_by_sql вы можете указать: выберите в поисковике, и все снова будет счастливым ...

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

3 голосов
/ 11 августа 2012

Чтобы отключить его ...

module DeactivateImplicitReadonly
  def custom_join_sql(*args)
    result = super
    @implicit_readonly = false
    result
  end
end
ActiveRecord::Relation.send :include, DeactivateImplicitReadonly
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...