ruby-on-railsvalidationactiverecordmodelnested-form-for

Rails model validation on form with new parent and child


I'm new to rails- but now wondering, are Rails validations lost on model objects when in my form I'm trying to create both the child and parent at the same time?

In my example (models in bold) 1 - Cake - has_many - CakeDetails

many - CakeDetails - belong_to 1 - Size

1 - CakeType - belongs_to 1 - Cake

I've managed to create a form That allows me to create a Cake, specify the sizes, associated prices and what type the cake will be.

But it seems all built in model validation is gone.

Is this correct? How do I bring validation back to my model objects?

When I render the new action I bring up a blank form and when I submit a blank form I get the following:

Started POST "/cakes" for 127.0.0.1 at 2018-04-25 21:19:03 +0100
Processing by CakesController#create as JS
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"r6B2ult7w7z4vez95f2pNXmk7Q1rYSzj8Wi9VwnS4EgIAw8PG/w7isfAeiiSB9sY8+0+oHldMVpy1GFKBmrpmw==", "cake"=>{"name"=>"", "description"=>"", "cake_type_id"=>"", "cake_details_attributes"=>{"0"=>{"_destroy"=>"true", "price"=>""}, "1"=>{"_destroy"=>"true", "price"=>""}}}, "commit"=>"Create Cake"}
   (0.1ms)  begin transaction
   (0.1ms)  rollback transaction
  Rendering cakes/new.html.erb within layouts/application
  CakeType Load (0.3ms)  SELECT "cake_types".* FROM "cake_types"
  Rendered cakes/_form.html.erb (15.9ms)
  Rendered cakes/new.html.erb within layouts/application (19.6ms)
Completed 200 OK in 109ms (Views: 100.4ms | ActiveRecord: 0.5ms)

I get that its rolling back the transaction because there is no cake_type_id. But there is no errors displayed.

The following is the error displaying section in my form. I've tried adding more to check for errors in the nested object but not seeing anything.

  <% if @cake.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(cake.errors.count, "error") %> prohibited this cake from being saved:</h2>
      <ul>
      <% @cake.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>
  <% @cake.cake_details.each do |detail| %>
    <% if detail.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(cake.errors.count, "error") %> prohibited this cake from being saved:</h2>
      <ul>
      <% @cake.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
    <% end %>
  <% end %>

My Cake model

class Cake < ApplicationRecord
  validates :name, :description, :cake_type, presence: true

  belongs_to :cake_type
  has_many :cake_details, -> {order('size_id ASC')}, inverse_of: :cake
  has_many :sizes, through: :cake_details
  accepts_nested_attributes_for :cake_details, :allow_destroy => true

  validates_associated  :cake_type, :cake_details
end

Cakes Controller

  def create
    #@cake = Cake.new(params[cake:{}])
    #params.require(:cake).permit(:name, :description,:cake_type_id)

    @cake = Cake.new(cake_params)
    respond_to do |format|
      if @cake.save
        format.html { redirect_to @cake, notice: 'Cake was successfully created.' }
        format.json { render :show, status: :created, location: @cake }
      else
        format.html { render :new }
        format.json { render json: @cake.errors, status: :unprocessable_entity }
      end
    end
  end

def cake_only_params
  params.require(:cake).permit(:name, :description,:cake_type_id)
end

def cake_params
  params.require(:cake).permit(:name, :description,:cake_type_id, cake_type_id:[:type_id],
  cake_details_attributes:[:_destroy, :id, :cake_id, :size_id, :price])
end

UPDATE

I thought I had to access the variable I declared in my action e.g @cake but I updated my form to use this

<%= form_with(model: cake, local: true) do |form| %>

instead of how I thought I needed it:

<%= form_with(model: @cake) do |form| %>

I'll read up on the docs but any comments on why I messed this up is definitely appreciated. Just starting to learn this framework... :/


Solution

  • I thought I had to access the variable I declared in my action e.g

    @cake

    but I updated my form to use this:

    <%= form_with(model: cake, local: true) do |form| %>
    

    instead of how I thought I needed it:

    <%= form_with(model: @cake) do |form| %>
    

    and that works now.

    Special thanks to jvillian for responding so quickly.

    And noting that:

    By adding local: true to form_with, you force the form to submit as html and your controller then uses format.html.