开发者

Ruby on Rails: How to create associated models on the fly?

开发者 https://www.devze.com 2023-02-01 12:09 出处:网络
I have the following models: class Product < ActiveRecord::Base belongs_to :brand belongs_to :model accepts_nested_attributes_for :brand, :model

I have the following models:

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

I have a problem to create开发者_如何转开发 a new product.

Here is the relevant code in the controller:

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

When user adds a new brand and a new model, params[:product] contains the following:

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

and I got the following error:

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

because model has a foreign key brand_id which is not set. I can't set it because the brand (like the model) is created on the fly when the product is created. I don't want to create the brand before the product, because then I the product has errors, I will need to delete the created brand.

Then I tried to change params[:product] like this:

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

but I end up with this:

unknown attribute: model_attributes

What would be the proper way to handle this ?


1.) You should avoid using Model as a model name (I think you can see why this could lead to errors, although I don't see this as your problem here)

2.) You are referencing in too much of a circular pattern. A Product has a Model, and a Model has a Brand. Why are you having Product belong to a Model AND a Brand?? I suggest the following setup:

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

I'm a little confused by your data structure - which is your underlying problem.

Product < Model <> Brand

When you have the circular reference like you've define above, you can't have NESTED forms because your models are 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


Well, first off, if you wrap your save in a transaction, a failure at any point in the transaction would roll back all writes, so your brand et al wouldn't be affected.

Product.transaction do
  @product.save
end

You could try this:

before_create :save_associated
validates_associated :brand, :model

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

What that'll do is when you create a product record, it'll validate itself, and then it'll validate the attached brand and model. If all goes well, it'll go on to your before_save callback, which will save your associated models, and then your product model will be saved. If any of the three models are invalid, you'll never get to save_associated, and if you're feeling extra paranoid, you can wrap the save in a transaction as indicated above to automatically roll back any changes if any part of the save fails.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号