当两次引用同一模型时,Rails N + 1

时间:2017-01-22 13:43:23

标签: ruby-on-rails activerecord controller

我有2个型号:

class User < ActiveRecord::Base

end

class Message < ActiveRecord::Base
  has_one :sender, :class_name => 'User'
  has_one :recipient, :class_name => 'User'
end

我希望获得用户收到的所有消息,以便messages_controller.rb我有:

  def messages_to_user
    messages = Message.where(recipient_id: current_user.id)

    respond_with messages
  end

此服务有效但在日志中我可以看到经典的N + 1问题:

Started GET "/messages_to_user" for 127.0.0.1 at 2017-01-22 15:35:01 +0200
Processing by MessageController#user as */*
  User Load (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ?  ORDER BY "users"."id" ASC LIMIT 1  [["id", 2]]
  Message Load (0.0ms)  SELECT "messages".* FROM "messages" WHERE "messages"."recipient_id" = ?  [["recipient_id", 2]]
[active_model_serializers]   User Load (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1  [["id", 2]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1  [["id", 2]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1  [["id", 2]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1  [["id", 2]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1  [["id", 2]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1  [["id", 2]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1  [["id", 2]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1  [["id", 2]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1  [["id", 2]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1  [["id", 2]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1  [["id", 2]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1  [["id", 2]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1  [["id", 2]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1  [["id", 2]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1  [["id", 2]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1  [["id", 2]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1  [["id", 2]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1  [["id", 2]]
[active_model_serializers] Rendered ActiveModel::Serializer::CollectionSerializer with ActiveModelSerializers::Adapter::Attributes (42.01ms)
Completed 200 OK in 217ms (Views: 63.0ms | ActiveRecord: 3.0ms)

所以我尝试通过添加includes来解决它:

  def messages_to_user
    messages = Message.includes(:recipient).where(recipient_id: current_user.id)

    respond_with messages
  end

但后来我收到以下错误:

SQLite3::SQLException: no such column: users.message_id: SELECT "users".* FROM "users" WHERE "users"."message_id" IN ('1', '2', '3', '4', '5', '6', '7', '8', '9')

如何解决? 我想返回一组消息 - 不是用户和用户内部设置的消息。但没有N + 1问题

修改

两个模型之间的连接是通过消息表完成的,这里是迁移(字段发件人,收件人),所以我不需要users.message_id中的users。此外,任何用户都可以发送/接收多条消息,也许我的实现没有反映出来?怎么做?

这是消息迁移:

class CreateMessages < ActiveRecord::Migration
  def change
    create_table :messasges do |t|
      t.string :title
      t.references :sender, index: true, foreign_key: true
      t.references :recipient, index: true, foreign_key: true
      t.string :content
      t.timestamps null: false
    end
  end
end

1 个答案:

答案 0 :(得分:1)

尝试

messages = Message.includes(:recipient).references(:recipient).where(recipient_id: current_user.id)