在我的应用程序中,用户has_many票证。不幸的是,票证表没有user_id:它有一个user_login(它是一个遗留数据库)。我有一天会改变这一点,但是现在这种改变会产生太多影响。
那么如何通过登录列建立“用户has_many:门票”关联?
我尝试了以下finder_sql,但它不起作用。
class User < ActiveRecord::Base
has_many :tickets,
:finder_sql => 'select t.* from tickets t where t.user_login=#{login}'
...
end
我得到一个奇怪的错误:
ArgumentError: /var/lib/gems/1.8/gems/activesupport-2.0.2/lib/active_support/dependencies.rb:402:in `to_constant_name': Anonymous modules have no name to be referenced by
from /var/lib/gems/1.8/gems/activerecord-2.0.2/lib/active_record/base.rb:2355:in `interpolate_sql'
from /var/lib/gems/1.8/gems/activesupport-2.0.2/lib/active_support/dependencies.rb:214:in `qualified_name_for'
from /var/lib/gems/1.8/gems/activesupport-2.0.2/lib/active_support/dependencies.rb:477:in `const_missing'
from (eval):1:in `interpolate_sql'
from /var/lib/gems/1.8/gems/activerecord-2.0.2/lib/active_record/associations/association_proxy.rb:95:in `send'
from /var/lib/gems/1.8/gems/activerecord-2.0.2/lib/active_record/associations/association_proxy.rb:95:in `interpolate_sql'
from /var/lib/gems/1.8/gems/activerecord-2.0.2/lib/active_record/associations/has_many_association.rb:143:in `construct_sql'
from /var/lib/gems/1.8/gems/activerecord-2.0.2/lib/active_record/associations/has_many_association.rb:6:in `initialize'
from /var/lib/gems/1.8/gems/activerecord-2.0.2/lib/active_record/associations.rb:1032:in `new'
from /var/lib/gems/1.8/gems/activerecord-2.0.2/lib/active_record/associations.rb:1032:in `tickets'
from (irb):1
我也尝试了这个finder_sql(在登录时使用双引号):
:finder_sql => 'select t.* from tickets t where t.user_login="#{login}"'
但它以同样的方式失败(无论如何,如果它有效,它将容易受到sql注入的攻击)。
在测试数据库中,我在ticket表中添加了一个user_id列,并尝试了这个finder_sql:
:finder_sql => 'select t.* from tickets t where t.user_login=#{id}'
现在这很好用。显然,我的问题与我尝试使用的用户列是字符串而不是id的事实有关。
我在网上搜了很长时间......但是找不到线索。
我希望能够将任何参数传递给finder_sql,并编写如下内容:
has_many :tickets_since_subscription,
:finder_sql => ['select t.* from tickets t where t.user_login=?'+
' and t.created_at>=?', '#{login}', '#{subscription_date}']
编辑:我不能使用has_many关联的:foreign_key参数,因为我的users表 有一个id主键列,在应用程序的其他地方使用。
编辑#2:显然我没有彻底阅读文档:has_many关联可以采用:primary_key参数,指定哪个列是本地主键(默认id)。谢谢丹尼尔睁开眼睛!我想它回答了我原来的问题:
has_many tickets, :primary_key="login", :foreign_key="user_login"
但我仍然想知道如何使has_many:tickets_since_subscription关联工作。
答案 0 :(得分:4)
我认为您需要:primary_key
选项has_many
。它允许您指定当前表中列的值,该列的值存储在另一个表的:foreign_key
列中。
has_many :tickets, :foreign_key => "user_login", :primary_key => "login"
我通过阅读has_many文档找到了这一点。
答案 1 :(得分:2)
要使用has_many:tickets_since_subscription,你可以使用named_scopes:
在模型中添加:
named_scope :since_subscription, lambda { |subscription_date| { :conditions => ['created_at > ?', subscription_date] }
有了这个,你可以找到你想要的东西:
user.tickets.since_subscription 3.days.ago
或
user.tickets.since_subscription user.subscription_date
(当然,您需要在用户模型中使用subscription_date列。)
您可以找到更多示例here。
如果你不想使用named_scopes,你可以用这个找到你想要的东西:
user.tickets.all(:conditions => ['created_at > ?', subscription_date])
答案 2 :(得分:1)
我认为您正在寻找:foreign_key
上的has_many
选项。这应该允许您指定外键不是user_id
,而是user_login
,而不调整查找器逻辑。
有关详细信息,请参阅ActiveRecord has_many文档。
答案 3 :(得分:0)
回答自己,万一没有更好的解决方案。我找不到has_many关联的解决方案,所以我最终创建了一个简单的finder方法。根本不是很好:它确实允许我调用 some_user.tickets ,但它没有给我所有has_many关联的好处(即clear,delete,&lt;&lt;,...关联本身的方法)。
def tickets
return Ticket.find(:all, :conditions=>["user_login = ?", login])
end
我仍然希望有人能提出更好的解决方案。