在 Bryan Helmkamp 名为“重构 Fat ActiveRecord 模型的 7 种模式”的优秀博客文章中,他提到了使用Form Objects
抽象出多层表单并停止使用accepts_nested_attributes_for
.
编辑:请参阅下面的解决方案。
我几乎完全复制了他的代码示例,因为我有同样的问题要解决:
class Signup
include Virtus
extend ActiveModel::Naming
include ActiveModel::Conversion
include ActiveModel::Validations
attr_reader :user
attr_reader :account
attribute :name, String
attribute :account_name, String
attribute :email, String
validates :email, presence: true
validates :account_name,
uniqueness: { case_sensitive: false },
length: 3..40,
format: { with: /^([a-z0-9\-]+)$/i }
# Forms are never themselves persisted
def persisted?
false
end
def save
if valid?
persist!
true
else
false
end
end
private
def persist!
@account = Account.create!(name: account_name)
@user = @account.users.create!(name: name, email: email)
end
end
我的代码中的不同之处之一是我需要验证帐户名称(和用户电子邮件)的唯一性。但是,ActiveModel::Validations
没有uniqueness
验证器,因为它应该是ActiveRecord
.
我想有三种方法可以解决这个问题:
- 写我自己的方法来检查这个(感觉多余)
- 包括 ActiveRecord::Validations::UniquenessValidator (试过这个,没有让它工作)
- 或者在数据存储层添加约束
我更喜欢使用最后一个。但后来我一直想知道我将如何实现这一点。
我可以做类似的事情(元编程,我需要修改其他一些领域):
def persist!
@account = Account.create!(name: account_name)
@user = @account.users.create!(name: name, email: email)
rescue ActiveRecord::RecordNotUnique
errors.add(:name, "not unique" )
false
end
但是现在我在课堂上运行了两项检查,首先我使用valid?
,然后我使用rescue
数据存储约束的语句。
有谁知道处理这个问题的好方法?或许为此编写我自己的验证器会更好(但是我会对数据库进行两个查询,理想情况下一个就足够了)。