如何在 Rails 3.1 中编写简单的嵌套、has_many :through、多对多表单?
我正在构建一个食谱管理器作为第一个 Rails 应用程序。我有一组基于这个非常好的 ERD 的多对多嵌套模型。理想情况下,我想创建一个表单,允许我以单一表单创建菜谱。另外,我希望用户能够分别将成分和步骤写入/粘贴到单个文本字段中。在下面的代码中,有一个虚拟属性可以解析表单中的 cr 分隔列表以尝试执行此操作。
当我编写成分时,我不断收到“只读”has_many 错误。我了解,根据我离线的出色帮助,我的加入设置不正确。
为了简化事情,我想将成分列表分配给第一步或每个步骤。 如何编写代码,以便它手动创建具有虚拟属性的连接模型?
我的四个模型:
recipe.rb
class Recipe < ActiveRecord::Base
has_many :steps, :dependent => :destroy
has_many :stepingreds, :through => :steps
has_many :ingredients, :through => :stepingreds
validates_presence_of :name, :description
attr_writer :step_instructions, :ingredient_names
after_save :assign_steps, :assign_ingredients
def step_instructions
@step_instruction || steps.map(&:instruction).join("\n")
end
def ingredient_names
@ingredient_name || ingredients.map(&:name).join("\n")
end
private
def assign_steps
if @step_instructions
self.steps = @step_instructions.split(/\n+/).map do |instruction|
Step.find_or_create_by_instruction(instruction)
end
end
end
def assign_ingredients
if @ingredient_names
self.ingredients = @ingredient_names.split(/\n+/).map do |name|
Ingredient.find_or_create_by_name(name)
end
end
end
end
step.rb
class Step < ActiveRecord::Base
#attr_accessible :recipe_id, :number, :instructions
belongs_to :recipe
has_many :stepingreds, :class_name => 'Stepingred'
has_many :ingredients, :through => :stepingreds
end
stepingred.rb
class Stepingred < ActiveRecord::Base
belongs_to :ingredient
belongs_to :step, :class_name => 'Step'
end
做什么成分.rb
class Ingredient < ActiveRecord::Base
has_many :stepingreds
has_many :steps, :through => :stepingred
has_many :recipes, :through => :steps
end
这是我的精简形式:
<%= form_for @recipe do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<p>
<%= f.label :description %><br />
<%= f.text_area :description, :rows => 4 %>
</p>
<p>
<%= f.label :ingredient_names, "Ingredients" %><br />
<%= f.text_area :ingredient_names, :rows => 8 %>
</p>
<p>
<%= f.label :step_instructions, "Instructions" %><br />
<%= f.text_area :step_instructions, :rows => 8 %>
</p>
<p><%= f.submit %></p>
<% end %>
我的数据库架构:
ActiveRecord::Schema.define(:version => 20110714095329) do
create_table "ingredients", :force => true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "recipes", :force => true do |t|
t.string "name"
t.text "description"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "stepingreds", :force => true do |t|
t.integer "recipe_id"
t.integer "step_id"
t.integer "ingredient_id"
t.float "amount"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "steps", :force => true do |t|
t.integer "recipe_id"
t.integer "number"
t.text "instruction"
t.datetime "created_at"
t.datetime "updated_at"
end
end
如果您有任何建议或可以推荐我可以模仿此应用程序的另一段示例代码,请告诉我。
I am building a recipe manager as a first rails app. I have a many-to-many, nested set of models based on this quite nice ERD. Ideally, I would like to create a form that allows me to create the recipe in a single form. Also, I would like the user to be able to write/paste the ingredients and steps into a single text field, respectively. In the code below, there is a virtual attribute that parses the cr separated lists in the form to attempt this.
I keep getting a "readonly" has_many through error when I write the ingredients. I understand that, based on excellent help I got offline, my join is not properly setup.
To simplify things, I would like to assign the ingredients list to either the first step or every step. How do I write the code so that it manually creates the join model with the virtual attribute?
My Four Models:
recipe.rb
class Recipe < ActiveRecord::Base
has_many :steps, :dependent => :destroy
has_many :stepingreds, :through => :steps
has_many :ingredients, :through => :stepingreds
validates_presence_of :name, :description
attr_writer :step_instructions, :ingredient_names
after_save :assign_steps, :assign_ingredients
def step_instructions
@step_instruction || steps.map(&:instruction).join("\n")
end
def ingredient_names
@ingredient_name || ingredients.map(&:name).join("\n")
end
private
def assign_steps
if @step_instructions
self.steps = @step_instructions.split(/\n+/).map do |instruction|
Step.find_or_create_by_instruction(instruction)
end
end
end
def assign_ingredients
if @ingredient_names
self.ingredients = @ingredient_names.split(/\n+/).map do |name|
Ingredient.find_or_create_by_name(name)
end
end
end
end
step.rb
class Step < ActiveRecord::Base
#attr_accessible :recipe_id, :number, :instructions
belongs_to :recipe
has_many :stepingreds, :class_name => 'Stepingred'
has_many :ingredients, :through => :stepingreds
end
stepingred.rb
class Stepingred < ActiveRecord::Base
belongs_to :ingredient
belongs_to :step, :class_name => 'Step'
end
ingredient.rb
class Ingredient < ActiveRecord::Base
has_many :stepingreds
has_many :steps, :through => :stepingred
has_many :recipes, :through => :steps
end
And here is my stripped down form:
<%= form_for @recipe do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<p>
<%= f.label :description %><br />
<%= f.text_area :description, :rows => 4 %>
</p>
<p>
<%= f.label :ingredient_names, "Ingredients" %><br />
<%= f.text_area :ingredient_names, :rows => 8 %>
</p>
<p>
<%= f.label :step_instructions, "Instructions" %><br />
<%= f.text_area :step_instructions, :rows => 8 %>
</p>
<p><%= f.submit %></p>
<% end %>
My database schema:
ActiveRecord::Schema.define(:version => 20110714095329) do
create_table "ingredients", :force => true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "recipes", :force => true do |t|
t.string "name"
t.text "description"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "stepingreds", :force => true do |t|
t.integer "recipe_id"
t.integer "step_id"
t.integer "ingredient_id"
t.float "amount"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "steps", :force => true do |t|
t.integer "recipe_id"
t.integer "number"
t.text "instruction"
t.datetime "created_at"
t.datetime "updated_at"
end
end
Please let me know if you have any suggestions or can recommend another piece of sample code I could model this app after.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您需要在
Recipe.rb
中添加accepts_nested_attributes_for :ingredients, :stepingreds, :steps
才能通过单个@recipe
创建关联对象> 对象。http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
You need to add
accepts_nested_attributes_for :ingredients, :stepingreds, :steps
inRecipe.rb
to be able to create the associated objects through a single@recipe
object.http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html