2010-09-18 7 views
45

dans mon modèle project.rb, je suis en train de créer un champ avec une variable dynamique:Rails 3 concevoir, current_user n'est pas accessible dans un modèle?

scope :instanceprojects, lambda { 
    where("projects.instance_id = ?", current_user.instance_id) 
} 

Je reçois l'erreur suivante:

undefined local variable or method `current_user' for #<Class:0x102fe3af0> 

où je peux accéder au contrôleur current_user.instance_id ... Y a-t-il une raison pour que le modèle ne puisse pas y accéder et un moyen d'y avoir accès? Aussi, est-ce le bon endroit pour créer une portée comme ci-dessus, ou est-ce que cela appartient au contrôleur?

Répondre

71

Cela n'a pas beaucoup de sens, comme vous l'avez déjà souligné. Le current_user n'appartient pas à la logique du modèle, il devrait être géré au niveau du contrôleur.

Mais vous pouvez toujours créer la portée comme ça, il suffit de passer le paramètre à partir du contrôleur:

scope :instanceprojects, lambda { |user| 
    where("projects.instance_id = ?", user.instance_id) 
} 

Maintenant, vous pouvez l'appeler dans le contrôleur:

Model.instanceprojects(current_user) 
+1

qui est génial. essayer maintenant – AnApprentice

+0

A travaillé super. Je vous remercie! – AnApprentice

+7

J'ai appelé la portée «de» afin qu'il lirait bien. 'Project.of current_user' – romeroabelleira

27

La réponse déjà acceptée fournit un moyen vraiment correct d'y parvenir.

Mais voici la version thread-safe de User.current_user tour.

class User 
    class << self 
    def current_user=(user) 
     Thread.current[:current_user] = user 
    end 

    def current_user 
     Thread.current[:current_user] 
    end 
    end 
end 

class ApplicationController 
    before_filter :set_current_user 

    def set_current_user 
    User.current_user = current_user 
    end 
end 

Cela fonctionne comme prévu, mais il peut être considéré comme sale, car nous définissons essentiellement une variable globale ici.

+2

Merci - J'ai eu une situation spécifique où j'avais vraiment besoin de cela et n'avait aucun moyen de le transmettre. BTW, ce code a quelques erreurs - vous devez utiliser l'utilisateur.current_user = méthode de ApplicationController (sinon, pourquoi l'avoir), et aussi il est défini dans: current_user, mais le lecteur lit à partir de: utilisateur (m'a pris beaucoup de temps pour voir ça!) – Jords

+0

Merci de le signaler. Réponse mise à jour –

+0

J'ai essayé cela et fonctionne parfaitement. Cependant, dans mon environnement de développement, il n'y a pas de situation multi-utilisateur. Je suis toujours préoccupé par les commentaires dans d'autres réponses sur le problème de thread sans danger. Quelqu'un d'autre a-t-il testé cela dans un environnement de production? À votre santé! –

8

Ryan Bates établit un moyen assez sûr de mettre en œuvre ce type de stratégie in this railscast

Cet épisode payé (ne me votez pas vers le bas!) Mais vous pouvez browse the source code for free

Ici, il crée une méthode current_tenant , mais vous pouvez facilement remplacer current_user à la place.

Voici les bits clés de code ...

#application_controller.rb 
around_filter :scope_current_tenant 

private 

def current_tenant 
    Tenant.find_by_subdomain! request.subdomain 
end 
helper_method :current_tenant 

def scope_current_tenant 
    Tenant.current_id = current_tenant.id 
    yield 
ensure 
    Tenant.current_id = nil 
end 

#models/tenant.rb 

def self.current_id=(id) 
    Thread.current[:tenant_id] = id 
end 

def self.current_id 
    Thread.current[:tenant_id] 
end 

Ensuite, dans le modèle que vous pouvez faire quelque chose comme ...

default_scope { where(tenant_id: Tenant.current_id) } 
0

Vous n'avez pas besoin d'utiliser les étendues. Si vous avez défini les associations appropriées dans les modèles, suivant morceau de code placé dans le contrôleur devrait faire l'affaire:

@projects = current_user.instance.projects 
Questions connexes