Undocumented behaviour of #save!

It seems passing validate: false to ActiveRecord::Base#save! bypasses validation, however doc states that:

With save! validations always run.

https://github.com/rails/rails/blob/5d1402a1011f58b405e42007d3ceed4e122d273e/activerecord/lib/active_record/persistence.rb#L130

Isn’t it confusing? Perhaps it’s worth triggering some check for this case?

I am using Rails v4.2.5.2, but it could also apply to newest versions.

Hi Artur!

Thanks! Good catch! This is indeed confusing. I think the intention of save! is to throw an exception if validation fails, while save “only” returns false if it can’t persist the record.

I would take one of the following actions to fix this:

  1. Update the docs of save! so they aren’t confusing anymore or

  2. Make save! run always validations, although validate: false is provided.

I would like someone of the core team tell use which direction they want to take with this.

Hi,

Hi nanaya,

with Rails 5.1.4 I can reproduce it:

Having the schema:

create_table “posts”, force: :cascade do |t|

t.string “title”

t.string “content”

t.string “meta_description”

t.datetime “created_at”, null: false

t.datetime “updated_at”, null: false

t.string “slug”

t.boolean “public”

t.string “image”

t.string “image_teaser”

t.string “youtube_id”

t.string “author”, default: “María García”

t.index [“created_at”], name: “index_posts_on_created_at”

t.index [“slug”], name: “index_posts_on_slug”, unique: true

end

And a model with this validations:

class Post < ApplicationRecord

validates :title, presence: true, length: { maximum: 100 }

validates :content, presence: true

validates :meta_description, presence: true, length: { maximum: 155 }

end

I execute:

irb(main):001:0> p = Post.new

=> #<Post id: nil, title: nil, content: nil, meta_description: nil, created_at: nil, updated_at: nil, slug: nil, public: nil, image: nil, image_teaser: nil, youtube_id: nil, author: “Max Mustermann”>

irb(main):002:0> p.save

(0.3ms) BEGIN

(0.4ms) ROLLBACK

=> false

irb(main):003:0> p.save!

(0.4ms) BEGIN

(0.3ms) ROLLBACK

ActiveRecord::RecordInvalid: translation missing: es.activerecord.errors.messages.record_invalid

from (irb):3

irb(main):004:0> p.save! validate: false

(0.3ms) BEGIN

SQL (1.1ms) INSERT INTO “posts” (“created_at”, “updated_at”) VALUES ($1, $2) RETURNING “id” [[“created_at”, “2018-02-11 13:54:05.991094”], [“updated_at”, “2018-02-11 13:54:05.991094”]]

(0.6ms) COMMIT

=> true

irb(main):005:0> p

=> #<Post id: 32, title: nil, content: nil, meta_description: nil, created_at: “2018-02-11 13:54:05”, updated_at: “2018-02-11 13:54:05”, slug: nil, public: nil, image: nil, image_teaser: nil, youtube_id: nil, author: “Max Mustermann”>

May I open an issue?

Best regards,

Alberto Almagro

Hi,

Hi,

Oh, indeed, https://github.com/rails/rails/blob/5d1402a1011f58b405e42007d3ceed4e122d273e/activerecord/lib/active_record/validations.rb#L42 calls the one I mentioned above. @nanaya, Could you point to a commit where it is fixed? I am able to reproduce it in 5.1.4.

https://github.com/rails/rails/issues/31959