通过关联在has_many中强制执行级联删除



我已经搜索了Stackoverflow和其他地方,但无法解决这个问题。

问题:当试图通过Rails销毁操作销毁实例变量时,我在这篇文章的标题中收到了错误消息。

相关代码:

class Company < ApplicationRecord
has_many :describes, dependent: :destroy
has_many :descriptors, through: :describes, source: :metadatum
end
class Metadatum < ApplicationRecord
has_many :describes, dependent: :destroy
has_many :descriptees, through: :describes, source: :company
...
end
class Describe < ApplicationRecord
belongs_to :company
belongs_to :metadatum
end
class CompaniesController < ApplicationController
...
def destroy
@company = Company.find(params[:id])
@company.destroy
redirect_to companies_url
end
...
end
ActiveRecord::Schema.define(version: <some version #) do
create_table "companies", force: :cascade do |t|
t.string   "name"
t.string   "description"
t.datetime "created_at",  null: false
t.datetime "updated_at",  null: false
t.index ["name"], name: "index_companies_on_name", unique: true
end
create_table "describes", id: false, force: :cascade do |t|
t.integer  "company_id"
t.integer  "metadatum_id"
t.datetime "created_at",   null: false
t.datetime "updated_at",   null: false
t.index ["company_id"], name: "index_describes_on_company_id"
t.index ["metadatum_id"], name: "index_describes_on_metadatum_id"
end
create_table "metadata", force: :cascade do |t|
t.string   "name"
t.string   "description"
t.datetime "created_at",  null: false
t.datetime "updated_at",  null: false
t.index ["name"], name: "index_metadata_on_name", unique: true
end
end
class CreateCompanies < ActiveRecord::Migration[5.0]
def change
create_table :companies do |t|
t.string :name
t.string :description
t.timestamps
end
add_index :companies, :name, unique: true
end
end
class CreateMetadata < ActiveRecord::Migration[5.0]
def change
create_table :metadata do |t|
t.string :name
t.string :description
t.timestamps
end
add_index :metadata, :name, unique: true
end
end
class CreateDescribes < ActiveRecord::Migration[5.0]
def change
create_table :describes, id: false do |t|
t.references :company, foreign_key: true
t.references :metadatum, foreign_key: true
t.timestamps
end
end
end

"CreateDescripts"迁移文件将"create_table"id"选项设置为"false",因为我永远不需要直接访问"descriptions"联接表。我找到了一篇Stackoverflow帖子(链接转义),其中建议强制执行级联删除操作需要联接表条目具有唯一标识符。Rails强制执行唯一数据库表记录的传统方法是拥有一个默认名称为"id"的主键。我尝试通过删除"CreateDescriptions"迁移文件中的"id:false"键/值对来实现此建议,并确保schema.rb文件的"describes"表在重新运行"db:migrate"后反映了这一点。

不幸的是,这种方法产生了同样的错误。Rails服务器日志将"CompaniesController"destroy"操作中的以下代码行标识为此错误的来源:

@company.destroy

生成的错误消息为:

undefined method `to_sym' for nil:NilClass Did you mean? to_s

当我创建一个新的"Company"对象并将其相应的记录保存到数据库中时,我确认了它的存在,例如,通过将"params"哈希中的"id"值与数据库表记录中的"id"字段进行交叉检查。

"CompaniesController"create"操作通过公司和元数据模型之间的has_many关联将一组元数据对象关联到新的公司对象。

def create
@company = Company.new(company_attributes)
params[:metadata][:ids].each do |m|
if !m.empty?
@company.descriptors << Metadatum.find(m)
end
end
if @company.save
...
end

我确认关联已被捕获。

有趣的是,当我不将Metadatum对象与新的公司对象关联时,我以后可以成功地销毁公司对象。只有当我将Metadatum对象与Company对象关联时,我以后才会遇到此错误。

该错误表示试图对nil类执行销毁操作。当我确认被销毁的公司物品存在时,它不可能为零。Rails服务器日志中标识为nil类的是什么?更重要的是,为什么对具有关联元数据对象的Company对象的销毁操作不在适当的"描述"联接表上传播强制级联删除?

我在其他地方收到的对此问题的响应导致了解决方案。首先,我需要将"dependent::destroy"约束添加到"Company"one_answers"Metadatum"模型中的"has_many through"关联中。

class Company < ApplicationRecord
has_many :describes, dependent: :destroy
has_many :descriptors, through: :describes, source: :metadatum, dependent: :destroy
...
class Metadatum < ApplicationRecord
has_many :describes, dependent: :destroy
has_many :descriptees, through: :describes, source: :company, dependent: :destroy
...

其次,我需要坚持Rails的约定,允许"create_table"方法自动生成一个"id"主键。因此,对于数据库模式中的"describes"表,我实现了以下步骤:

步骤01:从相应的迁移文件中删除"id:false"约束。

class CreateDescribes < ActiveRecord::Migration[5.0]
def change
# create_table :describes, id: false do |t|
create_table :describes do |t|
t.references :company, foreign_key: true
t.references :metadatum, foreign_key: true
t.timestamps
end
end
end

步骤02:重建生成的数据库,然后重新运行迁移。

rails db:reset
rails db:migrate

警告:"rails db:reset"将清除数据库。由于我在数据库中植入了测试数据,所以目前我并不关心这一点。然而,如果您进一步进行开发,并且"seed.rb"文件本身不足以重新创建测试状态,那么它确实会出现问题。

现在,"schema.rb"中的"descriptions"表读取正确:

create_table "describes", force: :cascade do |t|
t.integer  "company_id"
t.integer  "metadatum_id"
t.datetime "created_at",   null: false
t.datetime "updated_at",   null: false
t.index ["company_id"], name: "index_describes_on_company_id"
t.index ["metadatum_id"], name: "index_describes_on_metadatum_id"
end

在测试应用程序时,会发生级联删除,并删除相应的"描述"表记录。

最新更新