Rails: 多态模型,删除麻烦

Rails: Polymorphic model, troubles with deletion

在我的 Rails 电影数据库中,我现在添加了一个可以标记电影和演员的“标签”模型。 但是,我无法删除用于标记电影或演员之一的标签(foreign_key 约束);当我在这样的标签上调用 destroy 时,Rails 说:

Mysql2::Error: Cannot delete or update a parent row: a foreign key constraint fails (`newmovie_development`.`taggings`, CONSTRAINT `fk_rails_e21d88485b` FOREIGN KEY (`tag_id`) REFERENCES `tags` (`id`))

电影(演员在这方面完全一样):

class Movie < ApplicationRecord
    ...
    has_many :taggings, as: :taggable, dependent: :destroy
    has_many :tags, through: :taggings
    ...

标签:

class Tag < ApplicationRecord
    validates :name, presence: true

    has_many :taggings
    has_many :movies, through: :taggings, :source => 'taggable', :source_type => 'Movie'
    has_many :actors, through: :taggings, :source => 'taggable', :source_type => 'Actor'
end

标记:

class Tagging < ApplicationRecord
    belongs_to :tag
    belongs_to :taggable, polymorphic: true
end

你可能在这里有点困惑。如果您有想要编辑标签的电影或演员,可以使用 has_many :tags.

生成的 tags_ids= setter
<%= form_with(model: @actor) do |form| %>
  <div class="field">
    <%= form.label :tags_ids, 'Tags' %>
    <%= form.collection_checkboxes :tag_ids, Tag.all, :id, :name %>
  </div>
<% end %>
class ActorsController < ApplicationController
  before_action :set_actor, except: [:new, :create, :index]

  def update
    if @actor.update(actor_params)
      redirect_to @actor, success: 'Actor updated' 
    else
      render :new
    end
  end

  private

  def set_actor
    @actor = Actor.find(params[:id])
  end
  
  def actor_params
    params.require(:actor)
          .permit(:foo, :bar, tag_ids: [])
  end
end

这将自动将参数中传递的数组与标记 table 和 add/create 行中的任何现有行相应地进行比较。

如果你想创建一个按钮来一个一个地“从可标记中删除标签”(可能用 ajax 增强)你想确保你实际上是从标签中删除行 table 而不是标签:

module Actors
  class TagsController < ApplicationController
    # DELETE /actors/1/tags/2
    def destroy
      @tagging = @actor.taggings.find_by!(tag_id: params[:id])
      @tagging.destroy
    end

    private

    def set_actor
      @actor = Actor.find(params[:actor_id])
    end
  end
end

如果你真的想从整个系统中完全删除一个标签(Whosebug 上的 burnination 等价物)你需要在关联上设置依赖选项,以便不违反外键约束:

class Tag < ApplicationRecord
  validates :name, presence: true
  has_many :taggings, dependent: :destroy
  # ...
end

您还应确保添加唯一索引,以确保您的标签中不会出现重复项 table:

add_index :taggings, [:tag_id, :taggable_id, :taggable_type], unique: true

和验证:

class Tagging
  validates_uniqueness_of :tag_id, scope: [:taggable_id, :taggable_type]
end