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

Time:12-08

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:

          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.

CodePudding user response:

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.

  • Related