web-dev-qa-db-fra.com

Rails où la condition utilise NOT NIL

En utilisant le style Rails 3, comment pourrais-je écrire le contraire de:

Foo.includes(:bar).where(:bars=>{:id=>nil})

Je veux trouver où id n'est pas nul. J'ai essayé:

Foo.includes(:bar).where(:bars=>{:id=>!nil}).to_sql

Mais cela revient:

=> "SELECT     \"foos\".* FROM       \"foos\"  WHERE  (\"bars\".\"id\" = 1)"

Ce n'est certainement pas ce dont j'ai besoin, et cela ressemble presque à un bug dans ARel.

351
SooDesuNe

La manière canonique de faire cela avec Rails 3:

_Foo.includes(:bar).where("bars.id IS NOT NULL")
_

ActiveRecord 4.0 et versions ultérieures ajoute where.not pour que vous puissiez le faire:

_Foo.includes(:bar).where.not('bars.id' => nil)
Foo.includes(:bar).where.not(bars: { id: nil })
_

Lorsque je travaille avec des étendues entre des tables, je préfère utiliser merge pour pouvoir utiliser plus facilement des étendues existantes.

_Foo.includes(:bar).merge(Bar.where.not(id: nil))
_

De plus, puisque includes ne choisit pas toujours une stratégie de jointure, vous devez utiliser references ici aussi, sinon vous pourriez vous retrouver avec un SQL invalide.

_Foo.includes(:bar)
   .references(:bar)
   .merge(Bar.where.not(id: nil))
_
499
Adam Lassek

Ce n'est pas un bug dans ARel, c'est un bug dans votre logique.

Ce que vous voulez ici c'est:

Foo.includes(:bar).where(Bar.arel_table[:id].not_eq(nil))
251
Ryan Bigg

Pour Rails4:

Donc, ce que vous voulez, c'est une jointure interne. Vous devez donc utiliser le prédicat join:

  Foo.joins(:bar)

  Select * from Foo Inner Join Bars ...

Mais, pour mémoire, si vous voulez une condition "NOT NULL", utilisez simplement le prédicat not:

Foo.includes(:bar).where.not(bars: {id: nil})

Select * from Foo Left Outer Join Bars on .. WHERE bars.id IS NOT NULL

Notez que cette syntaxe signale une dépréciation (elle parle d'un extrait de chaîne SQL, mais je suppose que la condition de hachage est remplacée par une chaîne dans l'analyseur?), Assurez-vous donc d'ajouter les références à la fin:

Foo.includes(:bar).where.not(bars: {id: nil}).references(:bar)

AVERTISSEMENT DE DEPRECATION: Il semble que vous chargiez une (des) table (s) (s) (s): qui sont référencées dans un extrait SQL de chaîne. Par exemple:

Post.includes(:comments).where("comments.title = 'foo'")

Actuellement, Active Record reconnaît la table dans la chaîne et sait comment JOIN à la table des commentaires, plutôt que de charger des commentaires dans une requête distincte. Cependant, cela sans écrire un analyseur SQL complet est fondamentalement défectueux. Puisque nous ne voulons pas écrire un analyseur SQL, nous supprimons cette fonctionnalité. A partir de maintenant, vous devez explicitement indiquer à Active Record lorsque vous référencez une table à partir d'une chaîne:

Post.includes(:comments).where("comments.title = 'foo'").references(:comments)
36
Matt Rogish

Je ne suis pas sûr que cela soit utile, mais voici ce qui a fonctionné pour moi dans Rails 4

Foo.where.not(bar: nil)
23
Raed Tulefat

Avec Rails 4, rien de plus simple:

 Foo.includes(:bar).where.not(bars: {id: nil})

Voir aussi: http://guides.rubyonrails.org/active_record_querying.html#not-conditions

21
Tilo