执行级联删除通过关联遍历has_many

时间:2017-10-12 18:19:14

标签: ruby-on-rails ruby

我搜索过Stackoverflow和其他地方,但无法解决此问题。

问题:当尝试通过Rails destroy动作销毁实例变量时,我收到此帖子标题中的错误消息。

相关守则:

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

'CreateDescribes'迁移文件将'create_table''id'选项设置为'false',因为我永远不需要直接访问'describe'连接表。我找到了一个Stackoverflow帖子(该链接逃脱了我),其中建议强制执行级联删除操作要求连接表条目具有唯一标识符。传统的Rails强制执行唯一数据库表记录的方法是默认使用一个名为“id”的主键。我尝试通过删除'CreateDescribes'迁移文件中的'id:false'键/值对来确保实现此建议,并确保schema.rb文件中的'describe'表在重新运行'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通过公司和Metadatum模型之间的关联将一组Metadatum对象与新公司对象相关联。

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对象与新的Company对象关联时,我后来能够成功销毁Company对象。只有当我将Metadatum对象与公司对象关联时,我才会遇到此错误。

该错误表示在nil类上尝试了destroy操作。当我确认公司对象被销毁时,它不能是零。什么是Rails服务器日志标识为零类?更重要的是,为什么对具有关联Metadatum对象的Company对象的销毁操作不会在相应的“describe”连接表上传播强制级联删除?

1 个答案:

答案 0 :(得分:0)

我在别处收到的对这个问题的回应导致了解决方案。首先,我需要添加&#39;依赖:: destroy&#39;约束到&#39; has_many到&#39;公司&#39;中的协会和&#39; Metadatum&#39;模型。

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
...

其次,我需要坚持允许&#39; create_table&#39;自动生成“id”的方法首要的关键。因此,关于&#39;描述&#39;在数据库模式中的表中,我实现了以下步骤:

步骤01:删除&#39; id:false&#39;来自适当的迁移文件的约束。

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

警告:&#39; rails db:reset&#39;清除数据库。由于我在数据库中播放了测试数据,我现在并不关心这一点。但是,如果您进一步开发并且“种子”,那么它确实会成为问题。单独的文件不足以重新创建测试状态。

现在,&#39;描述&#39;表格&#39; schema.rb&#39;读得正确:

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

在测试应用时,会发生级联删除,并且相应的“#”描述了&#39;表记录已删除。