Ruby on rails 使用ActiveRecord同时创建模型和嵌套模型(1:n)
我的Rails5应用程序有一个组织模型和一个用户模型(1:n关系)。创建组织的工作流还应包括创建组织的第一个用户。我认为这可以通过嵌套模型使用ActiveRecord,但是创建操作失败,错误消息为“用户组织必须存在”Ruby on rails 使用ActiveRecord同时创建模型和嵌套模型(1:n),ruby-on-rails,ruby,activerecord,Ruby On Rails,Ruby,Activerecord,我的Rails5应用程序有一个组织模型和一个用户模型(1:n关系)。创建组织的工作流还应包括创建组织的第一个用户。我认为这可以通过嵌套模型使用ActiveRecord,但是创建操作失败,错误消息为“用户组织必须存在” 班级组织
班级组织
在视图中,我使用
帮助程序
这是我这边的一个bug,还是ActiveRecord根本不支持这一点?在rails指南中找不到关于它的任何信息。毕竟,这(理论上)应该是可能的:首先为组织插入,然后插入用户(顺序很重要,要知道用户外键的组织id)。您正在寻找“关联回调”。一旦您将这些参数发送到您的组织模型,您就可以在该模型中访问它们。如果每次创建一个组织时都会有一个新用户分配给它,那么您可以在组织模型中执行以下操作:
has_many :users, dependent: :destroy, after_add: :create_orgs_first_user
attr_accessor: :username #create virtual atts for all the user params and then assign them as if they were organizational attributes in the controller. This means changing your `organization_params` method to not nest user attributes inside the array `users_attributes`
def create_orgs_first_user
User.create(name: self.username, organization_id: self.id, etc.) # You can probably do self.users.create(params here) but I didn't try it that way.
end
如中所述,Rails5需要进行完整性检查。因为我不喜欢像禁用完整性检查这样的软弱无力的解决方案,所以我遵循了DHH在上面链接的问题中的建议:
我喜欢通过常规Ruby对象进行聚合。例如,我们有一个注册模型,它只是一个编排构建过程的Ruby对象。所以我要试一试
我编写了一个名为Signup的ruby类,它封装了组织和用户模型,并提供了与ActiveRecord模型类似的保存/创建界面。此外,通过包含ActiveModel::Model,类中免费提供了有用的内容(属性哈希构造函数等,请参阅)
特别感谢@engineersmnky为我指出了相应的github问题。不应出现“用户组织必须存在”错误。ActiveRecord是“智能”的,因为它应该执行两个INSERT
s。首先,它将在has\u many
侧保存模型,这样它就有了id
,然后它将在所属
侧保存模型,填充外键值。这个问题实际上是由5.1.1之前的Rails 5版本中的接受
的嵌套属性中的错误引起的。见和
解决方案是使用的逆选项,或者更好地升级到Rails 5.1.1
您可以通过删除
组织
模型中的接受嵌套的
属性,并在Rails控制台中创建一个新的组织
模型和一个新的用户
模型来证明这一点,并将它们关联起来(例如myorg.users您是否记得为:用户放置“接受嵌套的
属性”在您的组织模型文件中?发生这种情况的原因是Rails 5在默认情况下自然强制引用完整性。这意味着组织必须在用户之前存在。接受嵌套的属性。\u似乎没有实现这一概念。。另一个选项是创建一个新类来保存组织然后从这个组织中“构建”一个用户(或者在你的控制器中这样做,尽管我不是这方面的支持者)尝试执行属于\u to:organization,可选:true
。这将使其具有与Rails 4中相同的行为。这不是很好,但它可以防止在组织持久化之前对用户进行状态验证时出现此问题。@engineersmnky谢谢,这有助于我理解此问题。我将发布关于如何解决此问题的答案问题是,这可能是我所采用的解决方案的重复。在rails基础设施中使用PORO的开发人员太少了。
has_many :users, dependent: :destroy, after_add: :create_orgs_first_user
attr_accessor: :username #create virtual atts for all the user params and then assign them as if they were organizational attributes in the controller. This means changing your `organization_params` method to not nest user attributes inside the array `users_attributes`
def create_orgs_first_user
User.create(name: self.username, organization_id: self.id, etc.) # You can probably do self.users.create(params here) but I didn't try it that way.
end
# The Signup model encapsulates an organization and a user model.
# It's used in the signup process and helps persisting a new organization
# and a referenced user (the owner of the organization).
class Signup
include ActiveModel::Model
attr_accessor :organization_name, :user_name, :user_email, :user_password, :user_password_confirmation
# A save method that acts like ActiveRecord's save method.
def save
@organization = build_organization
return false unless @organization.save
@user = build_user
@user.save
end
# Checks validity of the model.
def valid?
@organization = build_organization
@user = build_user
@organization.valid? and @user.valid?
end
# A create method that acts like ActiveRecord's create method.
# This builds the object from an attributes hash and saves it.
def self.create(attributes = {})
signup = Signup.new(attributes)
signup.save
end
private
# Build an organization object from the attributes.
def build_organization
@organization = Organization.new(name: @organization_name)
end
# Build a user object from the attributes. For integritiy reasons,
# a organization object must already exist.
def build_user
@user = User.new(name: @user_name, email: @user_email, password: @user_password, password_confirmation: @user_password_confirmation, organization: @organization)
end
end