本文介绍了将复杂的has_many关系移植到Rails> 4.1(不使用finder_sql)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在将Rails应用程序移植到Rails 4.2。该Rails应用程序包含一些相当复杂的手动SQL代码关联-部分是由于数据库优化(例如,子选择而不是JOIN),部分是由于在撰写本文时没有可行的选择(Rails 3.0),部分原因是由于缺乏知识(我希望,至少-这很容易解决。)

I am porting a Rails app to Rails 4.2. This Rails app contains some rather complex manual SQL code in associations - partly due to DB optimizations (e.g. subselects instead of JOINs), partly due to no feasible alternative at the time of writing (Rails 3.0), partly surely due to lack of knowledge (I hope, at least - that would be easy to solve).

示例:一个InternalMessage类。消息可以在用户之间发送(InternalMessage的收件人和消息的删除,因为可能有多个,因此存储在InternalMessagesRecipients中),并且可以读取,回复,转发和删除它们。关联看起来像这样:

Example: An InternalMessage class. Messages can be sent between users (Recipients of an InternalMessage, and 'deletions' of messages, are stored in InternalMessagesRecipients, since there can be several) and they can be read, replied to, forwarded and deleted. The association looks like this:

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

关键部分是最后的 OR子句-通过这种关联,我想同时接收和发送消息,这些消息分别与用户表一起加入:

The key part is the "OR" clause at the end - with this association I want to get both received and sent messages, which are joined with the user table seperately:

  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可能有多个收件人(也可以自己发送给发件人)

since an InternalMessage might have several recipients (and can also be sent to the sender himself).

问:如何将 finder_sql 移植到与Rails 4.2兼容的有很多定义吗?

Q: How do I port this finder_sql to a Rails 4.2 compatible has_many definition?

推荐答案

更新

我前一段时间了解到这没有任何意义。 has_many 关系必须至少在一个方向上具有内射连接,因此SQL子句中的 OR是没有意义的。 CREATE 操作应如何确定满足新创建记录的条件?此关系按定义是只读的,因此不是 has_many 关系。

I learnt a while ago that this makes no sense. A has_many relationship must have injective connections at least in one direction, so an "OR" in a SQL clause makes no sense. How should a CREATE operation decide which condition to satisfy to create a new record? This relationship is read only by definition and so it is not a has_many relationship.

在这种情况下,简单类方法(或范围)将是正确的答案,而不是 has_many 。要合并来自多个查询的结果,请使用

In this case, a simple class method (or scope) would be the right answer instead of has_many. To concatenate results from several queries use something like

def internal_messages
  InternalMessage.where( id: sent_message_ids + received_message_ids)
end

保持结果对象可链接(即 @user)。 internal_messages.by_date 等)

to keep the resulting object chainable (i.e. @user.internal_messages.by_date etc.)

这篇关于将复杂的has_many关系移植到Rails&gt; 4.1(不使用finder_sql)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-24 06:42