简体   繁体   中英

Rails validating with two models

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.

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM