简体   繁体   中英

Rails Activerecord multiple table includes

I have four tables:

argument with fields

  • id

comments with

  • id
  • comment_id
  • argument_id
  • user_id

users

  • id

nicknames with

  • id
  • proposal_id
  • user_id
  • name

each argument has many comments,

each comment belongs to a user,

each user has a specific nickname in the argument.

When I fetch the argument comments from DB, I would like to include also the nicknames of each author.

The answer is about the ActiveRecord query I don't know how to write .

I tried with

@argument.comments.includes(:user => :nicknames)

but it doesn't seems to work and when I get the nickname through nickname = @argument.nicknames.find_by_user_id(comment.user.id) it executes the query...

[1m[36mNickname Load (0.6ms)[0m  [1mSELECT "nicknames".* FROM "nicknames" WHERE "nicknames"."argument_id" = 59 AND "nicknames"."user_id" = 9 LIMIT 1[0m

any suggestion?

You can tell if an association is loaded with loaded? .

What is happening here, if I understand your problem, is that you are trying to run a finder on an ActiveRecord::Relation. Quickly browsing through the code , it does not appear that it will try to see if a collection is loaded before it issues the query. It does, however, take a block that will avoid multiple queries. For example (the model names have been changed because I am using a sample project I created for another question):

c = Canteen.first
Canteen Load (0.2ms)  SELECT "canteens".* FROM "canteens" LIMIT 1
=> #<Canteen id: 1, name: "Really good place", created_at: "2012-12-13 00:04:11", updated_at: "2012-12-13 00:04:11">

c.meals.loaded?
=> false

c.meals.find {|m| m.id == 3}
  Meal Load (0.2ms)  SELECT "meals".* FROM "meals" WHERE "meals"."canteen_id" = 1
=> #<Meal id: 3, canteen_id: 1, name: "Banana Pie", price: #<BigDecimal:7fcb6784fa78,'0.499E1',18(45)>, created_at: "2012-12-13 00:37:41", updated_at: "2012-12-13 00:37:41">

You see in the last example that ActiveRecord issues the query to load the associated records. This is because ActiveRecord is calling to_a on the association, forcing the entire set to be loaded, and then filtering based on the block conditions. Obviously, this is not ideal.

Let's try again, eager loading the association.

c = Canteen.includes(:meals).first
  Canteen Load (0.2ms)  SELECT "canteens".* FROM "canteens" LIMIT 1
  Meal Load (0.2ms)  SELECT "meals".* FROM "meals" WHERE "meals"."canteen_id" IN (1)
=> #<Canteen id: 1, name: "Really good place", created_at: "2012-12-13 00:04:11", updated_at: "2012-12-13 00:04:11">

c.meals.loaded?
=> true

c.meals.find {|m| m.id == 3}
=> #<Meal id: 3, canteen_id: 1, name: "Banana Pie", price: #<BigDecimal:7fcb68b596f0,'0.499E1',18(45)>, created_at: "2012-12-13 00:37:41", updated_at: "2012-12-13 00:37:41">

In the last example here, you see that the collection is not loaded again. Instead, the block is used to filter the already loaded records.

As you can see below, even if the records are loaded, ActiveRecord will issue a query to grab the associated record:

c.meals.loaded?
=> true

c.meals.find(1)
  Meal Load (0.1ms)  SELECT "meals".* FROM "meals" WHERE "meals"."canteen_id" = 1 AND "meals"."id" = ? LIMIT 1  [["id", 1]]
=> #<Meal id: 1, canteen_id: 1, name: "Enchiladas", price: #<BigDecimal:7fcb6584ce88,'0.699E1',18(45)>, created_at: "2012-12-13 00:04:40", updated_at: "2012-12-13 00:04:40">

SELECT "meals".* FROM "meals" WHERE "meals"."canteen_id" = 1 AND "meals"."id" = 3
=> [#<Meal id: 3, canteen_id: 1, name: "Banana Pie", price: #<BigDecimal:7fcb68b808e0,'0.499E1',18(45)>, created_at: "2012-12-13 00:37:41", updated_at: "2012-12-13 00:37:41">]

Maybe something like :

@argument.includes(:comments => [{ :user => :nicknames }])

Didn't try it though...

你可以尝试这样的东西来包含多个表

 User.find(:all, :include => Room.find(:all,:include => :review))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM