由于 Ruby 和 Rails 中的外键约束,为什么用于为数据库播种的 rake 文件无法工作?
Why will a rake file used to seed a datbase not work due to foreign key constraints in Ruby on Rails?
我已经用 Rails 6 创建了一个虚拟旅行社,我正在尝试使用 rake 文件为船舶、邮轮和客户详细信息等播种数据。
文件:ships.rake
看起来像这样:
namespace :ships do
desc "TODO"
task seed_cabins: :environment do
CreditCard.destroy_all
Address.destroy_all
Customer.destroy_all
Cruise.destroy_all
Ship.destroy_all
p "tables emptied"
5.times do |index|
Ship.create!(name: Faker::Coffee.blend_name, tonnage: Faker::Number.within(range: 10000..100000))
end
p "ships created"
# create cabins for each ship
ships = Ship.all
ships.each do |ship|
5.times do |index|
Cabin.create!(
ship_id: ship.id,
name: "Suite #{index+1}",
beds: Faker::Number.between(from: 1, to: 3),
deck: Faker::Number.between(from: 1, to: 3)
)
end
end
p "Cabins created"
ships = Ship.all
ships.each do |ship|
2.times do |index|
Cruise.create!(
ship_id: ship.id,
name: Faker::Hacker.adjective.capitalize + " " +Faker::Hacker.noun.capitalize+" Cruise"
)
end
end
#create customers
3.times do |index |
Customer.create!(
first_name:Faker::Name.first_name,
last_name:Faker::Name.last_name,
has_good_credit: true,
paid: false
)
end
#give each customer an addresses and credit card
customers = Customer.all
customers.each do | customer|
Address.create!(
street:Faker::Address.street_address,
city:Faker::Address.city,
postcode:Faker::Address.postcode,
customer_id: customer.id
)
year = [2020, 2021,2022, 2023]
organisations =["American Express", "MasterCard", "Visa"]
CreditCard.create!(
customer_id:customer.id,
number:Faker::Number.number(12),
exp_date:year.sample.to_s + "/" + Faker::Number.between(1,12).to_s,
name_on_card: customer.first_name + " " + customer.last_name,
organisation: organisations.sample.to_s
)
end
p "customers created"
end
end
数据库模式如下所示:
ActiveRecord::Schema.define(version: 2020_10_27_221059) do
create_table "addresses", force: :cascade do |t|
t.string "street"
t.string "city"
t.string "postcode"
t.integer "customer_id", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["customer_id"], name: "index_addresses_on_customer_id"
end
create_table "cabins", force: :cascade do |t|
t.string "name"
t.integer "beds"
t.integer "deck"
t.integer "ship_id", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["ship_id"], name: "index_cabins_on_ship_id"
end
create_table "credit_cards", force: :cascade do |t|
t.string "number"
t.string "exp_date"
t.string "name_on_card"
t.string "organisation"
t.integer "customer_id", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["customer_id"], name: "index_credit_cards_on_customer_id"
end
create_table "cruises", force: :cascade do |t|
t.string "name"
t.integer "ship_id", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["ship_id"], name: "index_cruises_on_ship_id"
end
create_table "customers", force: :cascade do |t|
t.string "last_name"
t.string "first_name"
t.integer "has_good_credit"
t.boolean "paid"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "harbours", force: :cascade do |t|
t.string "name"
t.string "country"
t.string "lat"
t.string "long"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "ships", force: :cascade do |t|
t.string "name"
t.integer "tonnage"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
add_foreign_key "addresses", "customers"
add_foreign_key "cabins", "ships"
add_foreign_key "credit_cards", "customers"
add_foreign_key "cruises", "ships"
end
本质上,我只是尝试 运行 命令:rake ships:seed_cabins
并尝试生成新鲜的数据。但是,我不断收到以下错误:
rake aborted!
ActiveRecord::InvalidForeignKey: SQLite3::ConstraintException: FOREIGN KEY constraint failed
/home/jonathon/Projects/waad/RailsApps/travelagent/lib/tasks/ships.rake:8:in `block (2 levels) in <main>'
Caused by:
SQLite3::ConstraintException: FOREIGN KEY constraint failed
/home/jonathon/Projects/waad/RailsApps/travelagent/lib/tasks/ships.rake:8:in `block (2 levels) in <main>'
Tasks: TOP => ships:seed_cabins
(See full trace by running task with --trace)
我知道销毁表的顺序是个问题,当前顺序是我能找到的最接近文件正常工作的顺序。这似乎是 Ship.destroy_all
行的问题,因为当我删除它和 运行 文件时,它 运行 没有问题!
但是,据我所知,一旦其他表被清除,该数据库将不会留下任何约束,这将阻止 Ship
被删除?
如果有人能指出正确的方向,我将不胜感激。
你错过了摧毁小木屋的机会,它们仍然引用船只作为外键。因此外键约束开始起作用。
附带说明一下,您可以通过在关联中添加 dependent: :destroy
让您的生活更轻松。例如,在您的 Ship
模型中,您将添加
class Ship < ApplicationRecord
has_many :cabins, dependent: :destroy
end
因为没有相应的船,舱室就没有意义。这样做的目的是,每当一个 Ship 实例被销毁时,它也会同时销毁相关的舱室。
我已经用 Rails 6 创建了一个虚拟旅行社,我正在尝试使用 rake 文件为船舶、邮轮和客户详细信息等播种数据。
文件:ships.rake
看起来像这样:
namespace :ships do
desc "TODO"
task seed_cabins: :environment do
CreditCard.destroy_all
Address.destroy_all
Customer.destroy_all
Cruise.destroy_all
Ship.destroy_all
p "tables emptied"
5.times do |index|
Ship.create!(name: Faker::Coffee.blend_name, tonnage: Faker::Number.within(range: 10000..100000))
end
p "ships created"
# create cabins for each ship
ships = Ship.all
ships.each do |ship|
5.times do |index|
Cabin.create!(
ship_id: ship.id,
name: "Suite #{index+1}",
beds: Faker::Number.between(from: 1, to: 3),
deck: Faker::Number.between(from: 1, to: 3)
)
end
end
p "Cabins created"
ships = Ship.all
ships.each do |ship|
2.times do |index|
Cruise.create!(
ship_id: ship.id,
name: Faker::Hacker.adjective.capitalize + " " +Faker::Hacker.noun.capitalize+" Cruise"
)
end
end
#create customers
3.times do |index |
Customer.create!(
first_name:Faker::Name.first_name,
last_name:Faker::Name.last_name,
has_good_credit: true,
paid: false
)
end
#give each customer an addresses and credit card
customers = Customer.all
customers.each do | customer|
Address.create!(
street:Faker::Address.street_address,
city:Faker::Address.city,
postcode:Faker::Address.postcode,
customer_id: customer.id
)
year = [2020, 2021,2022, 2023]
organisations =["American Express", "MasterCard", "Visa"]
CreditCard.create!(
customer_id:customer.id,
number:Faker::Number.number(12),
exp_date:year.sample.to_s + "/" + Faker::Number.between(1,12).to_s,
name_on_card: customer.first_name + " " + customer.last_name,
organisation: organisations.sample.to_s
)
end
p "customers created"
end
end
数据库模式如下所示:
ActiveRecord::Schema.define(version: 2020_10_27_221059) do
create_table "addresses", force: :cascade do |t|
t.string "street"
t.string "city"
t.string "postcode"
t.integer "customer_id", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["customer_id"], name: "index_addresses_on_customer_id"
end
create_table "cabins", force: :cascade do |t|
t.string "name"
t.integer "beds"
t.integer "deck"
t.integer "ship_id", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["ship_id"], name: "index_cabins_on_ship_id"
end
create_table "credit_cards", force: :cascade do |t|
t.string "number"
t.string "exp_date"
t.string "name_on_card"
t.string "organisation"
t.integer "customer_id", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["customer_id"], name: "index_credit_cards_on_customer_id"
end
create_table "cruises", force: :cascade do |t|
t.string "name"
t.integer "ship_id", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["ship_id"], name: "index_cruises_on_ship_id"
end
create_table "customers", force: :cascade do |t|
t.string "last_name"
t.string "first_name"
t.integer "has_good_credit"
t.boolean "paid"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "harbours", force: :cascade do |t|
t.string "name"
t.string "country"
t.string "lat"
t.string "long"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "ships", force: :cascade do |t|
t.string "name"
t.integer "tonnage"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
add_foreign_key "addresses", "customers"
add_foreign_key "cabins", "ships"
add_foreign_key "credit_cards", "customers"
add_foreign_key "cruises", "ships"
end
本质上,我只是尝试 运行 命令:rake ships:seed_cabins
并尝试生成新鲜的数据。但是,我不断收到以下错误:
rake aborted!
ActiveRecord::InvalidForeignKey: SQLite3::ConstraintException: FOREIGN KEY constraint failed
/home/jonathon/Projects/waad/RailsApps/travelagent/lib/tasks/ships.rake:8:in `block (2 levels) in <main>'
Caused by:
SQLite3::ConstraintException: FOREIGN KEY constraint failed
/home/jonathon/Projects/waad/RailsApps/travelagent/lib/tasks/ships.rake:8:in `block (2 levels) in <main>'
Tasks: TOP => ships:seed_cabins
(See full trace by running task with --trace)
我知道销毁表的顺序是个问题,当前顺序是我能找到的最接近文件正常工作的顺序。这似乎是 Ship.destroy_all
行的问题,因为当我删除它和 运行 文件时,它 运行 没有问题!
但是,据我所知,一旦其他表被清除,该数据库将不会留下任何约束,这将阻止 Ship
被删除?
如果有人能指出正确的方向,我将不胜感激。
你错过了摧毁小木屋的机会,它们仍然引用船只作为外键。因此外键约束开始起作用。
附带说明一下,您可以通过在关联中添加 dependent: :destroy
让您的生活更轻松。例如,在您的 Ship
模型中,您将添加
class Ship < ApplicationRecord
has_many :cabins, dependent: :destroy
end
因为没有相应的船,舱室就没有意义。这样做的目的是,每当一个 Ship 实例被销毁时,它也会同时销毁相关的舱室。