Rails:需要帮助来理解联接表,嵌套表单,关联

时间:2018-09-27 07:15:24

标签: ruby-on-rails activerecord nested-forms

我是一名学习Rails的学生,我的项目是制作Recipes应用程序。我的食谱应用程序具有模型RecipeIngredient和其他功能,但重要的是,Ingredient必须是表中该类型的唯一成分。行将只是一个ID和名称。表中只能有一个“大米”,因此任何以大米为原料的Recipe都应使用同一行。这样(我想)可以按Recipe个过滤Ingredient个。

在创建/编辑Recipe时,我必须使用嵌套表单,因此用户可以在一个屏幕上填写食谱信息和配料,包括添加新的Ingredients以及从中选择配料选择的下拉列表。在编辑中,用户还可以从Ingredient中删除(取消关联)和Recipe

我知道我需要一个联接表(recipes_ingredients ??,如果需要的话,RecipesIngredient这个模型叫什么?

我不太了解此嵌套表单如何工作。我要为所有fields_for创建一个Ingredient吗?如何分离和创建?

也许有人可以向我指出正确的方向,以便我可以阅读有关此内容的信息。我已经花了很长时间思考,甚至​​开始了两次该项目。我感到沮丧,但我感觉我已经非常了解它。

我也尝试过使用simple_form和cocoon,但我觉得这让我更加困惑。

任何关于这个问题的见解都将是惊人的。谢谢。

2 个答案:

答案 0 :(得分:1)

这是一个非常典型的联接表设置:

enter image description here

在这里,我们有一个名为ingredients的规范化表,该表用作成分的主记录,还有一个recipe_ingredients,与recipe表联接。

在Rails中,我们将其设置为has_many through:关联:

class Recipe < ApplicationRecord
  has_many :recipe_ingredients
  has_many :ingredients, through: :recipe_ingredients
end

class Ingredient < ApplicationRecord
  has_many :recipe_ingredients
  has_many :recipes, through: :recipe_ingredients
end

class RecipeIngredient < ApplicationRecord
  belongs_to :recipe
  belongs_to :ingredient
end

您要使用has_many through:而不是has_and_belongs_to_many的原因是,由于没有模型,后者是“无头的”。直到您意识到无法访问其他列(例如数量),这似乎是一个好主意。

has_many through:命名联接表时,由于Rails从表名解析类名的方式,您应该遵循[thing in singular]_[thing in plural]的方案。例如,使用recipes_ingredients会导致丢失常量错误,因为Rails会尝试加载Recipes::Ingredient。连接模型本身应命名为SingularSingular

您当然也可以为适合域的名称命名联接表。

要添加嵌套行,请使用nested attributesfields_for :recipe_ingredients

<%= form_for(@recipe) do |f| %>
  <div class="field">
    <%= f.label :name %>
    <%= f.text_field :name %>
  </div>

  <fieldset>
    <legend>Ingredients</legend>
    <%= fields_for :recipe_ingredients do |ri| %>
    <div class="nested_fields">
       <div class="field">
         <%= ri.label :ingredient %>
         <%= ri.collection_select(:ingredient_id, Ingredient.all, :id, :name) %>
       </div>
       <div class="field">
         <%= ri.number_field :quantity %>
       </div>
    </div>
    <% end %>
  </fieldset>
<% end %>

但是,嵌套字段在许多方面都是笨拙的,允许一次创建/修改多个模型。将所有内容融合在一起的UX和应用程序流程并不理想。

要提供良好的用户体验,最好采用增量保存(用户在使用AJAX在后台添加配料之前保存配方),然后通过向/recipies/:recipe_id/ingredients进行一系列ajax POST请求来添加配料。但这是整个教程的主题,在您了解了基础知识之后,很可能会重新讨论它。

请参阅:

答案 1 :(得分:1)

您可以用简单的单词描述多对多关联。

首先,请看指南https://guides.rubyonrails.org/association_basics.html#the-has-many-through-association

您需要3个型号。配方模型,成分模型和配方成分模型。

我猜您已经有两个模型,即Recipe和Ingredient,所以让我们看一下联接表模型。

首先生成一个迁移:

rails generate migration create_recipe_ingredients   

db/migrate/***create_recipe_ingredients.rb处创建连接表

def change
 create_table :recipe_ingredients do |t|
   t.integer :ingredient_id, :recipe_id
 end
end

所以您的模型:

class Recipe < ApplicationRecord
  has_many:recipe_ingredients
  has_many:ingredients, through: :recipe_ingredients 
end

class RecipeIngredient < ApplicationRecord
  belongs_to :recipe
  belongs_to :ingredient
end

class Ingredient < ApplicationRecord
  has_many:recipe_ingredients
  has_many:recipes, through: :recipe_ingredients
end

在控制台上运行并测试关联:

irb:> recipe=Recipe.first
irb:> ingre=Ingredient.first
irb:> ingre.recipes << recipe
irb:> ingre.recipes #Recipes with this ingredient
irb:> recipe.ingredients #The ingredients of this specific recipe.
irb:> Ingredient.all #List all your ingredients.
irb:> Recipe.all #List all your recipes.