验证在创建和更新时表现不同的关联对象

Posted

技术标签:

【中文标题】验证在创建和更新时表现不同的关联对象【英文标题】:Validation on associated object behaving differently on create and update 【发布时间】:2016-11-29 07:11:56 【问题描述】:

我对检查关联对象数量的自定义验证有疑问。有两个类,EventGuest

当用户创建新活动时,他可以选择一些要邀请的客人。保存后,我想检查一下受邀客人的数量。

更新现有事件时验证工作正常。但是在创建新事件时,验证失败,因为self.guests.count 返回0。 在控制器中,我检查了我选择的客人是否以相同的方式传递给模型:params[:event][:guest_ids]

在更新的情况下,我可以看到在调用验证块之前将更新关联表的SQL INSERT INTO 添加到事务队列中。但在新事件的情况下,SQL INSERT 在验证失败之前不会出现。

我在这里缺少什么?我正在考虑一些解决方法,但我确信有适当的方法来实现它。

Rails 版本是 4.2.6

class Event < ActiveRecord::Base
  has_and_belongs_to_many :guests
  validate :check_guestlist

  def check_guestlist
    guest_count=self.guests.count
    if guest_count < 3
      errors.add(“Please invite more guests”)
    end
  end

class Guest < ActiveRecord::Base
  has_and_belongs_to_many :events
end


class EventsController < ApplicationController
  def create
    @event = current_user.events.new(event_params)

    respond_to do |format|
      if @event.save
        format.html  redirect_to @event, notice: 'Event was successfully created.' 
      else
        flash.now[:alert]='Event not saved.'
        format.html  render action: "new" 
      end
    end
  end

  def update
    @event = current_user.events.find(params[:id])

    respond_to do |format|
      if @event.update_attributes(event_params)
        format.html  redirect_to @event, notice: 'Event was successfully updated.' 
      else
        flash.now[:alert]='Changes not saved.'
        format.html  render action: "edit" 
      end
    end
  end
end

【问题讨论】:

在 Event 模型中为客人添加 accept_nested_attributes 并尝试。如果错误仍然存​​在,请添加您的控制器方法和表单。 谢谢!我尝试添加 accepts_nested_attributes_for :guests 但这没有帮助。控制器编码添加到原始问题。 【参考方案1】:

我设法通过替换自定义验证中的一行代码来解决此问题。 如果我更换

    guest_count=self.guests.count

    guest_count=self.guests.to_a.count

我得到了预期的结果。 在更新的情况下,两个表达式返回相同的结果。但是在新记录的上下文中,只有第二个按预期响应。 尽管如此,我仍然不明白为什么第一个版本不起作用。

【讨论】:

【参考方案2】:

好吧 self.guests.count 在 create 上不起作用的原因是这实际上是在进行数据库查询,具有事件的范围。

SELECT COUNT(*) FROMguestsWHEREguests.event_id= 1

现在这当然会失败,因为验证发生在客人被保存之前。现在更新它可以工作,因为在数据库中可以找到条目。然而,验证检查的是错误信息 - 数据库中的客人数量不一定与 self.guests 的数量相匹配 - 您可以再添加一位客人,但尚未保存。

您的解决方案确实有效,但我个人认为最好将其写为:

validates :guests, length:  minimum: 3, message: 'Please invite more guests' 

length 将测试来宾数组的大小 - 例如self.guests.length 实际上与 self.guests.to_a.count 相同

【讨论】:

嗨,大卫,非常感谢您的意见。但我不同意你的说法,即在更新案例中它“正在检查错误的信息”。事实并非如此,这引起了我的头痛。在更新案例中,我看到数据库更新在验证之前被放入事务队列,因此验证成功。我用真实的应用程序对此进行了测试,它在更新案例中运行良好! 试一试...在 Rails 控制台中创建一个有 3 位客人的事件,保存它。检查event.guests.count ...它将如预期的那样为3 ....向事件添加另一个客人...现在调用event.guests.count ..您将看到sql查询运行并且结果仍然是3(即使您加了一个,应该是4) 我做了一个我得到了正确的结果(在你的例子中 = 4),这是有道理的,因为在进行验证之前事务队列中有一个 INSERT INTO 语句。我很困惑...尤其是因为我实施的解决方法有效...

以上是关于验证在创建和更新时表现不同的关联对象的主要内容,如果未能解决你的问题,请参考以下文章

验证关联对象(惰性验证)

定递归验证关联的对象可以用于继承对象吗 spring validate

Rails 4 在保存时创建关联对象

看懂UML视图中的各种关系

教程4 - 验证和权限

如何根据 Rails 中的其他对象验证多对多关联?