ruby-on-rails - polimorficas - rails asociaciones
Rails: validar la presencia de parent_id en la asociaciĆ³n has_many (7)
Creo que tienes el mismo problema que traté. 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 validates_presence_of :account
validación de validates_presence_of :account
.
Para hacer la primera validación de paso de usuario, agregué el siguiente código a mi modelo de Cuenta:
before_validation_on_create :initialize_users
def initialize_users
users.each { |u| u.account = self }
end
Tengo un recurso de proyectos que tiene muchas tareas. Quiero asegurarme de que cada tarea tenga un project_id
de proyecto agregando validates_presence_of :project_id
al modelo de tareas.
Sin embargo, al crear un nuevo proyecto con tareas, el project_id no estará disponible hasta que el registro se guarde, por lo tanto no puedo usar validates_presence_of :project_id
.
Entonces, mi pregunta es, ¿cómo validar la presencia de project_id en el modelo de tarea? Quiero asegurar 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
En realidad necesitas tanto:
validates_presence_of project
validates_presence_of project_id
De esa manera, la tarea no se guardará en ninguno de los siguientes casos, suponiendo que solo haya 2 proyectos válidos en la base de datos, es decir, el ID del 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.
Mira esto:
Una cosa que he hecho en el pasado es agregar: validates_presence_of :parent_id, :on => :update
. No muy bueno pero ayuda a apretar un poco la red.
Tal vez no entiendo algo, pero parece que estás intentando engañar a los carriles. ¿Por qué no haces esto así?
class Task < ActiveRecord::Base
belongs_to :project
validate_presence_of :project
end
Tu código funciona:
- Si valida_presence_of: project, entonces mientras el proyecto esté allí, se validará. Pero si su proyecto no está guardado, aún podría guardar la tarea.
- Si valida_presence_of: project_id, entonces 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
Tu clase de Project
debe definir
accepts_nested_attributes_for :tasks
Consulte el Formulario modelo anidado en Railscasts para obtener más detalles sobre cómo realizar el formulario.
EDITAR:
En tu formulario deberías 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.
Vea 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 ... Debe validar de forma transparente.