Ruby on rails 使用ActiveRecord同时创建模型和嵌套模型(1:n)

Ruby on rails 使用ActiveRecord同时创建模型和嵌套模型(1:n),ruby-on-rails,ruby,activerecord,Ruby On Rails,Ruby,Activerecord,我的Rails5应用程序有一个组织模型和一个用户模型(1:n关系)。创建组织的工作流还应包括创建组织的第一个用户。我认为这可以通过嵌套模型使用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