如何在Rails中的注册表单中创建Devise用户时创建另一个对象?

我的系统中有不同类型的用户。 一种是,比方说,设计师:

class Designer < ActiveRecord::Base attr_accessible :user_id, :portfolio_id, :some_designer_specific_field belongs_to :user belongs_to :portfolio end 

这是在用户注册时立即创建的。 因此,当用户填写sign_up表单时,将创建一个Devise User以及此Designer对象,并将其user_id设置为创建的新User 。 如果我可以访问控制器的代码,这很容易。 但是对于Devise,我无法访问此注册控制器。

注册后创建UserDesigner的正确方法是什么?

在最近的一个项目中,我使用表单对象模式一步创建了Devise用户和公司。 这涉及绕过Devise的RegistrationsController并创建自己的SignupsController。

 # config/routes.rb # Signups get 'signup' => 'signups#new', as: :new_signup post 'signup' => 'signups#create', as: :signups # app/controllers/signups_controller.rb class SignupsController < ApplicationController def new @signup = Signup.new end def create @signup = Signup.new(params[:signup]) if @signup.save sign_in @signup.user redirect_to projects_path, notice: 'You signed up successfully.' else render action: :new end end end 

引用的注册模型被定义为表单对象。

 # app/models/signup.rb # The signup class is a form object class that helps with # creating a user, account and project all in one step and form class Signup # Available in Rails 4 include ActiveModel::Model attr_reader :user attr_reader :account attr_reader :membership attr_accessor :name attr_accessor :company_name attr_accessor :email attr_accessor :password validates :name, :company_name, :email, :password, presence: true def save # Validate signup object return false unless valid? delegate_attributes_for_user delegate_attributes_for_account delegate_errors_for_user unless @user.valid? delegate_errors_for_account unless @account.valid? # Have any errors been added by validating user and account? if !errors.any? persist! true else false end end private def delegate_attributes_for_user @user = User.new do |user| user.name = name user.email = email user.password = password user.password_confirmation = password end end def delegate_attributes_for_account @account = Account.new do |account| account.name = company_name end end def delegate_errors_for_user errors.add(:name, @user.errors[:name].first) if @user.errors[:name].present? errors.add(:email, @user.errors[:email].first) if @user.errors[:email].present? errors.add(:password, @user.errors[:password].first) if @user.errors[:password].present? end def delegate_errors_for_account errors.add(:company_name, @account.errors[:name].first) if @account.errors[:name].present? end def persist! @user.save! @account.save! create_admin_membership end def create_admin_membership @membership = Membership.create! do |membership| membership.user = @user membership.account = @account membership.admin = true end end end 

关于表单对象(以及我的工作源)的精彩读物是关于重构的CodeClimate博客文章 。

总之,我更喜欢这种方法而不是使用accepts_nested_attributes_for ,尽管可能有更多的方法。 如果你找到一个,请告诉我!

===

编辑:添加引用的模型及其关联以便更好地理解。

 class User < ActiveRecord::Base # Memberships and accounts has_many :memberships has_many :accounts, through: :memberships end class Membership < ActiveRecord::Base belongs_to :user belongs_to :account end class Account < ActiveRecord::Base # Memberships and members has_many :memberships, dependent: :destroy has_many :users, through: :memberships has_many :admins, through: :memberships, source: :user, conditions: { 'memberships.admin' => true } has_many :non_admins, through: :memberships, source: :user, conditions: { 'memberships.admin' => false } end 

模型中的这个结构与saucy一起建模,这是一个思想机器人的gem。 源不在Github AFAIK上,但可以从gem中提取它。 我通过重塑它已经学到了很多东西。

如果您不想更改注册控制器,一种方法是使用ActiveRecord回调

 class User < ActiveRecord::Base after_create :create_designer private def create_designer Designer.create(user_id: self.id) end end