Reputation: 16615
I have created a blank rails app (rails new cheese_shop
), with two models and a join table. I am trying to create a cheese shop and specifying which cheeses it contains, at creation time, like this:
cheeses = [
Cheese.create!(name: "Bree"),
Cheese.create!(name: "Kačkavalj"),
]
Shop.create! name: "The Whistling Cheese", cheeses: cheeses
However, I'm getting this error:
SQLite3::ConstraintException: NOT NULL constraint failed: stocks.shop_id: INSERT INTO "stocks" ("cheese_id", "created_at", "updated_at") VALUES (?, ?, ?)
Apparently, the shop ID is not inserted to the stocks
table when I create the shop. Is it possible to fix this, without having to do it in two steps (i.e. without first creating the Shop, and then adding the cheeses?)
class Cheese < ActiveRecord::Base
has_many :shops, through: :stocks
has_many :stocks
end
class Shop < ActiveRecord::Base
has_many :cheeses, through: :stocks
has_many :stocks
end
class Stock < ActiveRecord::Base
belongs_to :shop
belongs_to :cheese
end
class CreateTables < ActiveRecord::Migration
def change
create_table :cheeses do |t|
t.string :name, null: false
t.timestamps null: false
end
create_table :shops do |t|
t.string :name, null: false
t.timestamps null: false
end
create_table :stocks do |t|
t.integer :shop_id, null: false
t.integer :cheese_id, null: false
t.integer :amount
t.float :price
end
end
end
Upvotes: 1
Views: 120
Reputation: 16615
It turns out Rails creates the associations in two steps, first leaving out the Shop ID, then setting the Shop IDs with an UPDATE
, all in one transaction. So The NOT NULL
constraints are causing the problem.
Changing this:
t.integer :shop_id, null: false
t.integer :cheese_id, null: false
…to this:
t.integer :shop_id
t.integer :cheese_id, null: false
…solves the problem, although I'm unhappy with this since now I cannot rely on the database to ensure the integrity of my data.
Upvotes: 0
Reputation: 1447
maybe you should try to use nested attributes:
class Shop < ActiveRecord::Base
has_many :cheeses, through: :stocks
has_many :stocks
accepts_nested_attributes_for :stocks
end
and then you will be able to do something like:
cheese = Cheese.create!(name: "Bree")
params = { attrib: { name: "The Whistling Cheese", stocks_attributes: { cheese_id: cheese.id} } }
Shop.create params[:attrib]
here is doc: http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
Upvotes: 1