【问题标题】:Rails validating with two modelsRails 使用两个模型进行验证
【发布时间】:2014-06-19 02:33:11
【问题描述】:

好的,有点困惑如何解决这个问题。

我有一个表格和两个模型。这是我的表格:

<% 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 %>

如您所见,电子邮件字段不是表单构建器的一部分。如果电子邮件不存在,则电子邮件地址将用于创建新的访客记录。一旦我有了客人的ID,就可以进行预订记录了。

这是在我的 BookingController 中创建操作的代码 - 表单被提交到...

...

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

...

我确实意识到这个问题并不新鲜,但我在任何地方都找不到解决问题的好方法 - 我希望能够正确设置表单(如有必要)并验证所有领域都使用这两个模型。然后我需要显示错误消息。目前,我的电子邮件在验证过程中被忽略,我不知道下一步该怎么做。

非常感谢任何帮助。

【问题讨论】:

  • 在什么意义上它被忽略了?如果电子邮件不存在,您要么选择现有来宾,要么创建新来宾。您的意思是在显示表单错误时忽略?
  • 抱歉,我不知道如何验证所有数据,因为验证规则是在两个不同的模型中定义的。那么如何在验证预订数据的同时验证电子邮件呢?
  • 是的,我记得你的最后一个问题。有没有想过使用嵌套表单? railscasts.com/episodes/196-nested-model-form-part-1
  • 我实际上看过那个视频。使用嵌套表单处理真的是个问题吗?我想我必须将 form_for 对象更改为 guest,然后添加 fields_for 进行预订 - 即便如此 - 我还没有看到使用两个模型的验证规则并显示错误(如果有)的示例。
  • 只有在表单与 ActiveRecord 对象没有关联时才应该使用符号参数。在您的情况下,与大多数情况一样,您的表单已连接到 AR 对象,因此您应该使用form_for @booking。不与 AR 对象关联的表单的一个示例是联系表单。

标签: ruby-on-rails ruby-on-rails-4


【解决方案1】:

在我看来,最简单的方法是在控制器本身中验证电子邮件并将任何验证错误添加到预订变量中。像这样的:

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

注意:我没有测试代码

这可能不是最好的方法,但它可以完成工作并且很容易。您可以使用 accept_nested_attributes_for 但考虑到您只是在验证电子邮件,在我看来这有点不必要。不过,如果您想以最干净的方式进行操作,请坚持使用 accept_nested_attributes_for。

编辑

实际上,您的代码是正确的。你刚刚犯了一个语法错误。未显示客人错误的真正原因是您使用了局部变量而不是实例变量。试试这个:

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

您的错误消息应该与您已有的代码一起显示

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

编辑 2

为了避免在客人电子邮件无效的情况下保存预订实例,您可以执行以下操作:

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

因此,如果客人有任何错误,if语句将在@booking.save语句执行之前终止。

【讨论】:

  • 是的,我考虑过这一点,但由于我是 Rails 新手,我不确定在控制器中处理验证是否是一个很大的“不”。我可能会沿着这条路线走下去,但我之前已经指出了 accept_nested_attributes_for 的方向 - 你可以添加到你的答案中,以了解使用nested_attributes 需要做什么?还是谢谢。
  • 我自己从未使用过accept_nested_attributes_for。但我认为有一种更清洁的方法。我会更新我的答案。
  • 开始了 - 验证时遇到了一个奇怪的问题,但重置了我的数据库数据,并且所有似乎都在进行第一次检查 - 将继续使用它,但为了帮助
  • 所以查看我的控制器代码有没有办法在我继续其余的创建操作之前查看@guest 对象是否“有效” - 否则在没有相关的客人记录,这会导致错误!
  • 已排序!感谢您的帮助。
【解决方案2】:

您可以尝试进行交易。如果其中一个无效,rails 将执行回滚,您可以呈现错误。

关注this question代码,看看是否有帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-13
    • 2012-03-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-29
    相关资源
    最近更新 更多