【问题标题】:How do I pass a string to a has_many :finder_sql parameter?如何将字符串传递给 has_many :finder_sql 参数?
【发布时间】:2008-11-30 10:08:50
【问题描述】:

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

那么如何通过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 注入的攻击)。

在一个测试数据库中,我在tickets表中添加了一个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 参数,因为我的用户表确实有一个 id 主键列,在应用程序的其他地方使用。

Edit#2:显然我没有足够彻底地阅读文档:has_many 关联可以采用 :primary_key 参数来指定哪一列是本地主键(默认 id)。谢谢丹尼尔让我大开眼界!我想它回答了我原来的问题:

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

但我仍然很想知道如何使 has_many :tickets_since_subscription 关联发挥作用。

【问题讨论】:

    标签: ruby-on-rails has-many finder-sql


    【解决方案1】:

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

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

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

    【讨论】:

    • 天啊,我不敢相信我在阅读文档 10 次时没有看到这个选项!我想我需要一些假期。非常感谢你! :-)
    【解决方案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
    

    (当然你需要用户模型中的订阅日期列)。

    您可以找到更多示例here

    如果你不想使用named_scopes,你可以通过这个找到你想要的:

    user.tickets.all(:conditions => ['created_at > ?', subscription_date]) 
    

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

      • 谢谢。但我试过了,但它不起作用,因为 users#login 列不是 users 表的主键。它确实有一个 id 主键。如果可能的话,它会起作用: :foreign_key=>"user_login", :key=>"login" 但是 :key 参数不存在。
      【解决方案4】:

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

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

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

      【讨论】:

        猜你喜欢
        • 2011-06-29
        • 1970-01-01
        • 1970-01-01
        • 2018-05-07
        • 2016-08-02
        • 2016-03-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多