我正试图找出一种方法来改变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 transactions
。account_id
= 2'(AND transactions
。transfer_id
= 3')
事实上,我使用rails传统方式的所有尝试似乎产生了上述内容。
计算如何更改默认关系的重要性是为了确保查找account_id的所有select语句也会查看transfer_id。
非常感谢您的帮助。
答案 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声明。