Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

When do default values in Postgres get assigned in Rails callback chain?

At what point in the Rails callback chain does PostgreSQL assign any database (not null constraint) default values?

For example, I have an Experiment model that has a before_create callback to set the experiment_type. An experiment has_many Samples. If samples have been created at the time of experiment creation, the experiment is considered the same experiment_type as the samples’ sample_type. Otherwise, it gets assigned the default value of the database.

class Experiment < ApplicationRecord
  before_create :setup_exp_type

  def setup_exp_type
    sample_set = self.samples.first  # An Experiment has_many samples
    self.experiment_type ||= sample_set&.sample_type
  end

The database table has the constraint:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

          Column           |            Type             | Collation | Nullable |                      Default                      | Storage  | Stats target | Description 
---------------------------+-----------------------------+-----------+----------+---------------------------------------------------+----------+--------------+-------------
 id                        | integer                     |           | not null | nextval('experiments_id_seq'::regclass)           | plain    |              | 
...
 experiment_type           | experiment_type             |           | not null | '1'::experiment_type                              | plain    |              | 

The controller is straightforward:

  def create
    @experiment = Experiment.new(experiment_params)
    respond_to do |format|
      if @experiment.save
        format.html { redirect_to @experiment, notice: 'Experiment was successfully created.' }
        format.json { render :show, status: :created, location: @experiment }
      else
        format.html { render :new }
        format.json { render json: @experiment.errors, status: :unprocessable_entity }
      end
    end
  end

Assuming samples have been created prior to the experiment creation and assigned to the experiment, at the point the setup_exp_type callback gets called, I would assume the database default values have not been assigned yet since the record is still only in local memory. However, in testing, I am seeing self.experiment_type = 1 when debugging the second line of setup_exp_type. There are no other callbacks prior to this, so it is not being assigned anywhere else in the source code.

>Solution :

The default values are set when you call new on an object, you can see in the source code of the method here,

# File activerecord/lib/active_record/base.rb, line 1543
      def initialize(attributes = nil, options = {})
        @attributes = attributes_from_column_definition
        @association_cache = {}
        @aggregation_cache = {}
        @attributes_cache = {}
        @new_record = true
        @readonly = false
        @destroyed = false
        @marked_for_destruction = false
        @previously_changed = {}
        @changed_attributes = {}
        @relation = nil

        ensure_proper_type
        set_serialized_attributes

        populate_with_current_scope_attributes

        assign_attributes(attributes, options) if attributes

        yield self if block_given?
        run_callbacks :initialize
      end

In the first line attributes_from_column_definition is called which you can check here

def attributes_from_column_definition
        self.class.columns.inject({}) do |attributes, column|
          attributes[column.name] = column.default unless column.name == self.class.primary_key
          attributes
        end
      end

As you can see in second line it is calling the column default which sets the default value of an object when it is initialised.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading