使用ActiveRecord一次创建模型和嵌套模型(1:n)

时间:2016-10-03 13:54:22

标签: ruby-on-rails ruby activerecord

我的Rails5应用程序具有组织模型和用户模型(1:n关系)。创建组织的工作流程还应包括组织的第一个用户的创建。我认为这可以通过嵌套模型使用ActiveRecord,但是创建操作失败并显示错误消息"用户组织必须存在"。

class Organization < ApplicationRecord
    has_many :users, dependent: :destroy
    accepts_nested_attributes_for :users
end

class User < ApplicationRecord
    belongs_to :organization
end

class OrganizationsController < ApplicationController
    def new
        @organization = Organization.new
        @organization.users.build
    end

    def create
        @organization = Organization.new(organization_params)
        if @organization.save
            redirect_to @organization
        else
          render 'new'
        end
    end

    def organization_params
        params.require(:organization).permit(:name, users_attributes: [:name, :email, :password, :password_confirmation])
    end
end

在视图中,我使用<%= f.fields_for :users do |user_form| %>帮助器。

这是我身上的错误,还是ActiveRecord完全不支持?在导轨指南中无法找到任何相关信息。毕竟,这应该(理论上)是可能的:首先对组织进行INSERT,然后对用户进行INSERT(顺序很重要,要知道用户外键的组织ID)。

3 个答案:

答案 0 :(得分:1)

https://github.com/rails/rails/issues/18233中所述,Rails5需要进行完整性检查。因为我不喜欢像禁用完整性检查这样的愚蠢的解决方案,所以我遵循了DHH关于上述问题的建议:

  

我喜欢通过常规Ruby对象进行聚合。例如,我们有一个Signup模型,它只是一个编排构建过程的Ruby对象。所以,我该放手一搏!

我编写了一个名为注册的ruby类,它封装了组织和用户模型,并提供了类似ActiveRecord模型的保存/创建界面。此外,通过包含 ActiveModel :: Model ,有用的东西可以免费进入类(属性哈希构造函数等,请参阅http://guides.rubyonrails.org/active_model_basics.html#model)。

# 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

特别感谢@engineersmnky指出了相应的github问题。

答案 1 :(得分:0)

您正在寻找&#34;协会回拨&#34;。将这些参数发送到组织模型后,您可以在该模型中访问它们。如果每次创建组织时都会为其分配一个新用户,您只需在组织模型中执行以下操作:

 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

答案 2 :(得分:0)

&#34;用户组织必须存在&#34;不应该出错。 ActiveRecord是&#34; smart,&#34;因为它应该执行两个.announcement。首先,它将模型保存在INSERT侧,以便它具有has_many,然后它将模型保存在id侧,填充外键值。问题实际上是由5.1.1之前的Rails 5版本中的belongs_to中的错误引起的。请参阅https://github.com/rails/rails/issues/25198Trouble with accepts_nested_attributes_for in Rails 5.0.0.beta3, -api option

解决方案是使用accepts_nested_attributes_for选项,或者更好的是升级到Rails 5.1.1。

您可以通过删除inverse_of:模型中的accepts_nested_attributes_for并在Rails控制台中创建新的Organization模型和新的Organization来证明这是真的。模型,关联它们(例如User)并尝试myorg.users << myuser(例如save)。您会发现它会按预期工作。