0

Ok, bit confused on how to solve this issue.

I have one form and two models. Here is my form:

<% if @booking.errors.any? %>
  <% @booking.errors.full_messages.each do |msg| %>
    <p class="error"><%= msg %></p>
  <% end %>
<% end %>

<% if @guest.errors.any? %>
  <% @guest.errors.full_messages.each do |msg| %>
    <p class="error"><%= msg %></p>
  <% end %>
<% end %>

<%= form_for :booking, url: bookings_path do |f| %>
  <%= label_tag :email, "Guest's Email Address" %>
  <%= text_field_tag :email %>
  <%= f.label :nights, "Nights" %>
  <%= f.text_field :nights %>
  <%= f.label :nights, "People" %>
  <%= f.text_field :people %>
  <%= f.label :nights, "Arrival Date" %>
  <%= f.text_field :arrival %>
<% end %>

As you can see, the email field isn't part of the form builder. The email address will be used to create a new Guest record if the email doesn't already exist. Once I have the ID of the guest then the booking record can be made also.

Here is the code for create action in my BookingController - where the form is submitted to...

...

def create
  accommodation = current_user.accommodation
  guest = Guest.find_or_create_by(:email => params[:email])
  @booking = accommodation.bookings.new(post_params.merge(:guest_id => guest.id))

  if @booking.save
    flash[:success] = 'The booking has been added successfully.'  
    redirect_to :controller => 'bookings', :action => 'index'
  else
    render 'new'
  end
end

...

I do realise this question isn't new but I can't find a good solution anywhere to my problem - I want to be able to set the form up properly (if necessary) and validate all fields using the two models. Then I need to display the error messages. At the moment, my email is ignored during validation and I'm not sure what to do next.

Any help much appreciated.

4

2 回答 2

1

It seems to me that the easiest way to is to validate the email in the controller itself and add any validation error to the booking variable. Something like this:

def create
  accommodation = current_user.accommodation
  guest = Guest.find_or_create_by(:email => params[:email])
  @booking = accommodation.bookings.new(post_params.merge(:guest_id => guest.id))

  if @booking.save
    flash[:success] = 'The booking has been added successfully.'  
    redirect_to :controller => 'bookings', :action => 'index'
  else
    <% if params[:email].blank> %>
       @booking.errors.add(:email, "can't be blank.")
    <% end %>

    #You can do the same thing for whatever other validation errors you have

    render 'new'
  end
end

Note: I did not test the code

This is probably not the best way possible but it gets the job done and is easy. You could use accept_nested_attributes_for but it seems to me a little bit unnecessary considering that you are only validating an email. Nevertheless, if you want to do it the cleanest way, stick with accept_nested_attributes_for.

EDIT

Actually, your code is the right track. You just made a syntax error. The real reason your guests errors are not being shown is that you used a local variable instead of a instance variable. Try this:

@guest = Guest.find_or_create_by(:email => params[:email])

Your error messages should be displayed with the code you already have

<% if @guest.errors.any? %>
  <% @guest.errors.full_messages.each do |msg| %>
    <p class="error"><%= msg %></p>
  <% end %>
<% end %>

EDIT 2

In order to avoid a booking instance from beings saved in case the guest email is invalid you can do something like this:

if !@guest.errors.any? && @booking.save
    flash[:success] = 'The booking has been added successfully.'  
    redirect_to :controller => 'bookings', :action => 'index'
  else

Therefore, if the guest has any errors, the if statement will terminate before the @booking.save statement is executed.

于 2013-09-13T20:25:43.913 回答
0

You can try to do a transaction. If one of them is invalid, rails will do a rollback and you can render the errors.

Follow this question code, see if it helps.

于 2013-09-13T20:03:20.440 回答