【问题标题】:Rails 5 find_or_create still creating duplicatesRails 5 find_or_create 仍在创建重复项
【发布时间】:2018-03-05 20:12:05
【问题描述】:

我有一个“杜松子酒”模型,它允许“酿酒厂”模型中的“名称”嵌套属性。嵌套形式运行良好,但我意识到我正在创建“重复”的酿酒厂条目。 'find_or_create_by' 似乎应该防止这种情况发生,但我无法让它不创建副本。

由于这是一个嵌套表单,我也不确定应该放置哪个控制器。

这就是我目前所拥有的,如前所述,这会继续为酿酒厂创造多条记录。我没有任何错误。

gins_controller.rb

class GinsController < ApplicationController

...
  def new
    @gin = Gin.new
    @gin.build_distillery
  end

distillery_controller.rb

class DistilleriesController < ApplicationController
 ...
  def new
    @distillery = Distillery.find_or_create_by(name: 'name')
  end

gins_form

<%= form.fields_for :distillery do |distillery_form| %>
  <p>
    <%= distillery_form.label :distillery, class: "block text-grey-darker text-sm font-bold mb-2" %>
    <%= distillery_form.text_field :name, class:"lg:w-2/5 w-full shadow appearance-none border rounded py-2 px-3 mb-6 text-grey-darker" %>
  </p>
<% end %>

更新

已将其移至 gins_controller 但无法解决:

  def new
    @gin = Gin.new
    @gin.build_distillery
    @distillery = Distillery.find_or_create_by(name: 'name')
  end

【问题讨论】:

  • 可以添加 gins_form 吗?
  • 杜松子酒表格添加@Tobias
  • 如果您将酒厂作为Gin 的一部分提交,那么您需要在GinsController 中填写find_or_create_by,而不是DistilleryController。此外,在DistilleryController 中,您不应该在new 操作中创建酿酒厂,这就是new 呈现的形式。
  • 谢谢,@dinjas。我已将其移至 gins 控制器中,但不幸的是,我仍在创建重复项。
  • @SimonCooper 您的 GinsController 中的 create 操作是什么样的?

标签: ruby-on-rails nested-forms


【解决方案1】:

new 方法用于创建要在视图中显示的新对象(输入数据以在数据库中创建记录的表单)。但是new 方法不会将对象保存到数据库中。所以在new 方法中使用 find_or_create 是没有意义的。只需使用 newbuild

您应该在 create 方法中使用find_or_create,其中记录实际上是创建并保存的。

另一种方法是将酒厂名称定义为唯一字段,因此不能创建重复的酒厂。

第三个选项是您使用选择字段在表单中选择酿酒厂(这要求在创建杜松子酒之前创建酿酒厂)。一般来说,这应该是最好的方法:如果 Gin belongs_to :distillery (我假设),最好选择酒厂而不是在字段中输入名称。如果你打错了,你将创建一个新的酿酒厂,而不是使用现有的。

【讨论】:

    【解决方案2】:

    我同意巴勃罗的观点。 这里有一个关于如何实现新/创建方法的想法,如果你让大部分代码保持不变

     def new
        @gin = Gin.new
        @gin.build_distillery
     end
     def create
        @gin = Gin.new(gin_params)
        @gin.distillery = Distillery.find_or_create_by(name: distillery_name)
        @gin.save! # oversimplification - you probably have respond blocks
     end
    
     def distillery_name
       params[:distillery][:name] # or however it really is
     end
    

    【讨论】:

      【解决方案3】:

      由于您没有指定这一点,我假设您的关联如下所示:

      class Gin
        belongs_to :distillery 
      
      class Distillery
        has_many :gins 
      

      现在,通常情况下,您只需从下拉列表中选择一家酿酒厂即可。所以我们直接设置链接(distillery_id),所以模型Gin中不需要accepts_nested_attributes_for

      如果您使用的是简单形式的 gem,这很简单

       f.association :distillery 
      

      或者使用普通形式,你可以写类似

      f.collection_select :distillery_id, Distillery.all, :id, :name, {include_blank: true}
      

      到目前为止,您不必在控制器端做任何特殊的事情:保存表单时,它会设置 distillery-id,这将创建链接。但它也假设酿酒厂已经存在(在大多数情况下都会如此)。

      当您希望能够链接到现有的酿酒厂以一种形式创建新的酿酒厂时,它会变得有点复杂。

      一个非常简单的解决方案是按如下方式编写表单:

      = f.collection_select :distillery_id, Distillery.all, :id, :name, {include_blank: true}
      = text_field_tag :distillery_name  
      

      (为了获得最佳的用户体验,您可以添加一些 js 点缀,以确保用户可以从下拉列表中选择某些内容或填写名称)

      然后在你的控制器中你可以做类似的事情

      def create
        if params[:gin][:distillery_id].blank? && params[:distillery_name].present?
          @distillery = Distillery.find_or_create_by(name: params[:distillery_name])
          params[:gin][:distillery_id] = @distillery.id 
        end 
        gin = Gin.build(gin_params) 
        [... the rest of your create method as before ...]
      

      简而言之:

      • 我们检查用户是否没有从下拉列表中选择了一个酿酒厂(如果是,就用它代替)并且一个新的名字已经给出李>
      • 使用给定的名称,尝试找到酒厂或创建一个新的
      • 在表格中填写酒厂 ID
      • 然后像以前一样保存表单数据

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-07-19
        • 2021-10-05
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多