5

在我的应用程序中,用户 has_many 票。不幸的是,tickets 表没有 user_id:它有一个 user_login(它是一个遗留数据库)。总有一天我会改变它,但现在这个改变会产生太多的影响。

那么如何通过登录列建立“user has_many :tickets”关联呢?

我尝试了以下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 注入的影响)。

在一个测试数据库中,我在门票表中添加了一个 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 关联工作。

4

4 回答 4

4

我认为您希望:primary_key选择has_many. 它允许您指定当前表上的列,谁的值存储在:foreign_key另一个表的列中。

has_many :tickets, :foreign_key => "user_login", :primary_key => "login"

我通过阅读has_many文档发现了这一点。

于 2008-11-30T23:20:48.807 回答
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 列)。

您可以在此处找到更多示例。

如果您不想使用 named_scopes,您可以通过以下方式找到您想要的内容:

user.tickets.all(:conditions => ['created_at > ?', subscription_date]) 
于 2009-05-12T18:30:01.823 回答
1

:foreign_key我认为您正在寻找has_many. 这应该允许您指定外键不是user_id, 但是user_login,而无需调整查找器逻辑。

有关详细信息,请参阅 ActiveRecord has_many文档。

于 2008-11-30T10:16:22.913 回答
0

只是回答自己,以防万一没有更好的解决方案。我找不到 has_many 关联的解决方案,所以我最终创建了一个简单的查找器方法。一点都不好:它确实允许我调用some_user.tickets,但它并没有给我带来 has_many 关联的所有好处(即关联本身的 clear、delete、<<,... 方法)。

def tickets
    return Ticket.find(:all, :conditions=>["user_login = ?", login])
end

我仍然希望有人会提出更好的解决方案。

于 2008-11-30T17:21:57.393 回答