In Rails, how do I create nested objects using acc

2019-04-16 01:35发布

问题:

In my Rails application, I am attempting to set up creation form for a TrainingClass model. I want this form to allow the user to create multiple ClassMeeting models (which have a belongs_to relationship with the TrainingClass model) within the same form, and I am using accepts_nested_attributes_for to do this. Unfortunately, whenever I submit the form I get the error message: Meetings class can't be blank.

I realize that this is because ClassMeeting has validates :class_id, presence: true, and because TrainingClass can't have an id until after it is saved, but I'm not sure of the right way to get around this. (I can think of a few possible ways, but they aren't exactly elegant solutions.) Any ideas? I'd appreciate any help you can give me.

Note: I realize quite a few questions similar to this have been asked in the past. However, most of those questions are old and have outdated answers, and none of them solved my problem.


Here is my code. Keep in mind that although I have simplified some aspects of it for brevity, the relationship between the ClassMeeting and TrainingClass models was left untouched:

ClassMeeting Model:

# == Schema Information
#
# Table name: class_meetings
#
#  id         :integer         not null, primary key
#  class_id   :integer
#  start      :datetime
#  end        :datetime
#  location   :string(255)
#  created_at :datetime        not null
#  updated_at :datetime        not null
#

class ClassMeeting < ActiveRecord::Base
  attr_accessible :start, :end, :location

  validates :class_id, presence: true
  validates :start, presence: true
  validates :end, presence: true
  validates :location, presence: true, length: {maximum: 255}

  belongs_to :training_class, foreign_key: :class_id, inverse_of: :meetings
end

TrainingClass Model:

# == Schema Information
#
# Table name: training_classes
#
#  id            :integer         not null, primary key
#  description   :string(255)
#  created_at    :datetime        not null
#  updated_at    :datetime        not null
#

class TrainingClass < ActiveRecord::Base
  attr_accessible :description, :meetings_attributes

  validates :description, length: {maximum: 255}

  has_many :meetings, class_name: :ClassMeeting, foreign_key: :class_id, inverse_of: :training_class

  accepts_nested_attributes_for :meetings, allow_destroy: true
end

TrainingClasses Controller:

class TrainingClassesController < ApplicationController
  def new
    @training_class = TrainingClass.new()
    @training_class.meetings.build
  end

  def create
    @training_class = TrainingClass.new()

    if @training_class.update_attributes(params[:training_class])
        redirect_to @training_class, notice: 'Class was successfully created.'
    else
        render "new"
    end
  end
end

TrainingClass Form (View):

<%= form_for @training_class do |f| %>
    <%= render 'shared/error_messages', object: f.object %>

    <%= f.text_area :description %>

    <h2>Schedule</h2>
    <%= f.fields_for :meetings do |meeting| %>
        <%= meeting.label :start, "Start of Meeting:" %>
        <%= meeting.text_field :start %>

        <%= meeting.label :end, "End of Meeting:" %>
        <%= meeting.text_field :end %>

        <%= meeting.label :location, "Location:" %>
        <%= meeting.text_field :location %>
    <% end %>

    <%= f.submit class:"btn btn-large btn-primary" %>
<% end %>

回答1:

All right, I found the solution to my problem. All I need to do is validate the presence of training_class instead of class_id in the ClassMeeting model. That way the existence of a training class is still validated, but the validator doesn't interfere with accepts_nested_attributes_for's method of saving the model:

class ClassMeeting < ActiveRecord::Base
  attr_accessible :start, :end, :location

  validates :training_class, presence: true # :training_class instead of :class_id
  validates :start, presence: true
  validates :end, presence: true
  validates :location, presence: true, length: {maximum: 255}

  belongs_to :training_class, foreign_key: :class_id, inverse_of: :meetings
end


回答2:

I pored over this example, trying out the differences with my code, but in the end my problem was fixed by using :inverse_of.

See accepts_nested_attributes_for child association validation failing