检查Rails应用程序中多对多关联的唯一性

时间:2015-10-29 00:00:24

标签: ruby-on-rails ruby model-view-controller

我正在尝试创建一个Rails应用程序,允许用户输入配方并将其标记为一种配方。我为" Recipe"创建了一个模型。和#34;标记"给他们一个多对多的联想。 现在,我正在尝试添加一个方法,该方法将检查用户的输入标记名称是否已存在于数据库中。如果是,我想使用该标签,如果没有,我想创建一个新标签。这样我就可以避免重复的标签名称。 (因为最终我希望用户能够按标签搜索食谱,而且我不想要像#34;鸡肉和#34;列出10000次)。

我已经能够成功创建新标签,但是每次保存新标签名称时,检查标签是否在数据库中似乎都不起作用。 / p>

以下是我尝试在我的控制器内完成工作的方法:

tag_controller.rb

def add_tag
  tag_name_string_from_user = params[:tag][:name]
  @tag = Tag.where(name: tag_name_string_from_user)
  if @tag.nil?
    @tag = Tag.new(name: tag_name_string_from_user)
    @tag.save
    @recipe.tags << @tag
    render 'show.html.erb'
  end
end

这是我一直试图为它写的测试。我试图弄清楚用户输入是否传递给@标签 - 我感觉它不是,但我不确定如何测试。

recipe_tag_join_spec.rb

it "should allow you to create a tag" do
  visit "/recipes/new"
  fill_in 'Title', :with =>'myTitle'
  fill_in 'Author', :with =>'myAuthor'
  fill_in 'Ingredients', :with =>'myIngredients'
  fill_in 'Instructions', :with =>'myInstructions'
  click_button 'Create Recipe'
  fill_in "Tag Name", :with => "string"
  expect(find_field("Tag Name").value).to eq "string"
end

4 个答案:

答案 0 :(得分:0)

即使没有返回记录,

.nil?也会返回true。请改用.empty?

但您可能想尝试uniqueness验证。看看here

答案 1 :(得分:0)

  

我已经能够成功创建新标签,但需要检查   标签是否在数据库中似乎不是   工作 - 每次保存新的标签名称。

这不是代码中发生的事情。

present?

根据代码,您不会创建任何新的Tag对象,因为它永远不会传递if语句。没有?将始终评估为 false ,因为where方法将始终返回ActiveRecordRelation对象(即使它是空的)。

另见参考:http://api.rubyonrails.org/classes/ActiveRecord/QueryMethods.html#method-i-where

描述以:

开头
  

返回一个新关系,......

您可以按照建议检查@tag的为空?,或者检查{{1}}(也会检查它是否为nil?)。

答案 2 :(得分:0)

基本问题是,您希望where在某些情况下返回nil,但where 永远不会返回nil。您可以 更改条件,因为其他一些答案建议,但您也可以使用where

对于您正在做的事情,find_by是更好的选择,因为它返回第一个匹配记录(如果没有匹配记录,则返回nil)。 find_by docs here使用find_by会导致代码的其余部分按预期运行。

更进一步,您应该考虑使用find_or_create_by来进一步简化代码。换句话说,您不需要重新创建用于创建不存在的标记的代码,或者如果标记存在则返回标记。 Docs here

无论您最终在Ruby中使用哪种解决方案(例如wherefind_byfind_or_create_byvalid?等),您希望在您的数据库中创建一个唯一索引,以确保您不会获得重复的标记。即使是最好的Ruby代码也不会阻止竞争条件,即在同一时刻创建两次相同的标记。

答案 3 :(得分:0)

要确保标记名称是唯一的,请将此@tag = Tag.new(name: tag_name_string_from_user) if @tag.valid? @tag.save @recipe.tags << @tag else @tag=nil end 添加到标记模型。然后尝试创建一个新标签:

<ul class="items-container">
    <li class="item-wrapper">
        <div class="item">...</div>
    </li>
    <li class="item-wrapper">
        <div class="item">...</div>
    </li>
    <li class="item-wrapper">
        <div class="item">...</div>
    </li>
    <li class="item-wrapper">
        <div class="item">...</div>
    </li>
    <li class="item-wrapper">
        <div class="item">...</div>
    </li>
</ul>

这样做效果更好,因为您可以在验证之前将代码添加到进程名称字符串。