踩坑之validates_presence_of

记录下踩过的一个坑:

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

看个简单的例子:

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
### 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

此时,在终端执行:

1
2
3
4
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,执行如下代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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已经创建,后者才是我们需要的校验方式。