5

私のアプリケーションでは、ユーザーは has_many チケットを持っています。残念ながら、チケット テーブルには 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 パラメータを使用できません。ユーザー テーブルには、アプリケーションの他の場所で使用される 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 docsを読んでこれを見つけました。

于 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 アソシエーションでは解決策が見つからなかったので、単純な finder メソッドを作成することになりました。some_user.ticketsを呼び出すことはできますが、 has_many アソシエーション (つまり、アソシエーション自体の clear、delete、<<、... メソッド) のすべての利点が得られるわけではありません。

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

私はまだ誰かがより良い解決策を考え出すことを望んでいます.

于 2008-11-30T17:21:57.393 に答える