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

我是铁杆新手。 爆炸了。 但查询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’时,我得到以下exception:

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

这对我来说没有多大意义……因为如果名称拼错了,为什么当我测试隔离时它不会失败?

有人可以帮帮我吗? 这个让我疯了。 在这种情况下,我只想编写完整的sql或Hibernate QL,所以我可以完成它:(

如果我只是把问题完全弄错了,那么如果你让我知道的话,我会很感激的。 我的印象是使用范围和ActiveRelation是Rails 3.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有很好的连接和范围示例:

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

 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:不要只是复制意大利面我的代码我可能会有一些拼写错误。 没有时间去certificate,抱歉。

相关: https : //gist.github.com/rails/rails/pull/5494