Why is before_save not getting called for my ActiveRecord model?

10,030

As you can see in the documentation, before_save happens after validation. In your case, validation will fail and before_save will never be invoked.

Since the goal of your callback is to set your object to a valid state before validation happens, try the before_validation callback.

Share:
10,030
Chad Johnson
Author by

Chad Johnson

Actively developing web applications since 2000. Extensive architectural and developmental experience in both backend and frontend development with focuses on Node.js and React. Specializing in ecommerce with exposure to multiple industries.

Updated on July 06, 2022

Comments

  • Chad Johnson
    Chad Johnson almost 2 years

    Nothing prints out to the console when I use IRB to create a new model instance and save, and I get a "ActiveRecord::StatementInvalid: Mysql::Error: Column 'user_id' cannot be null" error, so I assume before_save is not getting called. I can't figure out why. I've even tried using the before_save filter. Here's my code:

    require 'secure_resource/secure_resource_encryption'
    
    class Database < ActiveRecord::Base
      belongs_to :username_encryption, :class_name => "Encryption", :foreign_key => :username_encryption_id
      belongs_to :password_encryption, :class_name => "Encryption", :foreign_key => :password_encryption_id
    
      # Virtual attribute to retrieve the decrypted username.
      def username
        if self.username_encryption.nil?
          return nil
        end
    
        begin
          return self.username_encryption.encryption
        rescue SecureResourceError
          raise SecureResourceError
        end
      end
    
      # Provides a way to reset the username.
      def username=(username)
        if self.username_encryption.nil?
          self.username_encryption = Encryption.new
          self.username_encryption.encryption = username
        end
      end
    
      # Virtual attribute to retrieve the decrypted password.
      def password
        if password_encryption.nil?
          return nil
        end
    
        begin
          return password_encryption.encryption
        rescue SecureResourceError
          raise SecureResourceError
        end
      end
    
      # Provides a way to reset the password.
      def password=(password)
        if self.password_encryption.nil?
          self.password_encryption = Encryption.new
          self.password_encryption.encryption = password
        end
      end
    
      def before_save
        p 'ZZZZZZZZZZZZZZZ'
        p self.user_id.to_s + ' ZZZZZZ'
        p 'ZZZZZZZZZZZZZZZ'
        self.username_encryption.user_id = self.user_id
        self.username_encryption.save
        self.username_encryption_id = self.username_encryption.id
    
        self.password_encryption.user_id = self.user_id
        self.password_encryption.save
        self.password_encryption_id = self.password_encryption.id
      end
    end
    
  • Chad Johnson
    Chad Johnson almost 13 years
    Ahh, so the MySQL error is happening during validation. For some reason I had assumed it was happening during save. I'll give this a try and see what happens. How can I explicitly tell if validation passes?
  • Chad Johnson
    Chad Johnson almost 13 years
    Thing is, in IRB, when I create a new Database instance, set parameters, and call database.valid?, true is returned. So how could this possibly be a validation issue?
  • user229044
    user229044 almost 13 years
    @Chad Ah, I didn't read closely enough. If MySQL is raising the error, your before_validation and before_save callbacks have fired and Rails is talking to the database. One of your user_id fields is null. Try using logger.info instead of puts/p, which do not write to the console in Rails.
  • Chad Johnson
    Chad Johnson almost 13 years
    Using before_validation worked. Validation was failing because I was creating an encryption record inside the database model in some method (ie. self.username_encryption = Encryption.new in Database.some_method). Then I was trying to set some properties of self.username_encryption in the before_save hook of the database class, and for some reason I couldn't set properties on self.username_encryption, so, when I called database.save, validation for the database object failed.
  • Tilo
    Tilo over 7 years
    I see this happening in Rails 4.2.4 although the validation passes!