踩坑之validates_presence_of

记录下踩过的一个坑:

类A与类B存在一对多关系,需要校验在创建b时,a存在,分别使用validates_presence_of :a 与 validates_presence_of :a_id,会发现当a尚未保存时,前者会成功创建b,后者则不行。

看个简单的例子:

group 与post 之间是一对多关系, post含字段group_id。

### app/models/post.rb
class Post < ApplicationRecord
  belongs_to :group, optional: true
  ## 为了方便找出两者的差异,这里使用optional: true,post创建时,group_id可为nil

  validates :content, presence: true
end

#### app/models/group.rb
class Group < ApplicationRecord
  belongs_to :user
  has_many :posts

  validates :title, presence: true
end

此时,在终端执行:

post = Post.new(content: "this is a post")
post.save

# => true

接下来分别validates_presence_of :group_idvalidates_presence_of :group来给post添加有效性校验,要求group必须存在。

  • 在post.rb中添加validates_presence_of :group_id

    进入console,执行如下代码:

    post = Post.new(content: "my post")
    
    post.save
    
    #=> rollback, 返回false, Validation failed: Group can't be blank
    
    group = Group.new(title: "hello", user_id: User.first.id)
    
    post = Post.new(content: "my post")
    
    post.group = group
    
    post.save
    
    #=> rollback, 返回false
    
    post.save!
    
    #=> rollback, 显示 Validation failed: Group can't be blank
    

    第一次创建post,没有给到group,无法新建,正常。

    第二次创建post,使用post.group = group 给post的group赋值, 但是,你会发现虽然post.group有记录,不为nil, 但是post.group_id 依然为nil,保存时,校验group_id,为nil, 报错。

  • 在post.rb中添加validates_presence_of :group

    post = Post.new(content: "my post")
    
    post.save
    
    #=> rollback, 返回false, Validation failed: Group can't be blank
    
    group = Group.new(title: "hello", user_id: User.first.id)
    
    post = Post.new(content: "my post")
    
    post.group = group
    
    post.save
    
    #=> 返回true, 依次在Group,Post中插入了一条新的记录
    

    第一次创建post,没有给到group,无法新建,正常,同validates_presence_of :group_id 的效果一样。

    第二次创建post,使用post.group = group 给post的group赋值,此时,虽然post.group_id 依然为nil,但是由于post.group不为nil,检验通过,group存在,成功创建。

    很显然,当我们创建post时,需要检验group必须存在,并不要求此时group已经创建,后者才是我们需要的校验方式。