2010-10-18 14 views
23

Supponiamo di avere un modello di post e un modello di commento. Usando un modello comune, Post ha molti commenti.default_scope e associazioni

Se Commento ha un set default_scope:

default_scope where("deleted_at IS NULL") 

Come posso facilmente recuperare tutti i commenti su un post, a prescindere dal campo di applicazione? Questo produce risultati non validi:

Post.first.comments.unscoped 

che genera le seguenti query:

SELECT * FROM posts LIMIT 1; 
SELECT * FROM comments; 

Invece di:

SELECT * FROM posts LIMIT 1; 
SELECT * FROM comments WHERE post_id = 1; 

Esecuzione:

Post.first.comments 

produce:

Comprendo il principio di base dell'annullamento della rimozione di tutti gli ambiti esistenti, ma non dovrebbe essere a conoscenza e mantenere l'ambito di associazione?

Qual è il modo migliore per tirare TUTTI i commenti?

+2

Perché non ci pensi intorno e crei un 'scope: active, dove (" deleted_at IS NULL ")' e lo chiama quando richiesto? – Yannis

+1

Questo è quello che farei anch'io. Se ti ritrovi a dover "annullare" il tuo valore predefinito, allora non è davvero un buon valore predefinito. –

+3

Non sono d'accordo. Penso che sia buona norma nascondere per impostazione predefinita e sovrascrivere esplicitamente quella predefinita quando necessario. Questo è in qualche modo correlato alle contromisure XSS di Rails. Dovevi html_escape ogni output di contenuto generato dall'utente in Rails 2. Al giorno d'oggi, tutto è sfuggito di default e devi sovrascriverlo manualmente anche se hai molti contenuti non generati dall'utente. Riguarda la sicurezza. Sicurezza delle informazioni in questo caso. Potresti essere denunciato per avere un commento illegale sulla tua pagina perché hai dimenticato il named_scope. Oppure i clienti impazziscono per vedere contenuti inediti su qualche pagina. – crispy

risposta

0
class Comment 
    def post_comments(post_id) 
    with_exclusive_scope { find(all, :conditions => {:post_id => post_id}) } 
    end 
end 

Comment.post_comments(Post.first.id) 
+0

Per me è un po 'fastidioso. Post/commenti, è solo un esempio, non vorresti ripetere questo attraverso molti modelli diversi, vero? Mi rendo conto di non averlo menzionato in origine. – releod

+1

Questo diventerà davvero brutto molto rapidamente. Se anche questo è hackerato secondo i tuoi gusti (è davvero un hackish), prova a rivalutare usando default_scope in primo luogo. – Faisal

+2

Wow! questo è preatty rotto in rotaie! In questo modo, default_scope è inutilizzabile. Ci dovrebbero essere post.unscoped_comments e metodi comment.unscoped_post generati ... – iwiznia

9

with_exlusive_scope è deprecato come di Rails 3. Vedere this commit.

Prima (Rails 2):

Comment.with_exclusive_scope { Post.find(post_id).comments } 

Dopo (Rails 3):

Comment.unscoped { Post.find(post_id).comments } 
14

per qualche strano motivo,

Comment.unscoped { Post.last.comments } 

include la default_scope di Comment,

tuttavia,

Comment.unscoped { Post.last.comments.to_a } 
Comment.unscoped { Post.last.comments.order } 

fanno non includono la default_scope di Comment.

Ho sperimentato questo in una sessione rails console con Rails 3.2.3.

+0

Sto riscontrando il tuo problema, nel mio caso 'Post.last .comments.order' funziona, mentre 'Post.last.comments.to_a' è rotto. Hai trovato qualche indizio su questo? Cercherò di aggiornare la mia app all'ultima Rails per vedere se è stata riparata. – Fabio

+1

Questo succede anche in 3.2.6, probabilmente un bug. – Fabio

+1

Ancora esiste in 'rails 4.1.1',' order (: id) 'aiuta a escludere il default_order. – freemanoid

6

Questo è davvero un problema molto frustrante che viola il principio di minima sorpresa.

Per il momento, si può solo scrivere:

Comment.unscoped.where(post_id: Post.first) 

Questa è la soluzione più elegante/IMO semplice.

Oppure:

Post.first.comments.scoped.tap { |rel| rel.default_scoped = false } 

Il vantaggio di questi ultimi:

class Comment < ActiveRecord::Base 
    # ... 

    def self.with_deleted 
    scoped.tap { |rel| rel.default_scoped = false } 
    end 
end 

allora si può fare cose divertenti:

Post.first.comments.with_deleted.order('created_at DESC') 

Dal Rails 4, Model.all restituisce un ActiveRecord :: Relazione, piuttosto che una serie di record. Così si può (e si deve) usare all invece di scoped:

Post.first.comments.all.tap { |rel| rel.default_scoped = false } 
7

Rails 4.1.1

Comment.unscope(where: :deleted_at) { Post.first.comments } 

O

Comment.unscoped { Post.first.comments.scope } 

Nota che ho aggiunto .scope, sembra che questo blocco dovrebbe restituire il tipo di ActiveRecord_AssociationRelation (cosa .scope non) ActiveRecord_Associations_CollectionProxy (senza .scope)

Problemi correlati