Что вызывает ошибку ActiveRecord:: ReadOnlyRecord?
Это следует за this вопросом, на который был дан ответ. Я действительно обнаружил, что могу удалить соединение из этого запроса, так что теперь рабочий запрос
start_cards = DeckCard.find :all, :joins => [:card], :conditions => ["deck_cards.deck_id = ? and cards.start_card = ?", @game.deck.id, true]
Это работает. Однако, когда я пытаюсь переместить эти DeckCards в другую ассоциацию, я получаю ошибку 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, и он не говорит много за описанием.
Ответы
Ответ 1
Из ActiveRecord CHANGELOG
(v1.12.0, 16 октября 2005 г.):
Введите записи только для чтения. Если вы вызываете object.readonly! то это будет отметьте объект как доступный только для чтения и поднимите ReadOnlyRecord, если вы позвоните object.save. object.readonly? отчеты является ли объект доступным только для чтения. Передача: readonly = > true для любого метод поиска будет отмечен как возвращенный записи как только для чтения. The: объединяет теперь подразумевает: readonly, так что если вы используете этот параметр, сохраняя тот же запись будет терпеть неудачу. Используйте find_by_sql для работы.
Использование find_by_sql
на самом деле не является альтернативой, поскольку оно возвращает необработанные данные строки/столбца, а не ActiveRecords
. У вас есть два варианта:
- Принудите переменную экземпляра
@readonly
false в записи (взломать)
- Используйте
:include => :card
вместо :join => :card
Sep 2010 UPDATE
Большая часть из вышеизложенного больше не выполняется. Таким образом, в Rails 2.3.4 и 3.0.0:
- с помощью
Record.find_by_sql
есть жизнеспособный вариант
-
:readonly => true
автоматически выводится только, если :joins
был указан без явным :select
и явным (или finder- scope-inherited) :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
.)
Ответ 2
Или в Rails 3 вы можете использовать метод readonly (замените "..." вашими условиями):
( Deck.joins(:card) & Card.where('...') ).readonly(false)
Ответ 3
Это может измениться в недавней версии Rails, но подходящий способ решить эту проблему - добавить : readonly = > false в параметры поиска.
Ответ 4
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
Вместо find_by_sql вы можете указать a: select на finder и все снова счастливое...
start_cards = DeckCard.find :all,
:select => 'deck_cards.*',
:joins => [:card],
:conditions => ["deck_cards.deck_id = ? and cards.start_card = ?", @game.deck.id, true]
Ответ 6
Чтобы отключить его...
module DeactivateImplicitReadonly
def custom_join_sql(*args)
result = super
@implicit_readonly = false
result
end
end
ActiveRecord::Relation.send :include, DeactivateImplicitReadonly