2010-05-18 14 views
20

Tengo un recurso de proyectos que tiene muchas tareas. Quiero asegurarme de que cada tarea tenga un project_id agregando validates_presence_of :project_id al modelo de tareas.Rieles: valida la presencia de parent_id en la asociación has_many

Sin embargo, al crear un nuevo proyecto con tareas, project_id no estará disponible hasta que se guarde el registro, por lo tanto no puedo usar validates_presence_of :project_id.

Entonces mi pregunta es, ¿cómo valido la presencia de project_id en el modelo de tareas? Quiero asegurarme de que cada tarea tenga un padre.

...

class Project < ActiveRecord::Base 

    has_many :tasks, :dependent => :destroy 
    accepts_nested_attributes_for :tasks, :allow_destroy => true 

...

class Task < ActiveRecord::Base 

belongs_to :project 
validates_presence_of :project_id 
+0

Esta pregunta no tiene mucho sentido para mí. Desea que una tarea pertenezca a un proyecto sin tener un proyecto para comenzar ... ¿cómo es posible obtener un id para algo que no existe? – porkeypop

+3

¿Está creando tareas a través de un formulario anidado cuando crea el proyecto? –

Respuesta

17

Su código funciona:

  • Si validates_presence_of: proyecto, a continuación, siempre y cuando el proyecto está ahí, que validará. Pero si su proyecto no se guarda, aún podría guardar la tarea.
  • Si validates_presence_of: project_id, el entero debe estar allí, indicando un valor guardado.

Aquí está rSpec que demuestra el punto. Si valida: project_id, no puede guardar una tarea sin guardar el proyecto.

class Task < ActiveRecord::Base 
    belongs_to :project 
end 

/specs/model_specs/task_spec.RB

require File.dirname(__FILE__) + '/../spec_helper' 

describe Task do 

    before(:each) do 
    @project = Project.new 
    end 

    it "should require a project_id, not just a project object" do 
    task = Task.new 
    task.project = @project 
    Task.instance_eval("validates_presence_of :project_id") 
    task.valid?.should == false 
    end 

    it "should not be valid without a project" do 
    task = Task.new 
    task.project = @project 
    Task.instance_eval("validates_presence_of :project") 
    task.valid?.should == false 
    task.save.should == false 
    end 

end 
+0

Quiero marcar su respuesta como correcta, pero la marca de verificación se ha ido:/ – deb

+0

Gracias deb! Quizás vuelva pronto. –

0

Su clase Project debe definir

accepts_nested_attributes_for :tasks 

Ver Nested Model Form on Railscasts para más detalles sobre cómo hacer la forma.


EDIT:

En el formulario que debe tener algo como esto:

_form.html.erb

<% form_for @project do |f| %> 
    # project fields... 
    <% f.fields_for :tasks do |builder| %> 
     <%= render 'task_fields', :f => builder %> 
    <% end %> 
    <p><%= link_to_add_fields "Add task", f, :tasks %></p> 
    <%= f.submit %> 
<% end %> 

_task_fields.html.erb

<%= f.label :name, "Task name:" %> 
<%= f.text_field :name %> 
# task fields... 
<%= link_to_remove_fields "Delete task", f, :tasks %> 

link_to_add_fields y link_to_remove_fields son métodos definidos en application_helper para agregar/eliminar campos dinámicamente.

+0

La pregunta es cómo hacer que project_id sea obligatorio para las tareas. – deb

+0

Publiqué un ejemplo del código para ayudar a aclarar. gracias! – deb

+0

hmn - buena pregunta. Entonces, cuando se guardan juntas, no crea proyectos primero, ¿entonces las tareas? – digitalWestie

3

Tal vez no entiendo algo, pero parece que estás tratando de engañar a los rieles. ¿Por qué no acaba de hacerlo de esta manera:

class Task < ActiveRecord::Base 
    belongs_to :project 
    validate_presence_of :project 
end 
+0

Esta es una vieja pregunta: pero la razón por la que no se puede hacer una validación de asociación simple es porque la tarea está en: fields_for ... están anidados en el: formulario de proyecto. Al guardar, el project_id no estará disponible todavía para la validación y la validación fallará. Puede probar esto simplemente eliminando la validación de: proyecto. El: reverse_of asegura que la asociación está intacta en save. https://viget.com/extend/exploring-the-inverse-of-option-on-rails-model-associations – hellion

2

Tome un vistazo a esto:

https://rails.lighthouseapp.com/projects/8994/tickets/2815-nested-models-build-should-directly-assign-the-parent

Una cosa que he hecho en el pasado es añadir: validates_presence_of :parent_id, :on => :update. No es genial, pero ayuda a apretar un poco la red.

+0

¡Fantástico! Esto funciona como un encanto. – molf

+0

Esto omite por completo la acción de crear. No está bien. Use: inverse_of para asegurarse de que su objeto principal esté asociado al guardar en los campos anidados. https://viget.com/extend/exploring-the-inverse-of-option-on-rails-model-associations – hellion

15

Ver here para la respuesta definitiva:

class Project < ActiveRecord::Base 

    has_many :tasks, :dependent => :destroy, :inverse_of => :project 
    accepts_nested_attributes_for :tasks, :allow_destroy => true 

class Task < ActiveRecord::Base 

belongs_to :project 
validates_presence_of :project 

No es tan elegante si me preguntas ... Se debe validar de forma transparente.

+0

gracias voy a buscar en "inverse_of" – deb

2

Creo que estás teniendo el mismo problema que tuve. Tengo dos modelos, Cuenta y Usuario, y cuando se crea la cuenta, el primer usuario se crea a través de @account.users.build. El modelo de usuario tiene una validación validates_presence_of :account.

para realizar la primera validación de paso de usuario, he añadido el código siguiente para mi modelo de cuenta:

before_validation_on_create :initialize_users 

    def initialize_users 
    users.each { |u| u.account = self } 
    end 
1

En realidad se necesita tanto:

validates_presence_of project 
validates_presence_of project_id 

De esa manera no se guardará la tarea en cualquiera de los siguientes casos, suponiendo que solo tiene 2 proyectos válidos en la base de datos, es decir, el ID de proyecto 99 no es válido:

task.project_id = 99 
task.save 

task.project = Project.new 
task.save 

Espero que esto sea de ayuda para alguien.

Cuestiones relacionadas