更改Rails belongs_to,has_many默认选择(Where)语句

时间:2012-01-10 21:03:40

标签: mysql ruby-on-rails ruby ruby-on-rails-3 arel

我正试图找出一种方法来改变belongs_to,has_many关系,从而在两者上做正常和预期(见下文)的行为...

'SELECT `transactions`.* FROM `transactions` WHERE `transactions`.`account_id` = 2'

'SELECT `transactions`.* FROM `transactions` WHERE `transactions`.`account_id` = 2' OR `transactions`.`transfer_id` = 3'

当前设置...

class Account < ActiveRecord::Base
  has_many :transactions
end

class Transaction < ActiveRecord::Base
  belongs_to :account
end

class CreateTransactions < ActiveRecord::Migration
  def change
    create_table :transactions do |t|
      t.string        :name,
      t.references    :account,
      t.timestamps
    end
  end
end

尝试解决方案......

class Account < ActiveRecord::Base
  has_many :transactions, :class_name => "Transaction", :finder_sql => ->(record) do
    record = self if(record.nil?)
    "SELECT * FROM transactions WHERE transactions.account_id = #{record.id} OR transactions.transfer_id = #{record.id}"
  end
end

class Transaction < ActiveRecord::Base
  belongs_to :transfer, :class_name => "Account", :foreign_key => 'transfer_id'
end

class CreateTransactions < ActiveRecord::Migration
  def change
    create_table :transactions do |t|
      t.string        :name,
      t.references    :account,
      t.integer       :transfer_id,
      t.timestamps
    end
  end
end

问题是,如何更改默认WHERE以包含OR。在不使用foreign_key的情况下向has_many添加范围或添加似乎会产生...

'SELECT transactions。* FROM transactions WHERE transactionsaccount_id = 2'(AND transactionstransfer_id = 3')

事实上,我使用rails传统方式的所有尝试似乎产生了上述内容。

计算如何更改默认关系的重要性是为了确保查找account_id的所有select语句也会查看transfer_id。

非常感谢您的帮助。

1 个答案:

答案 0 :(得分:2)

很抱歉,但我不相信has_many会支持您所寻找的内容。按照惯例,期望关联引用给定的所有键。在您的情况下,您尝试引用其中一个键。

最简单的解决方案是创建2个单独的关联,然后在需要的地方合并它们:

class Account < ActiveRecord::Base
  has_many :transactions # this will use the 'account_id' field by convention
  has_many :transfer_transactions, :class_name => "Transaction", :foreign_key => :transfer_id
  def all_transactions
    (transactions + transfer_transactions).uniq
  end
end

这里的缺点是您正在进行2次数据库调用,而#all_transactions方法将无法作为嵌套关联使用。

另一种可能性是修改您的架构并为每个物理交易创建2个交易条目,并标记借记/贷方方向。无论如何,这更符合双重入账:

class Account < ActiveRecord::Base
  has_many :transactions

  def credit_transactions
    transactions.credit
  end

  def debit_transactions
    transactions.debit
  end
end

class Transaction < ActiveRecord::Base
  belongs_to :account
  # add some validation to make sure you set the debit/credit flag

  scope :credit, where(:credit => true)
  scope :debit, where(:credit => false)

  # some virtual accessors for 'debit'
  def debit=(direction)
    credit = !direction
  end
  def debit?
    !credit?
  end
end

class CreateTransactions < ActiveRecord::Migration
  def change
    create_table :transactions do |t|
      t.string        :name
      t.references    :account
      t.boolean :credit, :nil => false
      t.timestamps
    end
  end
end

您可能想要研究的另一个选择是'Squeel'宝石。它基本上是一个用于SQL的ruby DSL,并允许各种复杂的查询。但是,我不确定它是否适用于has_many声明。