我有一个来自设计gem的用户类。
我有一个admin_account类,它是从使用STI的user继承的。我有一个admin_task类。我有一个admin_task_assignment类。
我得到一个外键错误,而试图通过rails控制台创建一个管理任务分配。
t = AdminTaskAssignment.create(admin_account_id: 1, admin_task_id: 1)
我正在搜索AdminAccount.find(1)和AdminTask.find(1)以查找存在的记录。但是我得到:
SQLite3::ConstraintException: FOREIGN KEY constraint failed (ActiveRecord::InvalidForeignKey) FOREIGN KEY constraint failed (SQLite3::ConstraintException)
class AdminAccount < User
has_many :admin_task_assignments, foreign_key: 'admin_account_id'
has_many :admin_tasks, through: :admin_task_assignments
end
class AdminTask < ApplicationRecord
has_many :admin_task_assignments, foreign_key: 'admin_task_id'
has_many :admin_accounts, through: :admin_task_assignments
belongs_to :client
end
class AdminTaskAssignment < ApplicationRecord
belongs_to :admin_account, foreign_key: 'admin_account_id'
belongs_to :admin_task, foreign_key: 'admin_task_id'
end
class Client < ApplicationRecord
has_many :admin_tasks
end
class User < ApplicationRecord
attribute :type, :string
validates :email, uniqueness: true
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
end
create_table "admin_accounts", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "admin_task_assignments", force: :cascade do |t|
t.integer "admin_account_id", null: false
t.integer "admin_task_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["admin_account_id"], name: "index_admin_task_assignments_on_admin_account_id"
t.index ["admin_task_id"], name: "index_admin_task_assignments_on_admin_task_id"
end
create_table "admin_tasks", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "client_id", null: false
t.string "name"
t.string "task_type"
t.index ["client_id"], name: "index_admin_tasks_on_client_id"
end
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "type"
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
add_foreign_key "admin_task_assignments", "admin_accounts"
add_foreign_key "admin_task_assignments", "admin_tasks"
add_foreign_key "admin_tasks", "clients"
首先从你的模型中删除所有的cruft:
- 电子邮件验证已经由设计::Validatable 处理
- 您不需要定义模式中存在的属性。
- 你不需要配置外键选项,如果它可以从关联的名称中派生出来,并且你不会因为过于显式而赢得任何东西。
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
end
class AdminAccount < User
has_many :admin_task_assignments
has_many :admin_tasks,
through: :admin_task_assignments
end
class AdminTaskAssignment < ApplicationRecord
belongs_to :admin_account
belongs_to :admin_task
end
class AdminTask < ApplicationRecord
has_many :admin_task_assignments
has_many :admin_accounts,
through: :admin_task_assignments
belongs_to :client
end
让我们来处理数据库模式。
- 如果你使用单表继承,你不需要或想要
admin_accounts
表。ActiveRecord甚至不会使用它,因为AdminAccount继承自User。放弃。 - 您需要正确设置外键,以便它们指向
users
表。
如果您还没有合并/推送这些模式更改,最简单的方法就是回滚到创建admin_accounts
表之前并修复/删除迁移:
class CreateAdminTaskAssignments < ActiveRecord::Migration[7.0]
def change
create_table :admin_task_assignments do |t|
t.belongs_to :admin_account,
null: false,
foreign_key: { to_table: :users } # this is super important
t.belongs_to :admin_task, null: false, foreign_key: true
t.timestamps
# optional -- add a unique index to ensure that the same task can't be assigned multiple times
# t.index [:admin_account_id, :admin_task_id], unique: true
end
end
end
并删除创建admin_accounts
表的迁移。重新运行迁移和Bob的叔叔。由于您正在删除表,因此您不必处理一堆带有垃圾数据的孤立记录。
如果你需要解决这个问题,你需要删除外键并用正确的外键替换它:
class FixTaskAssignmentsForeignKeys < ActiveRecord::Migration[7.0]
def change
remove_foreign_key :admin_tasks, :admin_accounts
add_foreign_key :admin_tasks, :users,
column: :admin_account_id
end
end
请注意,如果你已经添加了一堆数据到你的应用程序已经它可能已经有垃圾值在admin_account_id
列,实际上是从你的失效admin_accounts表的id,而不是用户id。您可能需要截断表。
然后你可以删除admin_accounts
表,因为不再有一个外键引用它:
class DropAdminAccounts < ActiveRecord::Migration[7.0]
def change
drop_table :admin_accounts do |t|
t.timestamps
end
end
end