我有以下模型及其相应的模式定义:
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
正如您从上面的评论中看到的那样,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
禁用此默认行为,还是必须为我的特定用例编写自定义代码?
答案 0 :(得分:0)
您需要将模型配置为使用非标准外键。
doPost(e)
然而,在任何体面的数据库设计中,主键都是唯一的。这就是唯一标识符背后的重点!由于NULL不是唯一的,因此它也必须是NOT NULL。这应该在数据库级别强制执行。不仅仅是应用级别验证。
一个更好的解决方案,但更少的麻烦可能是坚持使用Rails约定命名外键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
并将其类型更改为UUID而不是自动递增ID。这将大大减少每个模型所需的配置量。