web-dev-qa-db-fra.com

Comment retourner une relation ActiveRecord vide?

Si j'ai une portée avec un lambda et qu'il faut un argument, en fonction de la valeur de l'argument, je sais peut-être qu'il n'y aura pas de correspondance, mais je veux quand même renvoyer une relation, pas un tableau vide:

scope :for_users, lambda { |users| users.any? ? where("user_id IN (?)", users.map(&:id).join(',')) : [] }

Ce que je veux vraiment, c'est une méthode "none", l'opposé de "all", qui renvoie une relation qui peut toujours être chaînée, mais qui entraîne le court-circuit de la requête.

230
dzajic

Il existe maintenant un mécanisme "correct" dans Rails 4:

>> Model.none 
=> #<ActiveRecord::Relation []>
443
steveh7

Une solution plus portable qui ne nécessite pas de colonne "id" et ne présume pas qu'il n'y aura pas de ligne avec un id de 0:

scope :none, where("1 = 0")

Je cherche toujours un moyen plus "correct".

75
steveh7

Entrée Rails 4

Dans Rails 4, un ActiveRecord::NullRelation sera renvoyé d'appels comme Post.none.

Ni lui, ni les méthodes chaînées, ne générera des requêtes à la base de données.

Selon les commentaires:

ActiveRecord :: NullRelation renvoyé hérite de Relation et implémente le modèle d'objet Null. C'est un objet avec un comportement null défini et renvoie toujours un tableau vide d'enregistrements sans interroger la base de données.

Voir le code source .

43
Nathan Long

Vous pouvez ajouter une portée appelée "aucune":

scope :none, where(:id => nil).where("id IS NOT ?", nil)

Cela vous donnera un ActiveRecord :: Relation vide

Vous pouvez également l'ajouter à ActiveRecord :: Base dans un initialiseur (si vous le souhaitez):

class ActiveRecord::Base
 def self.none
   where(arel_table[:id].eq(nil).and(arel_table[:id].not_eq(nil)))
 end
end

Beaucoup de façons d'obtenir quelque chose comme ça, mais certainement pas la meilleure chose à garder dans une base de code. J'ai utilisé le scope: aucun lors du refactoring et constatant que je devais garantir une ActiveRecord :: Relation vide pendant une courte période.

42
Brandon
scope :none, limit(0)

Est une solution dangereuse parce que votre portée pourrait être enchaînée.

User.none.first

retournera le premier utilisateur. Il est plus sûr d'utiliser

scope :none, where('1 = 0')
24
bbrinck

Je pense que je préfère la façon dont cela ressemble aux autres options:

scope :none, limit(0)

Menant à quelque chose comme ceci:

scope :users, lambda { |ids| ids.present? ? where("user_id IN (?)", ids) : limit(0) }
14
Alex

Utilisation portée:

 portée: for_users, lambda {| users | utilisateurs.any? ? où ("user_id IN (?)", users.map (&: id) .join (',')): scoped} 

Mais, vous pouvez aussi simplifier votre code avec:

 portée: for_users, lambda {| users | où (: user_id => users.map (&: id)) si users.any? } 

Si vous voulez un résultat vide, utilisez ceci (supprimez la condition if):

 portée: for_users, lambda {| users | où (: user_id => users.map (&: id))} 
3
Pan Thomakos

C'est possible et c'est donc:

scope :for_users, lambda { |users| users.any? ? where("user_id IN (?)", users.map(&:id).join(',')) : User.none }

http://apidock.com/Rails/v4.0.2/ActiveRecord/QueryMethods/none

Corrige moi si je me trompe.

1
ilgam

Il y a aussi des variantes, mais toutes font une demande à db

where('false')
where('null')
1
fmnoise