web-dev-qa-db-fra.com

Rails 3, current_user n'est pas accessible dans un modèle?

dans mon modèle project.rb, j'essaie de créer une portée avec une variable dynamique:

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ù dans le contrôleur je peux accéder à current_user.instance_id... Y at-il une raison pour laquelle le modèle ne peut pas y accéder et un moyen d’y accéder? En outre, est-ce le bon endroit pour créer une étendue comme celle ci-dessus, ou est-ce que cela appartient au contrôleur?

46
AnApprentice

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

Mais vous pouvez toujours créer une telle portée, il suffit de lui transmettre le paramètre depuis le 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)
73
mdrozdziel

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 astuce.

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 cela peut être considéré comme sale, car nous définissons ici une variable globale.

30
Michał Szajbe

Ryan Bates propose un moyen plutôt sûr de mettre en œuvre ce type de stratégie dans cette diffusion sur rail

C'est un épisode payé (ne me votez pas, mais vous pouvez parcourir le code source gratuitement

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

Voici les éléments clés du 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, vous pouvez faire quelque chose comme ...

default_scope { where(tenant_id: Tenant.current_id) }
8
Mark Locklear

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

@projects = current_user.instance.projects
0
sampi