【问题标题】:Use nested attributes for model without auto-incrementing ID对没有自动递增 ID 的模型使用嵌套属性
【发布时间】:2017-11-03 16:45:33
【问题描述】:

我有以下模型及其相应的架构定义:

class Cube < ApplicationRecord
  has_many :faces, inverse_of: :cube, dependent: :destroy
  accepts_nested_attributes_for :faces
end

create_table "cubes", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
  t.datetime "created_at", null: false
  t.datetime "updated_at", null: false
end


##
# NOTE: Even though this model's related database table has an +id+ column, it
#       is not a regular auto-generated, auto-incremented ID, but rather an +id+
#       column that refers to an uuid that must be manually entered for each
#       Face that is created.
##
class Face < ApplicationRecord
  belongs_to :cube, inverse_of: :faces, optional: true
  validates :id, presence: true, uniqueness: true
end

# Note that id defaults to nil.
create_table "faces", id: :uuid, default: nil, force: :cascade do |t|
  t.datetime "created_at", null: false
  t.datetime "updated_at", null: false
  t.uuid "cube_id"
  t.index ["cube_id"], name: "index_faces_on_cube_id"
end

从上面的 cmets 可以看出,id 不是为 Face 模型自动生成的,而是在创建它们时预计会输入(因为我们正在建模现实世界中已经有的东西)唯一标识符,我们希望使用它而不是添加另一个 ID)。

问题出现在我尝试执行以下操作时:

cube = Cube.new

cube.faces_attributes = [
  {
    id: "2bfc830b-fd42-43b9-a68e-b1d98e4c99e8"
  }
]

# Throws the following error
# ActiveRecord::RecordNotFound: Couldn't find Face with ID=2bfc830b-fd42-43b9-a68e-b1d98e4c99e8 for Cube with ID=

对我来说,这意味着由于我们传递了一个id,Rails 期望这是对关联的Face 的更新,而不是传递一个新的Face 以创建并与cube 关联。

有没有办法让我通过accepts_nested_attributes_for 禁用此默认行为,还是我必须为我的特定用例编写自定义代码?

【问题讨论】:

  • 我想如果你想在控制器动作中创建一个关联的对象,你必须用new关键字创建它。喜欢 - face = Face.newcube.face = face,或者类似的东西
  • @PlanB 是的,看起来它可以工作。我只是“出于好奇”询问嵌套属性的“约定优于配置”是否可以针对这种特定情况弯曲。感谢贡献
  • 我认为你最好留在Convention over configuration。并保留原始记录ID,我认为这不是问题。同一对象可以有两个 id,因为原始 id 从 0(或 1)开始,所以它不是 heavy。最重要的是 - 专注于严重的问题(虽然这不是):)
  • 我的意思是,这看起来不是一个严重的问题,因为您不会丢失任何数据

标签: ruby-on-rails postgresql ruby-on-rails-5 accepts-nested-attributes


【解决方案1】:

您需要将模型配置为使用非标准外键。

class Face < ApplicationRecord
  self.primary_key = 'uuid'
  belongs_to :cube, inverse_of: :faces, optional: true
  # your validation is for the wrong column.
  validates :uuid, presence: true, uniqueness: true
end

但是,在任何体面的数据库设计中,主键都是唯一的。这就是唯一标识符背后的全部意义!由于 NULL 不是唯一的,它也必须是 NOT NULL。这应该在数据库级别强制执行。不仅仅是应用级别的验证。

然而,一个更好的解决方案可能是坚持使用 Rails 命名外键 id 并将其类型更改为 UUID 而不是自动递增 id 的约定。这将大大减少每个模型所需的配置量。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-02-20
    • 2022-11-15
    • 2019-08-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-14
    • 1970-01-01
    相关资源
    最近更新 更多