如何从父类创建子模型

时间:2021-01-12 21:34:42

I've been spinning may wheels with Rails on this for a few days. I'm working with three models: IngredientBase, Ingredient, and IngredientList. IngredientBase contains a string that may be "Chicken", "Eggs", or "Bacon." Ingredient will have a single Ingredient and a quantity. IngredientList will have many Ingredients.

这几天我一直在用铁轨旋转五月轮。我正在研究三种模型:配料、配料和配料表。配料库中有一串可能是“鸡肉”、“鸡蛋”或“培根”。配料只有一个配料和一个数量。配料表将包含许多配料。

I want the user to be able to ingredient_lists/new and be able to create several Ingredients before submitting the IngredientList. How could I do this without having the user submit multiple forms?

我希望用户能够列出配料表/新配料,并能够在提交配料表之前创建几个配料。如果不让用户提交多个表单,我怎么做呢?

2 个解决方案

#1


3  

Associative

联想

What you're looking at is nested models.

你看到的是嵌套模型。

Nested models basically allow you to append attributes to an instance of one model; those attributes being sent to an associative model.

嵌套模型基本上允许您将属性附加到一个模型的实例;这些属性被发送到关联模型。

This is achieved with accepts_nested_attributes_for:

这是通过accepts_nested_attributes_for实现的:

#app/models/ingredient_list.rb
class IngrendientList < ActiveRecord::Base
   has_many :ingredients
   accepts_nested_attributes_for :ingredients
end

#app/models/ingredient.rb
class Ingredient < ActiveRecord::Base
   belongs_to :ingredient_list
end

Because your models made me confused, I rewrote the structure for you. I think you're getting confused with join models etc:

因为你们的模型让我很困惑,我为你们重写了结构。我认为你会对加入模型感到困惑,等等:

#app/models/ingredient.rb
class Ingredient < ActiveRecord::Base
   #columns id | name | weight | etc | created_at | updated_at
   has_many :recipe_ingredients
   has_many :recipes, through: :recipe_ingredients
end

#app/models/recipe_ingredient.rb
class RecipeIngredient < ActiveRecord::Base
    #columns id | recipe_id | ingredient_id | quantity | created_at | updated_at
    belongs_to :recipe
    belongs_to :ingredient
    accepts_nested_attributes_for :ingredient
end

#app/models/recipe.rb
class Recipe < ActiveRecord::Base
   #columns id | name | etc | etc | created_at | updated_at
   has_many :recipe_ingredients #-> allows extra attributes
   has_many :ingredients, through: :recipe_ingredients 
   accepts_nested_attributes_for :recipe_ingredients
end

This will give you the ability to create a Recipe, add new Ingredients, and generally keep your model much more robust.

这将使您能够创建一个食谱,添加新的成分,并通常保持您的模型更加健壮。

Here's how you can make it work with controller and views etc:

以下是如何使它与控制器和视图等一起工作的:

#app/controllers/recipes_controller.rb
class RecipesController < ApplicationController
   def new
      @recipe = Recipe.new
   end
   def create
      @recipe = Recipe.new recipe_params
      @recipe.save
   end

   private

   def recipe_params
      params.require(:recipe).permit(:recipe, :params, recipe_ingredients_attributes: [:quantity, ingredient_attributes:[:name]])
   end
end

This will create a basic form, but we need to include the associated fields. There are several ways to do this, including the "manual" method from RailsCasts and using Cocoon.

这将创建一个基本的表单,但是我们需要包含相关的字段。有几种方法可以做到这一点,包括“手工”的方法从铁路列车和使用茧。

The important thing to note is thus:

值得注意的重要事项如下:

Every time you call a nested form field, you're basically getting Rails to add another instance of f.fields_for to your form. The VITAL thing to note here is you need to have the child_index of the fields_for block. This is what Rails uses to identify the fields, and needs to be kept unique.

每次调用嵌套表单字段时,基本上都是让Rails添加另一个f实例。fields_for表单。这里需要注意的一点是,需要使用fields_for块的child_index。这是Rails用来识别字段的方法,并且需要保持惟一性。

You can see more about this on an answer I wrote some time back.

你可以在我以前写的答案中看到更多。

For your form, you need the following:

你需要填写以下表格:

#app/views/recipes/new.html.erb
<%= form_for @recipe do |f| %>
   <%= f.text_field :title %> 
   <%= render "ingredients", locals: {f: f, child_index: Time.now.to_i}  %>
   <%= link_to "Add Ingredient", recipes_add_field_path, class: "ingredient" %>
   <%= f.submit %>
<% end %>

#app/views/recipes/_ingredients.html.erb
<%= f.fields_for :recipe_ingredients, child_index: child_index do |ri| %>
    <%= ri.text_field :quantity %>
    <%= ri.fields_for :ingredient do |ingredient| %>
       <%= ingredient.text_field :name %>
       <%= ingredient.text_field :weight %>
    <% end %>
<% end %>

#config/routes.rb
resources :recipes do
   member "add_field", to: "recipes#add_field"
end

#app/controllers/recipes_controller.rb
class RecipesController < ApplicationController
   def add_field
      @recipe = Recipe.new
      @recipe.recipe_ingredients.build.build_ingredient
      render "new"
   end
end

#app/assets/javascripts/application.js
$(document).on("click", "a.ingredient", function(){
   $.ajax({
      url: '/recipes/add_field',
      success: function(data){
        el_to_add = $(data).html();
        $('#recipes').append(el_to_add);
      }
      error: function(data){
         alert("Sorry, There Was An Error!");
      }
   });
});

#2


0  

Best way to handle nested relations is to use accept_nested_attributes

处理嵌套关系的最佳方法是使用accept_nested_attributes

For better understating here is a link

为了更好地理解,这里有一个链接

And to manage ui level form within form here is a gem

在表单中管理ui级别表单是一个gem。

#1


3  

Associative

联想

What you're looking at is nested models.

你看到的是嵌套模型。

Nested models basically allow you to append attributes to an instance of one model; those attributes being sent to an associative model.

嵌套模型基本上允许您将属性附加到一个模型的实例;这些属性被发送到关联模型。

This is achieved with accepts_nested_attributes_for:

这是通过accepts_nested_attributes_for实现的:

#app/models/ingredient_list.rb
class IngrendientList < ActiveRecord::Base
   has_many :ingredients
   accepts_nested_attributes_for :ingredients
end

#app/models/ingredient.rb
class Ingredient < ActiveRecord::Base
   belongs_to :ingredient_list
end

Because your models made me confused, I rewrote the structure for you. I think you're getting confused with join models etc:

因为你们的模型让我很困惑,我为你们重写了结构。我认为你会对加入模型感到困惑,等等:

#app/models/ingredient.rb
class Ingredient < ActiveRecord::Base
   #columns id | name | weight | etc | created_at | updated_at
   has_many :recipe_ingredients
   has_many :recipes, through: :recipe_ingredients
end

#app/models/recipe_ingredient.rb
class RecipeIngredient < ActiveRecord::Base
    #columns id | recipe_id | ingredient_id | quantity | created_at | updated_at
    belongs_to :recipe
    belongs_to :ingredient
    accepts_nested_attributes_for :ingredient
end

#app/models/recipe.rb
class Recipe < ActiveRecord::Base
   #columns id | name | etc | etc | created_at | updated_at
   has_many :recipe_ingredients #-> allows extra attributes
   has_many :ingredients, through: :recipe_ingredients 
   accepts_nested_attributes_for :recipe_ingredients
end

This will give you the ability to create a Recipe, add new Ingredients, and generally keep your model much more robust.

这将使您能够创建一个食谱,添加新的成分,并通常保持您的模型更加健壮。

Here's how you can make it work with controller and views etc:

以下是如何使它与控制器和视图等一起工作的:

#app/controllers/recipes_controller.rb
class RecipesController < ApplicationController
   def new
      @recipe = Recipe.new
   end
   def create
      @recipe = Recipe.new recipe_params
      @recipe.save
   end

   private

   def recipe_params
      params.require(:recipe).permit(:recipe, :params, recipe_ingredients_attributes: [:quantity, ingredient_attributes:[:name]])
   end
end

This will create a basic form, but we need to include the associated fields. There are several ways to do this, including the "manual" method from RailsCasts and using Cocoon.

这将创建一个基本的表单,但是我们需要包含相关的字段。有几种方法可以做到这一点,包括“手工”的方法从铁路列车和使用茧。

The important thing to note is thus:

值得注意的重要事项如下:

Every time you call a nested form field, you're basically getting Rails to add another instance of f.fields_for to your form. The VITAL thing to note here is you need to have the child_index of the fields_for block. This is what Rails uses to identify the fields, and needs to be kept unique.

每次调用嵌套表单字段时,基本上都是让Rails添加另一个f实例。fields_for表单。这里需要注意的一点是,需要使用fields_for块的child_index。这是Rails用来识别字段的方法,并且需要保持惟一性。

You can see more about this on an answer I wrote some time back.

你可以在我以前写的答案中看到更多。

For your form, you need the following:

你需要填写以下表格:

#app/views/recipes/new.html.erb
<%= form_for @recipe do |f| %>
   <%= f.text_field :title %> 
   <%= render "ingredients", locals: {f: f, child_index: Time.now.to_i}  %>
   <%= link_to "Add Ingredient", recipes_add_field_path, class: "ingredient" %>
   <%= f.submit %>
<% end %>

#app/views/recipes/_ingredients.html.erb
<%= f.fields_for :recipe_ingredients, child_index: child_index do |ri| %>
    <%= ri.text_field :quantity %>
    <%= ri.fields_for :ingredient do |ingredient| %>
       <%= ingredient.text_field :name %>
       <%= ingredient.text_field :weight %>
    <% end %>
<% end %>

#config/routes.rb
resources :recipes do
   member "add_field", to: "recipes#add_field"
end

#app/controllers/recipes_controller.rb
class RecipesController < ApplicationController
   def add_field
      @recipe = Recipe.new
      @recipe.recipe_ingredients.build.build_ingredient
      render "new"
   end
end

#app/assets/javascripts/application.js
$(document).on("click", "a.ingredient", function(){
   $.ajax({
      url: '/recipes/add_field',
      success: function(data){
        el_to_add = $(data).html();
        $('#recipes').append(el_to_add);
      }
      error: function(data){
         alert("Sorry, There Was An Error!");
      }
   });
});

#2


0  

Best way to handle nested relations is to use accept_nested_attributes

处理嵌套关系的最佳方法是使用accept_nested_attributes

For better understating here is a link

为了更好地理解,这里有一个链接

And to manage ui level form within form here is a gem

在表单中管理ui级别表单是一个gem。