Ruby on Rails:如何动态创建关联模型?

时间:2010-12-30 00:26:15

标签: ruby-on-rails ruby-on-rails-3

我有以下型号:

class Product < ActiveRecord::Base
  belongs_to :brand
  belongs_to :model
  accepts_nested_attributes_for :brand, :model
  ...
end

class Brand < ActiveRecord::Base
  has_many :products
  has_many :models
  ...
end

class Model < ActiveRecord::Base
  has_many :products
  belongs_to :brand 
  accepts_nested_attributes_for :brand
  ...
end

创建新产品时遇到问题。

以下是控制器中的相关代码:

class ProductsController < ApplicationController
  ...
  def create
    @product = Product.new(params[:product])
    if @product.save ...     # Here is the error
  end
  ...
end

当用户添加新品牌和新模型时,params[:product]包含以下内容:

"brand_attributes"=>{"name"=>"my_new_brand"}
"model_attributes"=>{"model_no"=>"my_new_model"}

我收到以下错误:

Mysql2::Error: Column 'brand_id' cannot be null: INSERT INTO `models` ...

因为模型具有未设置的外键brand_id。我无法设置它,因为品牌(如模型)是在创建产品时即时创建的。我不想在产品之前创建品牌,因为那时我产品有错误,我需要删除创建的品牌。

然后我尝试像这样更改params[:product]

"brand_attributes"=>{"name"=>"my_new_brand", 
                     "model_attributes"=>{"model_no"=>"my_new_model"}}

但我最终得到了这个:

unknown attribute: model_attributes

处理此问题的正确方法是什么?

2 个答案:

答案 0 :(得分:2)

1。)你应该避免使用Model作为模型名称(我想你可以看到为什么这会导致错误,虽然我不认为这是你的问题)

2。)你引用了太多的圆形图案。产品有模型,模型有品牌。为什么你的产品属于模特和品牌?我建议进行以下设置:

class Product < ActiveRecord::Base
  belongs_to :model
  accepts_nested_attributes_for :model
end

class Brand < ActiveRecord::Base
  has_many :models
end

class Model < ActiveRecord::Base
  has_many :products
  belongs_to :brand 
  accepts_nested_attributes_for :brand
end

我对你的数据结构感到有点困惑 - 这是你的根本问题。

Product < Model <> Brand

当您拥有上面定义的循环引用时,您不能拥有NESTED表单,因为您的模型已嵌套...

# schema
create_table :products do |t|
  t.string :name
  t.references :model
end

create_table :brands do |t|
  t.string :name
end

create_table :models do |t|
  t.string :name
  t.references :brand
end

答案 1 :(得分:1)

嗯,首先,如果您将保存包装在一个事务中,则事务中任何一点的失败都会回滚所有写入,因此您的品牌等不会受到影响。

Product.transaction do
  @product.save
end

你可以试试这个:

before_create :save_associated
validates_associated :brand, :model

def save_associated
  brand.save if brand.new_record?
  model.save if model.new_record?
end

当您创建产品记录时,它将做什么,它将验证自己,然后它将验证附加的品牌和型号。如果一切顺利,它将继续你的before_save回调,这将保存你的相关模型,然后你的产品模型将被保存。如果这三个模型中的任何一个无效,您将永远不会到达save_associated,如果您感觉有额外的偏执,您可以将保存包装在如上所示的交易中,以自动回滚任何更改(如果有任何部分)保存失败。