【问题标题】:How to create multiple records and handle validations with create method in rails?如何使用 rails 中的 create 方法创建多个记录并处理验证?
【发布时间】:2016-05-19 21:20:36
【问题描述】:

我将参数中的一组属性发送到我的控制器,如果在创建记录时出现任何错误,我需要处理验证错误。

因为我需要一次创建多个组。

参数

  Parameters: {"group"=>[{"sort_by"=>"id", "template_ids"=>[182], "name"=>"Csdfwses", "count"=>1}, {"sort_by"=>"id", "template_ids"=>[181], "name"=>"rthydrt", "count"=>1}]}

所以我的控制器的create方法是这样的:

def create
  @groups = Group.create group_params
   if @groups
    render json: { success: true, message: "#{@groups.length} groups created" }
   else
    render_422 @groups, 'Could not save groups.'
   end
end

如果在创建任何记录时出现任何错误,我想处理这种情况,以便在创建后显示错误消息。

使用上述方法,这里无法使用error 方法。如何显示错误信息?

我尝试使用begin-rescue

 def create
  begin
   @groups = Group.create! group_params
    if @groups
     render json: { success: true, message: "#{@groups.length} groups created" }
    else
     render_422 @groups, 'Could not save groups.'
    end
  rescue ActiveRecord::RecordInvalid => invalid
    render json: { success: false, message: "#{invalid.record.errors.messages}" }, status: 500
  end
 end

但如果有的话,我正在寻找更清洁的方法?

【问题讨论】:

    标签: ruby-on-rails ruby ruby-on-rails-4


    【解决方案1】:

    您想将哈希数组传递给model.create 以一次创建多条记录。

    def create
      @groups = Group.create group_params
       if @groups.all? { |group| group.persisted? }
        render json: { success: true, message: "#{@groups.length} groups created" }
       else
        render_422 @groups, 'Could not save groups.'
       end
    end
    

    如果您想显示任何验证错误,那么您需要查看model.errors,或者您可以查看model.errors.full_messages 的一系列错误。

    def create
      @groups = Group.create group_params
       if @groups.all? { |group| group.persisted? }
        render json: { success: true, message: "#{@groups.length} groups created" }
       else
        errors = @groups.select(&:invalid?).map{ |g| g.errors.full_messages }.join("<br/>") 
        render_422 @groups, "Could not save groups. Here are the errors: #{errors}"
       end
    end
    

    您会希望更好地格式化错误,但这是一个简单的示例。

    【讨论】:

    • 这是一个非常模糊的问题,但我认为他希望一次创建多个组记录 - 这只会创建一个记录。
    • 是的,我想一次创建多个组。我尝试使用rescue ActiveRecord::RecordInvalid =&gt; invalid 并显示像这样的消息render json: { success: false, message: "#{invalid.record.errors.messages}" }, status: 500
    • 我回答了这个问题。但是,我以一种非常丑陋的方式做到了,你会想弄清楚你想要错误的格式。可能你会想要跟踪哪个组对象有错误,以便用户更容易修复错误.
    • 祝你的项目好运 :)
    【解决方案2】:

    您通常会使用accepts_nested_attributes - 但这需要某种层次关系:

    class Company < ActiveRecord::Base
      has_many :employees
      accepts_nested_records_for :employees
      validates_associated :employees
    end
    
    class Employee < ActiveRecord::Base
      belongs_to :company
      validates_presence_of :name
    end
    
    c = Company.new(name: 'Acme', employee_attributes: [
      { name: 'Wile E. Coyotee' },
      { name: 'Bugs Bunny' },
      { name: nil } # invalid
    ])
    
    c.valid? # false
    c.save # false
    

    那么,如果没有父模型,您将如何做到这一点?创建一个类似于父关联的模型:

    # A fake parent model for groups.
    # This is not a ActiveRecord model
    # It's is not backed by a database table.
    class GroupCollection
      include ActiveModel::Model
      attr_accessor :groups
    
      def initialize(groups_attributes: [], **kw_args)
        super
      end
    
      # mimics behavior of accepts_nested_attributes
      # takes either an array or a key/val hash:
      # { 1 => {'name' => 'foo'} }
      def groups_attributes=(attrs)
         # normalize key/val hash
         attrs = attrs.values if attrs.is_a?(Hash)
         self.groups = attrs.map {|h| Group.new(h) }
      end
    
      # mimics behavior of validates_associated
      def valid?
        errors.clear
        # alternatively you can aggregate errors for
        # the nested records on the parent object
        if groups.reject { |g| g.valid? }.any?
          errors.add(:groups, 'are invalid.')
        end
        errors.none?
      end
    
      def save
        return false unless valid?
        save_in_transaction
      end
    
      def save!
        raise ActiveRecord::RecordInvalid and return unless valid?
        save_in_transaction
      end
    
      private 
        def save_in_transaction
          # use a transaction so changes are rolled back if a save fails.
          Group.transaction do
            groups.reject { |g| g.save! }.none? 
          end
        end
    end
    

    class GroupsController
    
      def create
        @collection = GroupCollection.new(groups_params)
        @groups = @collection.groups
    
        if @collection.save
          c = @groups.length
          render json: { 
                success: true, # anti-pattern! - rely on the response code instead
                message: "#{c} #{pluralize(c, 'group')} created" 
          }, status: :created
        else
          # you can get the error messages by iterating through @groups
          # and calling `.errors.full_messages` on each
          render_422 @groups, 'Could not save groups.'
        end
      end
    
      def collection_params
        params.permit(groups_attributes: [:name, :foo, :bar])
      end
    end
    

    【讨论】:

    • 你将如何处理唯一性约束?如果它有 uniqueness: true 作为公司名称,那么它将在 GroupCollection#save! 上引发异常
    • 这就是为什么您在正常的控制器流程中使用.save 而不是.save!.save! 应该只在您从 shell 调用它并需要直接反馈时才真正使用,或者当您在事务中包装多个保存并需要它在记录无效时回滚时使用。
    • 您可以使用validates_associated 让父级验证每个嵌套记录。
    • 感谢您的评论。为什么 *args**kw_argsinitialize 方法中。另外,为什么superinitialize 中?
    • 没错,虽然*args 在这里并不真正需要,因为初始化程序只需要一个哈希值。 **kw_args 啜饮传递给类的其余散列参数。它是一种较新的 Ruby 2.0 语法,与 initialize(kw_args = { groups_attributes: [] }) 的作用大致相同
    猜你喜欢
    • 2014-11-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-09
    • 1970-01-01
    • 2016-04-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多