什么时候在 Rails 回调链中分配 Postgres 中的默认值?

Posted

技术标签:

【中文标题】什么时候在 Rails 回调链中分配 Postgres 中的默认值?【英文标题】:When do default values in Postgres get assigned in Rails callback chain? 【发布时间】:2022-01-12 14:56:24 【问题描述】:

PostgreSQL 在 Rails 回调链中的哪个点分配任何数据库(非空约束)默认值?

例如,我有一个Experiment 模型,它有一个before_create 回调来设置experiment_type。一个experiment has_many Samples。如果在创建实验时创建了样本,则认为该实验与样本的sample_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

数据库表有约束:

          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。在此之前没有其他回调,因此在源代码中的其他任何地方都没有分配它。

【问题讨论】:

【参考方案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 列,该列在初始化时设置对象的默认值。

【讨论】:

很高兴知道,谢谢 很高兴能帮上忙 提供的两个来源都非常过时(rails 3.1 和 rails 2.3.8)

以上是关于什么时候在 Rails 回调链中分配 Postgres 中的默认值?的主要内容,如果未能解决你的问题,请参考以下文章

在 Moq 中分配 out/ref 参数

Spark在实际项目中分配更多资源

如何在 Boost.ASIO 中分配已连接的本机套接字类型 (TCP)

为啥我们可以直接在ByteBuffer中分配字节而不在FloatBuffer中分配浮点数

如果您在进程崩溃后在进程中分配内存会发生啥?

如何查找变量是在堆栈还是堆中分配?