Rails comprend avec conditions

Est-il possible dans Rails> 3.2 d’append des conditions à l’instruction de jointure générée par la méthode includes ?

Disons que j’ai deux modèles, personne et note. Chaque personne a beaucoup de notes et chaque note appartient à une seule personne. Chaque note a un atsortingbut important .

Je veux trouver toutes les personnes préchargeant uniquement les notes importantes. En SQL ce sera:

 SELECT * FROM people LEFT JOIN notes ON notes.person_id = people.id AND notes.important = 't' 

Dans Rails, la seule méthode similaire à utiliser includes à includes (note: les joins ne préchargeront pas les notes) comme ceci:

 Person.includes(:notes).where(:important, true) 

Cependant, cela générera la requête SQL suivante qui renvoie un jeu de résultats différent:

 SELECT * FROM people LEFT JOIN notes ON notes.person_id = people.id WHERE notes.important = 't' 

Veuillez noter que le premier jeu de résultats comprend toutes les personnes et le second uniquement les personnes associées aux notes importantes.

Notez également que: les conditions sont obsolètes depuis 3.1.

Selon ce guide Active Record Querying

Vous pouvez spécifier les conditions pour inclure le chargement avide comme ceci

 Person.includes(:notes).where("notes.important", true) 

Il recommande d’utiliser les joins quand même.

Une solution serait de créer une autre association comme celle-ci.

 class Person < ActiveRecord::Base has_many :important_notes, :class_name => 'Note', :conditions => ['important = ?', true] end 

Vous seriez alors en mesure de le faire

 Person.find(:all, include: :important_notes) 

La syntaxe Rails 5+:

 Person.includes(:notes).where(notes: {important: true}) 

Imbriqué:

 Person.includes(notes: [:grades]).where(notes: {important: true, grades: {important: true}) 

Rails 4.2+:

Option A – “preload” – plusieurs sélections, utilise “id IN (…)”)

 class Person < ActiveRecord::Base has_many :notes has_many :important_notes, -> { where(important: true) }, class_name: "Note" end Person.preload(:important_notes) 

SQL:

 SELECT "people".* FROM "people" SELECT "notes".* FROM "notes" WHERE "notes"."important" = ? AND "notes"."person_id" IN (1, 2) 

Option B – “eager_load” – un énorme choix, utilise “LEFT JOIN”)

 class Person < ActiveRecord::Base has_many :notes has_many :important_notes, -> { where(important: true) }, class_name: "Note" end Person.eager_load(:important_notes) 

SQL:

 SELECT "people"."id" AS t0_r0, "people"."name" AS t0_r1, "people"."created_at" AS t0_r2, "people"."updated_at" AS t0_r3, "notes"."id" AS t1_r0, "notes"."person_id" AS t1_r1, "notes"."important" AS t1_r2 FROM "people" LEFT OUTER JOIN "notes" ON "notes"."person_id" = "people"."id" AND "notes"."important" = ? 

Je n’ai pas pu utiliser les include avec une condition comme la réponse de Leo Correa. Insted je dois utiliser:

 Lead.includes(:contacts).where("contacts.primary" =>true).first 

ou vous pouvez aussi

 Lead.includes(:contacts).where("contacts.primary" =>true).find(8877) 

Ce dernier récupèrera le Lead avec l’ID 8877 mais inclura seulement son contact principal

L’une des méthodes consiste à écrire la clause LEFT JOIN en utilisant des jointures:

 Person.joins('LEFT JOIN "notes" ON "notes"."person_id" = "people.id" AND "notes"."important" IS "t"') 

Pas joli, cependant.

Pour les personnes intéressées, j’ai essayé ceci quand un atsortingbut d’enregistrement était faux

 Lead.includes(:contacts).where("contacts.primary" => false).first 

et cela ne fonctionne pas. En quelque sorte, pour les booléens, seuls les true travaux, donc je l’ai retourné pour inclure where.not

 Lead.includes(:contacts).where.not("contacts.primary" => true).first 

Cela fonctionne parfaitement

La même chose a été discutée dans le stackoverflow japonais. Assez pirate, mais le suivant semble fonctionner, du moins sur les rails 5.

 Person.eager_load(:notes).joins("AND notes.important = 't'") 

Un aspect important est que de cette manière, vous pouvez écrire des conditions de jointure arbitraires. L’inconvénient est que vous ne pouvez pas utiliser d’espace réservé, vous devez donc faire attention lorsque vous utilisez des parameters comme condition de jointure.

https://ja.stackoverflow.com/q/22812/754