Как создать другой объект при создании пользователя Devise из его регистрационной формы в Rails?

В моей системе есть разные пользователи. Один вид — это, скажем, конструктор:

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 у меня нет доступа к этому контроллеру регистрации.

Как правильно создать User и Designer при регистрации?


person at.    schedule 25.03.2013    source источник


Ответы (2)


В недавнем проекте я использовал form object шаблон, чтобы создать пользователя 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, драгоценным камнем от thinkbot. Источник не находится на Github AFAIK, но его можно извлечь из драгоценного камня. Я многому научился, переделывая его.

person Thomas Klemm    schedule 25.03.2013
comment
как мы сохраняем это в базе данных, если у нас есть модели ActiveRecord для учетной записи, пользователя и членства. И не могли бы вы опубликовать, как выглядят сами формы и модель ActiveRecord для этих контроллеров. Спасибо. - person brg; 23.04.2013
comment
Добавлена ​​структура модели. Сохранение обрабатывается @signup.save, вызываемым в контроллере, который создает все объекты и их ассоциации. - person Thomas Klemm; 23.04.2013

Если вы не хотите менять контроллер регистрации, один из способов — использовать обратные вызовы ActiveRecord.

class User < ActiveRecord::Base
  after_create :create_designer

  private

  def create_designer
      Designer.create(user_id: self.id) 
  end
end
person wcpaez    schedule 26.03.2013
comment
Обратные вызовы не работают, потому что у меня нет доступа к хешу params - person at.; 26.03.2013