Rails includes with conditions

前端 未结 7 1025
执笔经年
执笔经年 2020-12-08 03:33

Is is possible in Rails > 3.2 to add conditions to the join statement generated by the includes method?

Let\'s say I have two models, Person and Note. E

相关标签:
7条回答
  • 2020-12-08 04:13

    Rails 5+ syntax:

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

    Nested:

    Person.includes(notes: [:grades]).where(notes: {important: true, grades: {important: true})
    
    0 讨论(0)
  • 2020-12-08 04:14

    I was unable to use the includes with a condition like Leo Correa's answer. Insted I neeed to use:

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

    or you can also

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

    This last one will retrieve the Lead with id 8877 but will only include its primary contact

    0 讨论(0)
  • 2020-12-08 04:18

    One way is to write the LEFT JOIN clause yourself by using joins:

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

    Not pretty, though.

    0 讨论(0)
  • 2020-12-08 04:28

    According to this guide Active Record Querying

    You can specify conditions on includes for eager loading like this

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

    It recommends to use joins anyway.

    A workaround for this would be to create another association like this

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

    You would then be able to do this

    Person.find(:all, include: :important_notes)
    
    0 讨论(0)
  • 2020-12-08 04:29

    Rails 4.2+:

    Option A - "preload": multiple selects, uses "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": one huge select, uses "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" = ?
    
    0 讨论(0)
  • 2020-12-08 04:31

    Same was discussed in Japanese stackoverflow. Quite hacky, but following seems to work, at least on rails 5.

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

    One important aspect is that by this way, you can write arbitrary join condition. Down side is that you cannot use placeholder so you need to be careful when using params as the join condition.

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

    0 讨论(0)
提交回复
热议问题