需要使用范围嵌套连接的ActiveRelation

时间:2011-05-03 05:29:51

标签: ruby-on-rails ruby activerecord join nested

我是铁杆新手。爆炸了。查询API虽然给了我 有些麻烦。我一直在缩放并很快做很多事情, 但这是我第一次花费数小时试图解决这个问题。 它不像我以前用过的任何东西 - 常规SQL或Hibernate, 或者其他什么。

我的模型非常简单。

  • PrivateMessage有很多收件人
  • 收件人有一个接收者(类别用户)
    • 收件人还有'is_read'和'is_deleted'字段

我的目标是构建一个查找所有未读和未删除的查询 给定用户的私人消息。要做到这一点,我们需要加入 'private_messages'到'收件人'...然后'收件人'到 '用户'。

以下是相关的用户型号代码:

has_many :sent_messages, :class_name => 'PrivateMessage', :foreign_key => 'sender_id'
has_many :recipient_of_messages, :class_name => 'Recipient', :foreign_key => 'receiver_id'

scope :by_id, lambda { |id| where(:id => id) } 

我的收件人模型具有以下相关代码:

belongs_to :receiver, :class_name => 'User', :foreign_key => "receiver_id"
belongs_to :private_message

scope :unread, where(:is_read => false).where(:is_deleted => false)
scope :by_receiver_id, lambda { |id| Recipient.joins(:receiver).merge(User.by_id(id)) }
scope :unread_by_receiver_id, lambda { |id| unread.by_receiver_id(id) }

单独测试时,这可以100%工作。

但是,当我对私人消息查询进行编码时,我遇到了问题。

belongs_to :sender, :class_name => 'User'
has_many :recipients, :class_name => 'Recipient'

scope :sorted, order("private_messages.created_at desc")
scope :non_deleted, where(:is_deleted_by_sender => false)
scope :non_deleted_by_sender_id, lambda { |id| sorted.non_deleted.joins(:sender).merge(User.by_id(id)) }

# this scope does not work
scope :non_deleted_by_receiver_id, lambda { |id| sorted.joins(:recipients).merge(Recipient.by_receiver_id(id)) }
scope :newest, sorted.limit(3)

# this scope does not work either
scope :newest_unread_by_receiver_id, lambda { |id| newest.joins(:recipients).merge(Recipient.unread_by_receiver_id(id)) }

当我尝试使用'newest_unread_by_receiver_id'或'non_deleted_by_receiver_id'时,我得到以下异常:

ActiveRecord::ConfigurationError: Association named 'receiver' was not found; perhaps you misspelled it?

这对我来说没有多大意义......因为这个名字是拼写的 错了,为什么当我测试隔离时它不会失败?

有人可以帮帮我吗?这个让我疯了。有时 像这样,我只想在完整的sql或Hibernate QL中编程,所以我可以完成它:(

如果我只是把问题完全弄错了,那么如果你让我知道的话,我会很感激的。我的印象是使用范围和ActiveRelation是Rails 3.1中的前进方式。

由于

3 个答案:

答案 0 :(得分:1)

我可能会使用这样的东西。为清晰起见,我将示波器分开。

模型(重命名为PrivateMessage - > Message and Recipient - > MessageCopy):

class User < ActiveRecord::Base
  has_many :sent_messages, :class_name => "Message", :foreign_key => :sender_id
  has_many :sent_message_copies, :through => :sent_messages, :source => :message_copies
  has_many :received_messages, :through => :received_message_copies, :source => :message
  has_many :received_message_copies, :class_name => "MessageCopy", :foreign_key => :recipient_id
end

class Message < ActiveRecord::Base
  belongs_to :sender, :class_name => "User"
  has_many :message_copies
  has_many :recipients, :through => :message_copies
end

class MessageCopy < ActiveRecord::Base
  belongs_to :message
  belongs_to :recipient, :class_name => "User"
  scope :unread, where(:read => false)
  scope :undeleted, where(:deleted => false)
  scope :sent_to, lambda { |recipient| where(:recipient_id => recipient.id) }
end

架构(迁移会占用太多空间):

ActiveRecord::Schema.define(:version => 20110503061008) do
  create_table "message_copies", :force => true do |t|
    t.boolean  "read",         :default => false
    t.boolean  "deleted",      :default => false
    t.integer  "message_id"
    t.integer  "recipient_id"
  end
  create_table "messages", :force => true do |t|
    t.string   "title"
    t.integer  "sender_id"
  end
  create_table "users", :force => true do |t|
    t.string   "name"
  end
end

- 修改

使用联接返回消息的示例查询

Message.joins(:message_copies).where(:message_copies => {:read => false, :deleted => false, :recipient_id => 3})

消息范围重用其他模型上的范围

scope :non_deleted_by_recipient, lambda { |recipient|
  joins(:message_copies).merge(MessageCopy.unread.undeleted.sent_to(recipient))
}

- EDIT2

这个Railscast有很好的连接和范围示例:

答案 1 :(得分:0)

尽管你似乎找到了答案我想告诉你我是如何在我的应用程序中完成的:

message table:
id, sender_id, recipient_id, conversation_id, sender_deleted_at, recipient_deleted_at, title, body, (whatever you like)

conversation table:
id, sender_id, recipient_id, conversation_id, sender_deleted_at, etc.

class User < ActiveRecord::Base
  has_many :messages
  has_many :conversations
  has_many :sent_messages, :class_name => "Message", :foreign_key => "sender_id", 
                           :conditions => "sender_deleted_at IS NULL", :dependent => :destroy, :order => "created_at DESC"
  has_many :recieved_messages, :class_name => "Message", :foreign_key => "recipient_id", 
                            :conditions => "recipient_deleted_at IS NULL", :dependent => :destroy, :order => "created_at DESC"
  has_many :created_conversations, :class_name => "Conversation", :foreign_key => "sender_id"
  has_many :recieved_conversations, :class_name => "Conversation", :foreign_key => "recipient_id"
end

class Message < ActiveRecord::Base
  belongs_to :sender, :class_name => "User", :foreign_key => "sender_id"
  belongs_to :recipient, :class_name => "User", :foreign_key => "recipient_id"
  belongs_to :conversation

  before_create :assign_conversation
  after_create  :save_recipient, :set_replied_to, :send_receipt_reminder
end

class Conversation < ActiveRecord::Base
  belongs_to :sender, :class_name => "User", :foreign_key => "sender_id"
  belongs_to :recipient, :class_name => "User", :foreign_key => "recipient_id"

  has_many :messages

  scope :conversations_for_user, lambda {|user| {:conditions => ["sender_id = :user OR   recipient_id = :user", :user => user] }}
end

通过这种方式,您可以获取任何内容,还可以将消息显示为对话。您可以在当前对话中获取未读消息,可以获取给定对话或用户的所有消息等。

此外,每条消息只有一条记录,这似乎是一个不错的解决方案。如果您不想自己编写,我还可以为您提供其他方法。

此致 斯特凡诺

PS:不要只是复制意大利面我的代码我可能会有一些拼写错误。没有时间去证明,抱歉。

答案 2 :(得分:0)