2011-05-27 9 views
6

Estoy investigando cómo validates_presence_of realmente funciona. Supongamos que tengo dos modelosvalidates_presence_of with belongs_to associations, de la manera correcta

class Project < ActiveRecord::Base 
    [...] 
    has_many :roles 
end 

y

class Role < ActiveRecord::Base 
    validates_presence_of :name, :project 

    belongs_to :project 
end 

lo quiero por lo que el papel siempre pertenece a un proyecto existente, pero me acabo de enterar de this example que esto podría conducir a papeles no válidos (huérfanos) guardados en el db. Entonces, la manera correcta de hacerlo es insertar el validates_presence_of :project_id en mi modelo de Roles y parece funcionar, incluso si creo que semánticamente tiene más sentido para validar la presencia de un proyecto en lugar de una identificación de proyecto.

Además, estaba pensando que podría poner una identificación no válida (para un proyecto no existente) si solo valido la presencia de project_id, ya que de manera predeterminada AR no agrega verificaciones de integridad a las migraciones, e incluso si agrego de forma manual algunos DB no los admite (es decir, MySQL con MyISAM o sqlite). Este ejemplo demuestra que

# with validates_presence_of :name, :project, :project_id in the role class 
Role.create!(:name => 'foo', :project_id => 1334, :project => Project.new) 
    AREL (0.4ms) INSERT INTO "roles" ("name", "project_id") VALUES ('foo', NULL) 
+----+------+------------+ 
| id | name | project_id | 
+----+------+------------+ 
| 7 | foo |   | 
+----+------+------------+ 

Por supuesto no voy a escribir código como este, pero quiero evitar este tipo de datos erróneos en DB.

Me pregunto cómo asegurar que un papel SIEMPRE tenga un proyecto (real y guardado) asociado.

Encontré la gema validates_existence, pero prefiero no agregar una gema a mi proyecto a menos que sea estrictamente necesario.

¿Alguna idea de esto?

actualización

validates_presence_of :project y añadiendo :null => false para la columna de project_id en la migración parece ser una solución más limpia.

+0

Recomiendo usar la joya validates_existence para esto, ya que es exactamente lo que necesita. Además, es una dependencia bastante pequeña de tener. – Jits

+1

Simplemente no rápido: asegúrese de usar su base de datos para validar también. Hacer la vida mucho más segura. – CharlesJHardy

+0

@Jits, creo que haré eso. @Chuck Haré eso también, pero de esa manera no tendré errores de validación, por lo que todavía necesito validación a nivel de ruby. – Fabio

Respuesta

2

Probé muchas combinaciones de validadores, pero la solución más limpia es usar la gema validates_existence.Con que puedo escribir código como este

r = Role.new(:name => 'foo', :project => Project.new) # => #<Role id: nil, name: "foo", project_id: nil, created_at: nil, updated_at: nil> 
r.valid? # => false 
r.errors # => {:project=>["does not exist"], :project_id=>["does not exist"]} 

Así que mi modelo final es tan simple como

class Role < ActiveRecord::Base 
    belongs_to :project 
    validates_existence_of :project 
    # or with alternate syntax 
    validates :project, :existence => true 
    [...] 
end 

Con la validación db más solución Aditya (es decir: null => false en la migración y validates_presence_of: Proyecto en el modelo) Role#valid? devolverá true y Role#save generará una excepción en el nivel de la base de datos cuando project_id sea nulo.

6

Rails intentará encontrarlo en la identificación y agregará un error de validación si no se encuentra un objeto con una identificación.

class Role < AR::Base 
    belongs_to :project 
    validates_presence_of :project, :name 
end 


Role.create!(:name => "admin", :project_id => 1334)# Project 1334 does not exist 
# => validation error raised 

veo su problema también quiere hacer frente a la situación en la que se proporciona el objeto autor, pero es nuevo y no en dB. En el caso de que el chequeo de presencia no funcione. Resolverá.

Role.create!(:name => "admin", :project => Project.new) # Validation passes when it shouldn't. 

Actualización: Hasta cierto punto se puede mitigar el efecto del paso de un nuevo objeto ficticio al hacer una validación de la asociada: proyecto.

class Role < ActiveRecord::Base 
    belongs_to :project 
    validates_presence_of :project 
    validates_associated :project 
end 

Si Project.new.valid? es falso entonces Role.create!(:name => "admin", :project => Project.new) también generará un error. Sin embargo, si Project.new.valid? es verdadero, lo anterior creará un objeto de proyecto al guardar.

¿Te ayuda usar validates_associated :project?

+0

No me gusta el 'validates_associated: project' porque parece que necesito dos líneas para cumplir el mismo requisito, es decir, un proyecto debe existir, pero funciona. – Fabio

Cuestiones relacionadas