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.
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))
_
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))
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)
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)
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