如何在具有外键约束的has_one关系上建立一个验证存在的模型



我似乎遇到了某种循环关系,gem文档中的两个解决方案无法解决这种关系。请参阅下面的示例。这是否意味着要采取不同的做法?

有人会争辩说,因为一个对象在没有另一个的情况下不可能真正持久化,所以它们应该只是一个模型。我认为最好将所有关于身份验证的逻辑提取到它的独立模型中,以免让用户膨胀。大多数时候,凭证内容只在创建会话时使用,而用户则一直在使用。

create_table "credentials", force: :cascade do |t|
t.bigint "user_id", null: false
...
t.index ["user_id"], name: "index_credentials_on_user_id"
end
add_foreign_key "credentials", "users"
class Credential < ApplicationRecord
belongs_to :user, inverse_of: :credential
end
class User < ApplicationRecord
has_one :credential, inverse_of: :user
validates :credential, presence: true
end
Fabricator(:user_base, class_name: :user)
Fabricator(:user, from: :user_base) do
credential
end
Fabricator(:credential) do
user(fabricator: :user_base)
end
irb(main):001:0> Fabricate(:user)
TRANSACTION (0.1ms)  BEGIN
TRANSACTION (0.1ms)  ROLLBACK
Traceback (most recent call last):
1: from (irb):1:in `<main>'
ActiveRecord::RecordInvalid (Validation failed: Credential can't be blank)
irb(main):002:0> Fabricate(:credential)
Traceback (most recent call last):
2: from (irb):1:in `<main>'
1: from (irb):2:in `rescue in <main>'
ActiveRecord::RecordInvalid (Validation failed: Credential can't be blank)
irb(main):003:0> Fabricate.build(:user).save
TRANSACTION (0.2ms)  BEGIN
User Create (0.8ms)  INSERT INTO "users" ("email", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id"  [["email", "fake@mail.com"], ["created_at", "2021-05-29 18:19:09.312429"], ["updated_at", "2021-05-29 18:19:09.312429"]]
Credential Create (0.9ms)  INSERT INTO "credentials" ("user_id", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id"  [["user_id", 19], ["created_at", "2021-05-29 18:19:09.319411"], ["updated_at", "2021-05-29 18:19:09.319411"]]
TRANSACTION (41.2ms)  COMMIT
=> true

解决这个问题的方法肯定会奏效。我通常建议人们解决这个问题的方法是覆盖反向关系。在这种情况下,ActiveRecord将做正确的事情。

Fabricator(:user) do
credential { Fabricate.build(:credential, user: nil) }
end
Fabricator(:credential) do
user { Fabricate.build(:user, credential: nil) }
end

具有外键的模型,在本例中为Credential,是需要具有要持久化的user_id值的模型。这意味着在创建凭据之前,需要有一个用户(在内存或数据库中(。这就是为什么使用build对您有效的原因。

如果用户存在于内存中,rails将足够智能,可以在创建凭据之前先创建该用户。在我看来,当你使用带有Fabricate的构建时,它正在初始化一个用户和一个凭据,所以当用户被保存时,它会将凭据与新创建的用户一起保存。

请注意,文档对belongs_to使用此语法,而不是has_one。看来您可能需要参考文档中的回调部分来解决此问题。

最新更新