2

I am creating a nested form with attributes from different models. I expect all the required attributes to be valid, before a new object is saved.

<%= form for @product do |f| %>

  <%= f.fields_for @customer do |g| %>

    <%= g.label :name %>
    <%= g.text_field :name %>

    <%= g.label :email %>
    <%= g.text_field :email %>

    <%= g.label :city %>
    <%= g.text_field :city %>

    <%= g.label :state %>
    <%= g.text_field :state %>

    <%= g.label :zipcode %>
    <%= g.text_field :zipcode %>

  <% end %>

  <%= f.label :product %>
  <%= f.text_field :product %>

  <%= f.label :quantity %>
  <%= number_field(:quantity, in 1..10) %>

<% end %>

Here are my models

class Product < ActiveRecord::Base

  belongs_to :customer
  validates_associated :customer
  validates :product, :presence => "true"

end

class Customer < ActiveRecord::Base

  has_one :product
  validates :name, :email, presence: true
  validates :email, format: { with: /[A-Za-z\d+][@][A-Za-z\d+][.][A-Za-z]{2,20}\z/ }              
  validates :city, presence: true
  validates :zipcode, format: { with: /\A\d{5}\z/ }

end

I added validates_associated to my Product Model, so my form_for @product should require all the customer validations to pass. That means name, email, city and zipcode have to be there and have to be formatted properly.

I fiddled around, and submitted the form without filling in the Customer required fields, and the form was considered valid.

I don't understand where my mistake is.

EDIT

Alright, so by adding validates :customer, the customer attributes are now required. But they aren't actually saved to the database. I think this has to do with my params

def product_params
  params.require(:product).permit(:product, :quantity)
end

Do I need to add my Customer Params to my permitted params list?

3 Answers 3

4

The validates_associated method only validates the associated object if the object exists, so if you leave the form fields blank, the Product you are creating/editing will validate, because there is no associated Customer.

Instead, assuming you're using Rails 4+, you want to use accepts_nested_attributes_for :customer, along with validates :customer, presence: true in order to required the customer fields in your product form.

If you're using Rails 3, then accepts_nested_attributes_for will not work for a belongs_to association. Instead, your Customer class will need to use accepts_nested_attributes_for :product, and you will need to alter your form view accordingly.

UPDATE

You also need to allow your controller action to accept parameters for the :customer association:

def product_params
  params.require(:product).permit(:product, :quantity, :customer_attributes => [:name, :email, :city, :state, :zipcode])
end

It's worth noting that because there is no :id field in your customer form fields, and no :customer_id field in your product form fields, you will create a new customer every time you successfully submit the product form.

Sign up to request clarification or add additional context in comments.

1 Comment

OK, I tried adding validates :customer and now that field is required. But when I type in the field, it says it's missing. It must be because of my params. I'll see if I can fix that
3

try this out:

In Controller create an instance of a product and associated customer as follows:

  @product = Product.new
  @customer = @product.build_customer

in use this code for form

  <%= form for @product do |f| %>

  <%= f.fields_for :customer do |g| %>

    <%= g.label :name %>
    <%= g.text_field :name %>

    <%= g.label :email %>
    <%= g.text_field :email %>

    <%= g.label :city %>
    <%= g.text_field :city %>

    <%= g.label :state %>
    <%= g.text_field :state %>

    <%= g.label :zipcode %>
    <%= g.text_field :zipcode %>

  <% end %>

  <%= f.label :product %>
  <%= f.text_field :product %>

  <%= f.label :quantity %>
  <%= number_field(:quantity, in 1..10) %>

<% end %>

i.e use :customer symbol instead of @customer instance variable.

and use accepts_nested_attributes_for helper method in Product model as @Charles said

Comments

0

Complementing the other answers, I control what I receive in the controller, avoiding further action and noticing if a value is not the one I want.

  def update
    if params[:customer][:product_attributes]["0"][:name] == ""
      redirect_to customer_path(@incident), alert: 'You need to add a name'
    else
      respond_to do |format|
        if @customer.update(customer_params)
          format.html { redirect_to customer_path(@customer), notice: 'Succesfully updated' }
          format.json { render :show, status: :ok, location: @customer }
        else
          format.html { render :edit }
          format.json { render json: @customer.errors, status: :unprocessable_entity }
        end
      end
    end
  end

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.