2008-11-30 7 views
5

En mi aplicación, un usuario tiene_muchas entradas. Desafortunadamente, la tabla de tickets no tiene un user_id: tiene un user_login (es una base de datos heredada). Voy a cambiar eso algún día, pero por ahora este cambio tendría demasiadas implicaciones.¿Cómo paso una cadena a un parámetro has_many: finder_sql?

Entonces, ¿cómo puedo crear una asociación de "usuario has_many: tickets" a través de la columna de inicio de sesión?

Intenté lo siguiente finder_sql, pero no funciona.

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

me sale un error extraño:

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 

También probé este finder_sql (con comillas dobles alrededor del inicio de sesión):

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

Pero no de la misma manera (y de todos modos , si funciona, sería vulnerable a la inyección sql).

En una base de datos de prueba, he añadido una columna user_id en la tabla de entradas y probamos este finder_sql:

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

Ahora bien, esto funciona bien. Entonces, aparentemente, mi problema tiene que ver con el hecho de que la columna de usuarios que trato de usar es una cadena, no una identificación.

He buscado en la red durante bastante tiempo ... pero no pude encontrar una pista.

Me gustaría ser capaz de pasar cualquier parámetro a la finder_sql, y escribir cosas como esta:

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

Editar: no puedo usar el: parámetro foreign_key de la asociación has_many porque mi tabla de usuarios hace tienen una columna de clave principal de identificación, que se utiliza en otro lugar de la aplicación.

Edición # 2: aparentemente no leí la documentación lo suficiente: la asociación has_many puede tomar un parámetro: primary_key, para especificar qué columna es la clave primaria local (identificación predeterminada). ¡Gracias Daniel por abrir mis ojos! Supongo que responde a mi pregunta original:

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

Pero aun así, el amor para saber cómo puedo hacer que el has_many: tickets_since_subscription trabajo asociación.

Respuesta

4

Creo que quieres la opción :primary_key en . Le permite especificar la columna en la tabla actual cuyo valor se almacena en la columna :foreign_key en la otra tabla.

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

me encontré con esto leyendo los documentos has_many.

+1

¡Dios mío, no puedo creer que no haya visto esta opción cuando leí los documentos 10 veces! Creo que necesito algunas vacaciones. ¡Muchas gracias! :-) – MiniQuark

1

Creo que está buscando la opción :foreign_key en has_many. Eso debería permitirle especificar que la clave externa no es user_id, sino user_login, sin ajustar la lógica del buscador.

Consulte la documentación de ActiveRecord has_many para obtener más información.

+0

Gracias. Pero lo intenté y no funciona porque la columna # de inicio de sesión de los usuarios no es la clave principal de la tabla de usuarios. Tiene una clave principal de identificación. Funcionaría si esto fuera posible: : foreign_key => "user_login", **: key => "login" ** Pero el parámetro: key no existe. – MiniQuark

0

Simplemente respondiendo a mí mismo, en caso de que no haya una solución mejor. No pude encontrar una solución con la asociación has_many, así que terminé creando un método de búsqueda simple. No es nada bueno: me permite llamar al algún_usuario.tickets, pero no me da todos los beneficios de las asociaciones has_many (concretamente, los métodos borrar, eliminar, < <, ... en la asociación misma).

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

Sigo esperando que alguien encuentre una solución mejor.

2

tener algo como has_many: tickets_since_subscription puede utilizar named_scopes:

En el modelo de complemento:

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

Con esto, usted puede encontrar lo que quiere de esta manera:

user.tickets.since_subscription 3.days.ago 

o

user.tickets.since_subscription user.subscription_date 

(por supuesto, necesita la columna subscription_date en el modelo de usuario).

Puede encontrar más ejemplos here.

Si no desea utilizar named_scopes se puede encontrar lo que busca con esto:

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