Rails-从另一个模型中的模型创建实例

时间:2018-11-19 14:34:23

标签: ruby-on-rails ruby-on-rails-5

我对Ruby还是很陌生,想知道如果我每次创建新用户(注册时)都想怎么做,它会自动创建一个Contractor实例,以便新User = Contractor。

我很迷茫,提供一些帮助和解释真的很高兴。

用户控制器

each_pair { |name, val|  }class UsersController < ApplicationController
  before_action :configure_permitted_parameters, if: :devise_controller?
  before_action :update_resource_params, if: :devise_controller?

  def new
    @user = User.new
  end

  def create
    @user = User.new(user.params)
    @contractor = Contractor.create(user: @user)
    if @user.save
      UserMailer.user_alert(@user).deliver_now
      redirect_to @user, notice: 'User was successfully created.'
    else
      render :new
    end
  end

承包商模型

class Contractor < ApplicationRecord
  belongs_to :user
  has_many :construction_projects, dependent: :destroy
  has_many :clients, dependent: :destroy
  has_many :documents, through: :construction_projects
  has_many :resources
  mount_uploader :logo, LogoUploader

  # Model validations
  validates :user, presence: true
  validates_associated :construction_projects
  validates_associated :clients
  validates_associated :documents
  validates_associated :resources

用户模型

class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable

  has_one :contractor
  has_many :clients, through: :contractor
  has_many :construction_projects, through: :contractor

  # Model validations
  validates_associated :clients
  validates_associated :construction_projects

  validates :email, presence: true, uniqueness: true, format: { with: URI::MailTo::EMAIL_REGEXP }

  def option_projects
   projects = self.contractor.construction_projects.map{ |cp| [cp.name, cp.id] }
   projects << ["Add a new project", "Add a new project"]
   projects
  end
end

2 个答案:

答案 0 :(得分:1)

您已定义一个用户has_one承包商,这意味着该承包商具有外键(user_id)。您正在这样创建承包商:

@user = User.new(user.params)
@contractor = Contractor.create(user: @user)

Rails需要使用指向新用户的user_id创建承包商行,该行尚未保存。 Rails继续尝试保存新用户(以便获得ID),然后创建承包商。

您不在此处检查任何验证错误,因此有可能创建承包商而不是用户。

看起来,当您第一次创建承包商(用户除外)时,您不需要传递任何属性。所以我将您的代码更改为此:

@user = User.new(user.params)   # should that be user_params?
if @user.save
  # now we have a saved user, so create its associated Contractor
  @user.create_contractor!
  ...

在声明create_contractor!关联时会自动为您定义has_one方法。如果创建承包商时出现错误,则该方法的!版本将引发异常。如果您在这里没有任何处理此类错误的方法(在正常情况下不要指望),那似乎是合理的。

答案 1 :(得分:0)

您需要首先建立从用户到承包商的关联:

class User < ApplicationRecord
  # ...
  has_one :contractor
  # or
  has_many :contractors
end

然后您可以通过从关联建立一个记录来一次插入两个记录:

irb(main):001:0> @user = User.new
=> #<User id: nil, created_at: nil, updated_at: nil>
irb(main):002:0> @user.build_contractor
=> #<Contractor id: nil, user_id: nil, created_at: nil, updated_at: nil>
irb(main):003:0> @user.save
   (0.3ms)  BEGIN
  User Create (0.8ms)  INSERT INTO "users" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id"  [["created_at", "2018-11-19 17:43:37.730785"], ["updated_at", "2018-11-19 17:43:37.730785"]]
  Contractor Create (1.9ms)  INSERT INTO "contractors" ("user_id", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id"  [["user_id", 2], ["created_at", "2018-11-19 17:43:37.734395"], ["updated_at", "2018-11-19 17:43:37.734395"]]
   (1.2ms)  COMMIT

ActiveRecord非常聪明,可以按正确的顺序插入记录,以便将返回的ID用于contractors.user_id

如果关联为has_many,则可以通过在关联上调用.new来执行相同的操作:

@user.contractors.new

请参阅: