【问题标题】:Rails: Need help understanding a join table, nested forms, associationsRails:需要帮助了解连接表、嵌套表单、关联
【发布时间】:2018-09-27 07:15:23
【问题描述】:

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

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

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

我真的不明白这个嵌套表单是如何工作的。我要为所有Ingredients 创建一个fields_for?如何解除关联和创建?

也许有人可以指出我可以阅读的正确方向。我已经绞尽脑汁很久了,甚至两次开始这个项目。我很沮丧,但我觉得我离理解它很近了。

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

关于这个主题的任何启示都会令人惊叹。谢谢你。

【问题讨论】:

  • 你的问题很广泛。尝试使用has_many through 关联设置您的模型,然后使用cocoon gem 设置您的嵌套表单。如果您已经尝试过这种方法,请更新您的问题,清楚地解释问题。

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


【解决方案1】:

这是一个相当典型的连接表设置:

这里我们有一个名为 ingredients 的规范化表,它用作成分的主记录,recipe_ingredientsrecipe 表连接。

在 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: 命名连接表时,您应该遵循[thing in singular]_[thing in plural] 的方案,因为Rails 从表名中解析类名的方式。例如,使用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 在后台添加配料之前保存配方)并通过一系列 ajax POST 请求将配料添加到/recipies/:recipe_id/ingredients 可能是一个更好的主意。但这本身就是整个教程的主题,很可能在您了解基础知识后重新审视。

见:

【讨论】:

  • 非常感谢您的这篇文章 - 它帮助我极大地理解了联接、命名约定和表单。谢谢你,先生!
【解决方案2】:

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

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

您需要 3 个模型。 Recipe 模型、成分模型和 Recipe_Ingredient 模型。

我猜你已经有了两个模型,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.

【讨论】:

  • 哇,这超级有用。感谢您的写作。现在我开始使用 Cocoon 和简单表单来计算嵌套表单。再次感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多