2011-08-19 2 views
5

Ho tre modelli ActiveRecord: Partner, MembershipChannel (che è un modello di STI, che eredita da Channel) e ChannelMembership (non ero responsabile per la denominazione di questi modelli ...)Perché il mio has_many è associato al record (a volte) di sola lettura?

quando carico un ChannelMembership attraverso l'associazione partner, mi a volte (!) finisce con una registrazione di sola lettura. Questo è in Rails 3.0.9. Lo stesso codice non si comporta in questo modo in 2.3.11.

> p = Partner.first 
> p.channel_memberships.map(&:readonly?) 
# => [false, false, false, false, false, false] 
> p.reload.channel_memberships.limit(1).first.readonly? 
# => false 
> p.reload.channel_memberships.first.readonly? 
# => true 

Perché readonly? vero quando first viene chiamato l'associazione, ma non sulla relazione da limit?

Capisco che readonly venga attivato se utilizzo frammenti SQL durante la ricerca di un record, ma questo non è il caso qui. È solo un semplice has_many attraverso l'associazione. L'unica cosa che complica è che si unisce a un modello STI. Cosa c'è di più, guardando l'SQL generato dagli ultimi due esempi, sono identici!

Posso ottenere il comportamento desiderato specificando :readonly => false sull'associazione, ma voglio capire cosa sta succedendo.

Non ci sono ambiti di default su Canale, MembershipChannel o ChannelMembership. Ecco la dichiarazione di associazione in merito Partner:

class Partner 
    has_many :membership_channels 
    has_many :channel_memberships, :through => :membership_channels 
end 

Ecco l'SQL generato da miei log:

Partner Load (0.4ms) SELECT "partners".* FROM "partners" LIMIT 1 
    ChannelMembership Load (0.7ms) SELECT "channel_memberships".* FROM "channel_memberships" INNER JOIN "channels" ON "channel_memberships".channel_id = "channels".id WHERE (("channels".partner_id = 2) AND (("channels"."type" = 'MembershipChannel'))) 
    Partner Load (0.5ms) SELECT "partners".* FROM "partners" WHERE "partners"."id" = 2 LIMIT 1 
    ChannelMembership Load (1.0ms) SELECT "channel_memberships".* FROM "channel_memberships" INNER JOIN "channels" ON "channel_memberships".channel_id = "channels".id WHERE (("channels".partner_id = 2) AND (("channels"."type" = 'MembershipChannel'))) LIMIT 1 
    Partner Load (0.4ms) SELECT "partners".* FROM "partners" WHERE "partners"."id" = 2 LIMIT 1 
    ChannelMembership Load (0.6ms) SELECT "channel_memberships".* FROM "channel_memberships" INNER JOIN "channels" ON "channel_memberships".channel_id = "channels".id WHERE (("channels".partner_id = 2) AND (("channels"."type" = 'MembershipChannel'))) LIMIT 1 

risposta

2

sono stato in grado di riprodurre il problema attraverso un has_many base: attraverso l'associazione e sono anche da che cosa sta causando

Da quello che posso dire, succede solo quando il metodo di ricarica viene chiamato sull'oggetto originale. Non sono sicuro se questo è dovuto a qualcosa che il reload sta facendo in modo specifico, o forse perché alcuni flag di attributo sono stati ripristinati?

La mia seconda teoria è che ha qualcosa a che fare con il fatto che

p.reload.channel_memberships.limit(1) 

restituisce un ActiveRecord :: Relation attraverso il quale si ottiene la vostra prima ChannelMembership, e

p.reload.channel_memberships.first 

lo carica direttamente dall'associazione. Forse una combinazione di reimpostazione della ricarica di alcuni elementi memorizzati nella cache (non conosco la fonte AR) sta segnalando l'associazione come sola lettura. Quando si applica l'ambito limite (1) su di esso, è possibile che vengano reimpostati in una nuova relazione e funzionino come ci si aspetterebbe.

Mi giro attorno ad ActiveRecord :: Persistenza/Associazioni un po 'di più per la risposta completa.

+0

Sì, penso che questo sarebbe un buon momento per scavare in AR e AREL. In ogni caso, intendevo imparare come orientarmi nella fonte dei binari. –