Ruby on rails Rails 5查找或创建仍在创建重复项

Ruby on rails Rails 5查找或创建仍在创建重复项,ruby-on-rails,nested-forms,Ruby On Rails,Nested Forms,我有一个“gin”模型,它允许在“蒸馏厂”模型中嵌套“name”的属性。嵌套表单运行良好,但我意识到我正在创建“重复”蒸馏厂条目。看来“查找”或“创建”应该可以防止出现这种情况,但我不能让它不创建副本。 由于这是一个嵌套表单,我也不确定应该放置哪个控制器 这就是我到目前为止所做的,正如前面提到的,这将继续为一家酿酒厂创造多个记录。我没有任何错误 gins\u controller.rb class GinsController < ApplicationController ...

我有一个“gin”模型,它允许在“蒸馏厂”模型中嵌套“name”的属性。嵌套表单运行良好,但我意识到我正在创建“重复”蒸馏厂条目。看来“查找”或“创建”应该可以防止出现这种情况,但我不能让它不创建副本。

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

这就是我到目前为止所做的,正如前面提到的,这将继续为一家酿酒厂创造多个记录。我没有任何错误

gins\u controller.rb

class GinsController < ApplicationController

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

new
方法用于创建要在视图中显示的新对象(用于输入数据以在数据库中创建记录的表单)。但是
new
方法不会将对象保存到数据库中。因此,在
new
方法中使用find\u或\u create是没有意义的。只需使用新建构建

您应该在create方法中使用
find\u或\u create
,在该方法中,记录实际上是以保存的方式创建的

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

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

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

 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

由于您没有具体说明这一点,我假设您的关联如下所示:

class Gin
  belongs_to :distillery 

class Distillery
  has_many :gins 
= f.collection_select :distillery_id, Distillery.all, :id, :name, {include_blank: true}
= text_field_tag :distillery_name  
一般来说,在这种情况下,您只需从下拉列表中选择一个酿酒厂。因此,我们将直接设置链接(
distillery\u id
),因此我们不需要在模型
Gin
中为
接受嵌套的属性

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

 f.association :distillery 
或者使用标准形式,你可以写一些

f.collection_select :distillery_id, Distillery.all, :id, :name, {include_blank: true}
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,这将创建链接。但它也假设酿酒厂已经存在(在大多数情况下都是如此)

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

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

class Gin
  belongs_to :distillery 

class Distillery
  has_many :gins 
= f.collection_select :distillery_id, Distillery.all, :id, :name, {include_blank: true}
= text_field_tag :distillery_name  
(为了获得最佳的用户体验,您需要添加一些js Spreads,以确保用户可以从下拉列表中选择某个内容或填写名称)

然后在控制器中,您可以执行以下操作

f.collection_select :distillery_id, Distillery.all, :id, :name, {include_blank: true}
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
  • 然后像以前一样保存表单数据

您可以添加金丝雀窗体吗?金丝雀窗体已添加@Tobias如果您将酿酒厂作为
Gin
的一部分提交,那么您需要在
GinsController
中执行
查找或创建操作,而不是
酒厂控制器
。另外,在
蒸馏器控制器
中,您不应该在
新建
操作中创建蒸馏器,这就是
新建
呈现的形式。谢谢,@dinjas。我已将其移动到gins控制器中,但不幸的是,我仍在创建副本。@SimonOper您的gins控制器中的
create
操作是什么样子的?