¿Qué está causando este error ActiveRecord :: ReadOnlyRecord?
-
11-07-2019 - |
Pregunta
Esto sigue esta pregunta anterior, que fue respondida. De hecho, descubrí que podía eliminar una combinación de esa consulta, por lo que ahora la consulta de trabajo es
start_cards = DeckCard.find :all, :joins => [:card], :conditions => ["deck_cards.deck_id = ? and cards.start_card = ?", @game.deck.id, true]
Esto parece funcionar. Sin embargo, cuando intento mover estas DeckCards a otra asociación, aparece el error ActiveRecord :: ReadOnlyRecord.
Aquí está el código
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
y los Modelos relevantes (el cuadro son las cartas de los jugadores en la mesa)
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
Estoy haciendo una acción similar justo después de este código, agregando DeckCards
a la mano del jugador, y ese código funciona bien. Me preguntaba si necesitaba belong_to: tableau
en el modelo DeckCard, pero funciona bien para agregar a la mano del jugador. Tengo una columna tableau_id
y hand_id
en la tabla DeckCard.
Busqué ReadOnlyRecord en la API de rieles, y no dice mucho más allá de la descripción.
Solución
Rails 2.3.3 y versiones inferiores
Del ActiveRecord CHANGELOG
(v1.12.0, 16 de octubre de 2005) :
Introducir registros de solo lectura. Si llamas a object.readonly! entonces lo hará marcar el objeto como solo lectura y elevar ReadOnlyRecord si llamas object.save. object.readonly? informes si el objeto es de solo lectura. Pasando: readonly = > fiel a cualquier el método del buscador marcará devuelto registros como de solo lectura. El: se une La opción ahora implica: solo lectura, así que si usa esta opción, guardando la misma el registro ahora fallará. Use find_by_sql para evitarlo.
Usar find_by_sql
no es realmente una alternativa, ya que devuelve datos sin procesar de fila / columna, no ActiveRecords
. Tienes dos opciones:
- Forzar la variable de instancia
@readonly
a false en el registro (piratear) - Use
: include = > : tarjeta
en lugar de: join = > : tarjeta
Rails 2.3.4 y superior
La mayoría de lo anterior ya no es cierto, después del 10 de septiembre de 2012:
- usando
Record.find_by_sql
es una opción viable -
: readonly = > verdadero se infiere automáticamente solo si : une
se especificó sin unexplícito: seleccione
ni una opción: readonly
explícita (o hereditaria-alcance-heredada) (vea la implementación deset_readonly_option!
enactive_record / base.rb
para Rails 2.3.4, o la implementación deto_a
enactive_record / relacion.rb
y decustom_join_sql
enactive_record / relacion /query_methods.rb
para Rails 3.0.0) - sin embargo,
: readonly = > verdadero
siempre se infiere automáticamente enhas_and_belongs_to_many
si la tabla de unión tiene más de las dos columnas de claves foráneas y se especificó: une
sin unexplícito: seleccione
(es decir, los valores: readonly
proporcionados por el usuario se ignoran; consultefinding_with_ambiguous_select?
enactive_record / asociaciones / has_and_belongs_to_many_association.rb
.) - en conclusión, a menos que se trate de una tabla de unión especial y
has_and_belongs_to_many
, entonces la respuesta de@aaronrustad
se aplica perfectamente en Rails 2.3.4 y 3.0.0. - no no use
: incluye
si desea lograr unaUNIÓN INTERNA
(: incluye
implica un < code> LEFT OUTER JOIN , que es menos selectivo y menos eficiente queINNER JOIN
.)
Otros consejos
O en Rails 3 puede usar el método de solo lectura (reemplace " ... " con sus condiciones):
( Deck.joins(:card) & Card.where('...') ).readonly(false)
Esto podría haber cambiado en la versión reciente de Rails, pero la forma adecuada de resolver este problema es agregar : readonly = > falso a las opciones de búsqueda.
select ('*') parece corregir esto en Rails 3.2:
> Contact.select('*').joins(:slugs).where('slugs.slug' => 'the-slug').first.readonly?
=> false
Solo para verificar, omitir select ('*') produce un registro de solo lectura:
> Contact.joins(:slugs).where('slugs.slug' => 'the-slug').first.readonly?
=> true
No puedo decir que entiendo la lógica, pero al menos es una solución rápida y limpia.
En lugar de find_by_sql, puede especificar a: select en el buscador y todo vuelve a ser feliz ...
start_cards = DeckCard.find: all,
: select = > 'deck_cards. *',
: se une = > [:tarjeta],
: condiciones = > [" deck_cards.deck_id =? y cards.start_card =? " ;, @ game.deck.id, verdadero]
Para desactivarlo ...
module DeactivateImplicitReadonly
def custom_join_sql(*args)
result = super
@implicit_readonly = false
result
end
end
ActiveRecord::Relation.send :include, DeactivateImplicitReadonly