【问题标题】:Automatically populating one database table from another. Rails API从另一个数据库表中自动填充一个数据库表。导轨 API
【发布时间】:2025-12-06 12:20:08
【问题描述】:

我们在 Rails API 中使用 Devise,并有一个包含 NAME 和 EMAIL 以及其他常用注册信息的用户表。我们还有一个 Tutor profile 表,它与用户是 one_to_one 关系。我们想要的是,当新用户注册时,来自个人用户记录的 NAME 和 EMAIL 会自动填充他们的 Tutor 个人资料表作为默认值。这是我们的导轨模型。任何帮助将不胜感激。谢谢!! :-)

class Tutor < ApplicationRecord
  # delegate :email, :email=, to: :user
  after_initialize :set_defaults

  def set_defaults
    self.image_url ||= "https://cdn.icon-icons.com/icons2/1378/PNG/512/avatardefault_92824.png"
    self.bio ||= ""
  end

  belongs_to :user
end


class User < ApplicationRecord

  has_one :tutor#, dependent: :destory
  after_create :init_tutor
  # accepts_nested_attributes_for :tutor
  # delegate :email, :email=, to: :tutor

  def init_tutor
    self.create_tutor
  end

  def generate_jwt
    JWT.encode({ id: id,
                exp: 60.days.from_now.to_i },
               Rails.application.secrets.secret_key_base)
  end
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable

        # validates :fullname uniqueness: { case_sensitive: false }, presence: true  #, allow_blank: false, format: { with: /\A[a-zA-Z0-9]+\z/ }
end

【问题讨论】:

  • 您似乎在 Tutor 模型中尝试了 delegate :email, :email=, to: :user。这是正确的做法。它没有工作吗?尝试将belongs_to :user 放在委托声明之前。
  • 当没有设置特定的导师姓名和电子邮件时,您想使用用户的姓名和电子邮件作为后备吗?还是应该两个值始终相同?

标签: ruby-on-rails ruby database api


【解决方案1】:

注意:具有相同字段和相同数据的请求通常被认为是糟糕的数据库设计。您可以相当容易地从导师对象中查找用户的姓名和电子邮件地址(例如,委派方法)并在进行查询时使用连接。如果这适用于您的应用程序,那么它比复制数据要好得多,如下所示。

您可以使用以下内容创建与用户同名和电子邮件的导师:

class User < ApplicationRecord

  has_one :tutor, dependent: :destroy
  after_create :init_tutor

  def init_tutor
    create_tutor(email: email, name: name)
  end

但是,我通常建议不要在活动记录中使用 beforeafter 挂钩,这些挂钩会对该记录之外产生任何影响(例如,创建不同的记录、发送电子邮件等)。在大型应用程序中做过这样的事情后,我的经验是,随着时间的推移,这条路会导致疯狂。这意味着创建或更新对象可能会创建或更新其他对象,这些对象可能会创建或更新其他对象或具有其他副作用,因此很难理解或预测保存记录可能会做什么。

相反,我建议使用ServiceObject 模式(或类似模式)。这看起来像:

class UserCreator
  def call(user_params)
    user = User.create(params)
    user.create_tutor(params.slice(:name, :email))
    user
  end
end

随着您的应用程序的增长,这种模式会变得更好,您需要一个地方来做一些事情,比如发送电子邮件、排队后台作业、更新用户数等等。

【讨论】:

  • 按照 OP 的要求,将记录从一个表复制到另一个表中,这是一个非常糟糕的主意。重复信息是糟糕的数据库设计实践。此响应中演示的方法有很多方法可以使表变得不同步。请不要这样做!
  • 完全同意跨表复制数据是一种不好的做法,建议解决方案,因为这就是所要求的。将更新解决方案并建议不要这样做。