2008-11-30 8 views
5

In meiner Anwendung hat ein Benutzer has_many Tickets. Leider hat die Ticket-Tabelle keine user_id: Sie hat eine user_login (es ist eine Legacy-Datenbank). Ich werde das irgendwann ändern, aber im Augenblick hätte diese Änderung zu viele Auswirkungen. Verband durch die Login Säule:Wie übergebe ich eine Zeichenfolge an einen Parameter has_many: finder_sql?

Wie kann ich eine „tickets Benutzer has_many“ bauen?

Ich habe versucht, die folgenden finder_sql, aber es funktioniert nicht.

class User < ActiveRecord::Base 
    has_many :tickets, 
     :finder_sql => 'select t.* from tickets t where t.user_login=#{login}' 
    ... 
end 

ich einen seltsamen Fehler:

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 

Ich habe auch versucht, diese finder_sql (mit Anführungszeichen um die Login):

:finder_sql => 'select t.* from tickets t where t.user_login="#{login}"' 

Aber es die gleiche Art und Weise versagt (und sowieso , wenn es funktioniert, wäre es anfällig für SQL-Injektion).

In einer Testdatenbank, fügte ich eine user_id Spalte in der Karten-Tabelle und versucht, diese finder_sql:

:finder_sql => 'select t.* from tickets t where t.user_login=#{id}' 

Jetzt das funktioniert gut. Offensichtlich hat mein Problem damit zu tun, dass die Benutzerspalte, die ich verwenden möchte, eine Zeichenfolge und keine ID ist.

Ich suchte das Netz für einige Zeit ... konnte aber keinen Hinweis finden.

ich jeden Parameter an die finder_sql passieren zu können, würde lieben, und schreiben Dinge wie diese:

has_many :tickets_since_subscription, 
:finder_sql => ['select t.* from tickets t where t.user_login=?'+ 
    ' and t.created_at>=?', '#{login}', '#{subscription_date}'] 

Edit: foreign_key Parameter des has_many Vereinigung, weil meine Benutzer-Tabelle tut: Ich habe das nicht verwenden können, haben eine ID-Primärschlüsselspalte, die an anderer Stelle in der Anwendung verwendet wird.

Edit # 2: Offenbar habe ich die Dokumentation nicht gründlich genug gelesen: die has_many-Verbindung kann einen: primary_key-Parameter verwenden, um anzugeben, welche Spalte der lokale Primärschlüssel ist (Standard-ID). Danke Daniel, dass du meine Augen öffnest! Ich denke, es ist meine ursprüngliche Frage beantwortet:

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

Aber ich würde noch gerne wissen, wie ich die has_many machen kann: tickets_since_subscription Verbandsarbeit.

Antwort

4

Ich glaube, Sie wollen die :primary_key Option has_many. Sie können die Spalte der aktuellen Tabelle angeben, deren Wert in der Spalte :foreign_key in der anderen Tabelle gespeichert wird.

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

Ich fand dies durch Lesen der has_many Dokumente.

+1

OMG, ich kann nicht glauben, dass ich diese Option nicht gesehen habe, als ich die Dokumente 10 mal gelesen habe! Ich denke, ich brauche Ferien. Vielen Dank! :-) – MiniQuark

1

Ich denke, Sie suchen nach der :foreign_key Option auf has_many. Damit können Sie angeben, dass der Fremdschlüssel nicht user_id ist, sondern user_login, ohne die Finderlogik anzupassen.

Weitere Informationen finden Sie in der Dokumentation ActiveRecord has_many.

+0

Dank. Aber ich habe das ausprobiert und es funktioniert nicht, weil die Login-Spalte der Benutzer nicht der Primärschlüssel der Benutzertabelle ist. Es hat einen ID-Primärschlüssel. Es würde funktionieren, wenn das möglich wäre: : foreign_key => "user_login", **: schlüssel => "login" ** Aber der: Schlüsselparameter existiert nicht. – MiniQuark

0

Ich antworte nur mir selbst, falls es keine bessere Lösung gibt. Ich konnte mit der has_many-Zuordnung keine Lösung finden, also habe ich eine einfache Finder-Methode erstellt. Überhaupt nicht großartig: es erlaubt mir, some_user.tickets zu nennen, aber es gibt mir nicht alle Vorteile der has_many Verbände (nämlich die löschen, löschen, < <, ... Methoden auf der Assoziation selbst).

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

Ich hoffe immer noch, dass jemand mit einer besseren Lösung kommen wird.

2

Um so etwas wie has_many haben:

In Modell Add: tickets_since_subscription Sie named_scopes können

named_scope :since_subscription, lambda { |subscription_date| { :conditions => ['created_at > ?', subscription_date] } 

Mit diesem können Sie finden, was Sie so wollen:

user.tickets.since_subscription 3.days.ago 

oder

user.tickets.since_subscription user.subscription_date 

(Sie benötigen natürlich die Spalte subscription_date im Benutzermodell).

können Sie weitere Beispiele here finden.

Wenn Sie nicht wollen, named_scopes verwenden, können Sie finden, was Sie mit diesem wollen:

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