我正在将Rails应用程序移植到Rails 4.2。这个Rails应用程序包含一些相当复杂的关联手动SQL代码 - 部分原因是DB优化(例如子选择而不是JOIN),部分原因是由于在编写时没有可行的替代方案(Rails 3.0),部分原因是由于缺乏知识(我希望,至少 - 这很容易解决。)
示例:InternalMessage类。可以在用户之间发送消息(内部消息的收件人,以及消息的删除,存储在InternalMessagesRecipients中,因为可以存在多个消息),并且可以读取,回复,转发和删除消息。该协会看起来像这样:
class User < AR::Base
has_many :internal_messages,
:finder_sql => "SELECT DISTINCT(internal_messages.id), internal_messages.* FROM internal_messages " +
' LEFT JOIN internal_messages_recipients ON internal_messages.id=internal_messages_recipients.internal_message_id' +
' WHERE internal_messages.sender_id = #{id} OR internal_messages_recipients.recipient_id = #{id}',
:counter_sql => 'SELECT count(DISTINCT(internal_messages.id)) FROM internal_messages ' +
' LEFT JOIN internal_messages_recipients ON internal_messages.id=internal_messages_recipients.internal_message_id' +
' WHERE internal_messages.sender_id = #{id} OR internal_messages_recipients.recipient_id = #{id}'
# ...
end
关键部分是&#34; OR&#34;最后的子句 - 通过这种关联,我希望得到收到和发送的消息,这些消息与用户表单独连接:
has_many :sent_messages, -> { where(:sender_deleted_at => nil) }, :class_name => 'InternalMessage', :foreign_key => 'sender_id' #, :include => :sender
has_many :internal_messages_recipients, :foreign_key => 'recipient_id'
has_many :rcvd_messages, :through => :internal_messages_recipients, :source => :internal_message, :class_name => 'InternalMessage'
因为InternalMessage可能有多个收件人(也可以自己发送给发件人)。
问:如何将此finder_sql
移植到与Rails 4.2兼容的has_many
定义?
答案 0 :(得分:4)
<强>更新强>
前一段时间我才知道这没有道理。 has_many
关系必须至少在一个方向上具有内射连接,因此&#34; OR&#34;在SQL子句中毫无意义。 CREATE
操作应该如何决定创建新记录要满足哪个条件?此关系仅按定义读取,因此不是has_many
关系。
在这种情况下,简单的类方法(或范围)将是正确的答案,而不是has_many
。要连接多个查询的结果,请使用类似
def internal_messages
InternalMessage.where( id: sent_message_ids + received_message_ids)
end
保持结果对象可链接(即@user.internal_messages.by_date
等)
答案 1 :(得分:3)
将包含SQL字符串的proc作为范围传递。
has_many :internal_messages, -> { proc { "SELECT DISTINCT(internal_messages.id), internal_messages.* FROM internal_messages " +
' LEFT JOIN internal_messages_recipients ON internal_messages.id=internal_messages_recipients.internal_message_id' +
' WHERE internal_messages.sender_id = #{id} OR internal_messages_recipients.recipient_id = #{id}' } }