我正在尝试创建一个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
答案 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中使用哪种解决方案(例如where
,find_by
,find_or_create_by
,valid?
等),您将希望还在您的数据库中创建一个唯一索引,以确保您不会获得重复的标记。即使是最好的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>
这样做效果更好,因为您可以在验证之前将代码添加到进程名称字符串。