Postgres 中的默认值何时分配到 Rails 回调链中?
When do default values in Postgres get assigned in Rails callback chain?
PostgreSQL 在 Rails 回调链中的哪个点分配任何数据库(非空约束)默认值?
例如,我有一个 Experiment
模型,它有一个 before_create
回调来设置 experiment_type
。一个experiment
has_manySamples
。如果在创建实验时创建了样本,则该实验被视为与样本 sample_type
相同的 experiment_type
。否则,它会被分配数据库的默认值。
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
数据库table有约束:
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 | |
控制器简单明了:
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
假设在实验创建之前创建了样本并分配给实验,在 setup_exp_type
回调被调用时,我假设数据库默认值尚未分配,因为记录是仍然只在本地内存中。但是,在测试中,我在调试 setup_exp_type
的第二行时看到 self.experiment_type = 1
。在此之前没有其他回调,因此它没有被分配到源代码中的其他任何地方。
当你在一个对象上调用new时设置默认值,你可以在方法的源代码中看到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
第一行调用了attributes_from_column_definition
,你可以查看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
正如您在第二行中看到的那样,它正在调用 default 列,它在初始化对象时设置对象的默认值。
PostgreSQL 在 Rails 回调链中的哪个点分配任何数据库(非空约束)默认值?
例如,我有一个 Experiment
模型,它有一个 before_create
回调来设置 experiment_type
。一个experiment
has_manySamples
。如果在创建实验时创建了样本,则该实验被视为与样本 sample_type
相同的 experiment_type
。否则,它会被分配数据库的默认值。
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
数据库table有约束:
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 | |
控制器简单明了:
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
假设在实验创建之前创建了样本并分配给实验,在 setup_exp_type
回调被调用时,我假设数据库默认值尚未分配,因为记录是仍然只在本地内存中。但是,在测试中,我在调试 setup_exp_type
的第二行时看到 self.experiment_type = 1
。在此之前没有其他回调,因此它没有被分配到源代码中的其他任何地方。
当你在一个对象上调用new时设置默认值,你可以在方法的源代码中看到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
第一行调用了attributes_from_column_definition
,你可以查看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
正如您在第二行中看到的那样,它正在调用 default 列,它在初始化对象时设置对象的默认值。