3

我的系统中有不同种类的用户。一种是设计师:如何在Rails的注册表单中创建Devise User时创建另一个对象?

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的正确方法是什么?

回答

8

在最近的一个项目中,我用form object pattern一步创建了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 

对表单对象(和我的工作来源)的优秀阅读是this CodeClimate blog post on Refactoring

总而言之,我更喜欢这种方法,而非使用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,宝石由thoughtbot。来源不在Github AFAIK上,但可以从宝石中提取。我通过改造来学习很多东西。

+0

我们如何坚持这一到数据库假设我们有一个账号,用户和会员的ActiveRecord模型。您是否介意发布该窗体本身的外观以及该控制器的主动记录模型。谢谢。 – brg 2013-04-23 14:58:34

+0

添加了模型结构。持久化由控制器中调用的@ signup.save处理,它创建所有对象及其关联。 – 2013-04-23 18:28:26

+0

你是一位绅士学者,谢谢。 – brg 2013-04-23 18:59:40

2

如果您不想变更登记控制器,一个方法是使用ActiveRecord的回调

class User < ActiveRecord::Base 
    after_create :create_designer 

    private 

    def create_designer 
     Designer.create(user_id: self.id) 
    end 
end 
+0

回调不起作用,因为我无法访问参数散列 – 2013-03-26 05:01:20

+0

这正是我所需要的!谢谢 – 2016-09-28 06:44:36

相关问题