Ceci suit this question précédente, à laquelle une réponse a été donnée. En fait, j'ai découvert que je pouvais supprimer une jointure de cette requête. La requête de travail est maintenant
start_cards = DeckCard.find :all, :joins => [:card], :conditions => ["deck_cards.deck_id = ? and cards.start_card = ?", @game.deck.id, true]
Cela semble fonctionner. Cependant, lorsque j'essaie de déplacer ces DeckCards dans une autre association, l'erreur ActiveRecord :: ReadOnlyRecord est générée.
Voici le code
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
et les modèles pertinents (le tableau correspond aux cartes des joueurs sur la table)
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
Je fais une action similaire juste après ce code, en ajoutant DeckCards
à la main du joueur, et ce code fonctionne bien. Je me demandais si j'avais besoin de belongs_to :tableau
dans le modèle DeckCard, mais cela fonctionne bien pour l’ajout à la main du joueur. J'ai un tableau_id
et hand_id
colonnes dans la table DeckCard.
J'ai cherché ReadOnlyRecord dans le Rails api, et cela ne dit pas grand chose au-delà de la description.
Rails 2.3.3 et inférieurs
À partir de ActiveRecord CHANGELOG
(v1.12.0, 16 octobre 2005):
Introduisez des enregistrements en lecture seule. Si vous appelez object.readonly! il marquera alors l'objet comme étant en lecture seule et lèvera ReadOnlyRecord si vous appelez object.save. object.readonly? indique si l'objet est en lecture seule. Passing: readonly => true pour n’importe quelle méthode du Finder marquera les enregistrements retournés comme étant en lecture seule. L'option: join implique maintenant: readonly, donc si vous utilisez cette option, l'enregistrement du même enregistrement échouera maintenant. Utilisez find_by_sql pour contourner le problème.
L'utilisation de find_by_sql
N'est pas vraiment une alternative car elle renvoie des données de lignes/colonnes brutes, et non pas ActiveRecords
. Vous avez deux options:
@readonly
À false dans l'enregistrement (bidouille):include => :card
Au lieu de :join => :card
Rails 2.3.4 et supérieur
La plupart de ce qui précède n'est plus valable après le 10 septembre 2012:
Record.find_by_sql
est une option viable:readonly => true
Est automatiquement déduit uniquement si :joins
A été spécifié sans un :select
explicite ni une option explicite (ou Finder-scope-inherited) :readonly
(voir l'implémentation de set_readonly_option!
dans active_record/base.rb
pour Rails 2.3.4, ou l'implémentation de to_a
dans active_record/relation.rb
et de custom_join_sql
dans active_record/relation/query_methods.rb
pour Rails 3.0.0):readonly => true
est toujours automatiquement déduit dans has_and_belongs_to_many
si la table de jointure contient plus que les deux colonnes de clé étrangère et que :joins
a été spécifié sans explicite :select
( les valeurs :readonly
fournies par l'utilisateur sont ignorées - voir finding_with_ambiguous_select?
dans active_record/associations/has_and_belongs_to_many_association.rb
.)has_and_belongs_to_many
, la réponse de @aaronrustad
s'applique alors parfaitement dans Rails 2.3.4 et 3.0.0.:includes
si vous voulez obtenir un INNER JOIN
(:includes
implique un LEFT OUTER JOIN
, qui est moins sélectif et moins efficace que INNER JOIN
.)Ou dans Rails 3, vous pouvez utiliser la méthode readonly (remplacez "..." par vos conditions):
( Deck.joins(:card) & Card.where('...') ).readonly(false)
Cela a peut-être changé dans la version récente de Rails, mais la façon appropriée de résoudre ce problème consiste à ajouter : readonly => false aux options de recherche.
select ('*') semble résoudre ce problème dans Rails 3.2:
> Contact.select('*').joins(:slugs).where('slugs.slug' => 'the-slug').first.readonly?
=> false
Juste pour vérifier, omettre de sélectionner ('*') produit un enregistrement en lecture seule:
> Contact.joins(:slugs).where('slugs.slug' => 'the-slug').first.readonly?
=> true
Je ne peux pas dire que je comprends la raison, mais au moins c'est une solution de contournement rapide et propre.
Au lieu de find_by_sql, vous pouvez spécifier un: sélectionnez dans le Finder et tout est à nouveau heureux ...
start_cards = DeckCard.find :all, :select => 'deck_cards.*', :joins => [:card], :conditions => ["deck_cards.deck_id = ? and cards.start_card = ?", @game.deck.id, true]
Pour le désactiver ...
module DeactivateImplicitReadonly
def custom_join_sql(*args)
result = super
@implicit_readonly = false
result
end
end
ActiveRecord::Relation.send :include, DeactivateImplicitReadonly