принадлежащие с полиморфными отношениями не удаляются из отношения has_many через вложенную форму - PullRequest
0 голосов
/ 20 февраля 2019

Я пытаюсь создать довольно сложную вложенную форму, основанную на полиморфных отношениях.У меня большая часть этого работает, однако это одно отношение не будет выполнять удаление, как я ожидаю.

Recipe имеет много RecipeSteps, RecipeSteps может быть полиморфным, связанным с одной из трех вещей: Techniques, Steps или Recipes

По какой-то причине rails отказывается удалять RecipeStep, когда я пытаюсь сделать это из recipes#edit через вложенную форму и передать _delete: 1 в RecipeStep.Ответ указан в нижней части блока кода ниже.

Я попытался изменить belongs_to, связанный с RecipeStep на requires: false, я попытался добавить dependent: destroy для всего, что могло бы вызватьошибка FK.И я попытался обновить recipes_controller.rb Strong Params, чтобы пропустить всю эту информацию.Я включаю :_delete, :id и все остальные параметры RecipeStep требует.

Я не получаю никаких ошибок, он просто даже не пытается удалить.

Я использую эти драгоценные камни:

  • Кокон
  • Простая форма

Вот соответствующий код:

Проблема полиморфных отношений

stepable.rb

# frozen_string_literal: true

# Adds the polymorphic relation to recipes through recipe_steps
module Stepable
  extend ActiveSupport::Concern

  included do
    has_many :recipe_steps, as: :stepable
    has_many :recipes, through: :recipe_steps
  end
end

Рецепт

recipe.rb - Модель

# frozen_string_literal: true
# == Schema Information
#
# Table name: recipes
#
#  id          :bigint(8)        not null, primary key
#  title       :string
#  description :text
#  created_at  :datetime         not null
#  updated_at  :datetime         not null
#

# Recipe's are containers that can have as many Steps, Techniques, or even
#   other Recipes within them.
class Recipe < ApplicationRecord
  include Stepable

  has_many :recipe_steps

  has_many :steps,
           through: :recipe_steps,
           source: 'stepable',
           source_type: 'Step',
           dependent: :destroy

  has_many :techniques,
           through: :recipe_steps,
           source: 'stepable',
           source_type: 'Technique',
           dependent: :destroy

  has_many :recipes,
           through: :recipe_steps,
           source: 'stepable',
           source_type: 'Recipe',
           dependent: :destroy

  has_many :ingredients,
           through: :steps

  has_many :step_ingredients,
           through: :steps,
           dependent: :destroy

  accepts_nested_attributes_for :recipes,
                                reject_if: :all_blank,
                                allow_destroy: true

  accepts_nested_attributes_for :techniques,
                                reject_if: :all_blank,
                                allow_destroy: true

  accepts_nested_attributes_for :steps,
                                reject_if: :all_blank,
                                allow_destroy: true

  accepts_nested_attributes_for :recipe_steps,
                                reject_if: :all_blank,
                                allow_destroy: true
end

recipes_controller.rb -Контроллер

# frozen_string_literal: true

class RecipesController < ApplicationController
  before_action :set_recipe, only: [:show, :edit, :update, :destroy]

  # GET /recipes
  # GET /recipes.json
  def index
    @recipes = Recipe.all.decorate
  end

  # GET /recipes/1
  # GET /recipes/1.json
  def show
  end

  # GET /recipes/new
  def new
    @recipe = Recipe.new
  end

  # GET /recipes/1/edit
  def edit
  end

  # POST /recipes
  # POST /recipes.json
  def create
    @recipe = Recipe.new(recipe_params)

    respond_to do |format|
      if @recipe.save
        format.html { redirect_to @recipe, notice: 'Recipe was successfully created.' }
        format.json { render :show, status: :created, location: @recipe }
      else
        format.html { render :new }
        format.json { render json: @recipe.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /recipes/1
  # PATCH/PUT /recipes/1.json
  def update
    respond_to do |format|
      if @recipe.update(recipe_params)
        format.html { redirect_to @recipe, notice: 'Recipe was successfully updated.' }
        format.json { render :show, status: :ok, location: @recipe }
      else
        format.html { render :edit }
        format.json { render json: @recipe.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /recipes/1
  # DELETE /recipes/1.json
  def destroy
    @recipe.destroy
    respond_to do |format|
      format.html { redirect_to recipes_url, notice: 'Recipe was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_recipe
      @recipe = Recipe.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def recipe_params
      params.require(:recipe)
            .permit(
              :name,
              :title,
              :description,
              recipe_steps_attributes: [
                :id,
                :position,
                :stepable_type,
                :stepable_id,
                :recipe_id,
                :_destroy
              ],
              recipes_attributes: [
                :id,
                :_destroy
              ],
              techniques_attributes: [
                :id,
                :title,
                :description,
                :_destroy
              ],
              steps_attributes: [
                :id,
                :title,
                :description,
                :_destroy,
                step_ingredients_attributes: [
                  :id,
                  :_destroy,
                  :ingredient_id,
                  measurements_attributes: [
                    :id,
                    :unit,
                    :scalar,
                    :purpose,
                    :_destroy
                  ],
                  ingredient_attributes: [
                    :id,
                    :title,
                    :description,
                    :_destroy
                  ]
                ]
              ]
            )
    end
end

recipe_step.rb - Модель

# frozen_string_literal: true

# == Schema Information
#
# Table name: recipe_steps
#
#  id            :bigint(8)        not null, primary key
#  recipe_id     :bigint(8)
#  stepable_type :string
#  stepable_id   :bigint(8)
#  position      :integer
#  created_at    :datetime         not null
#  updated_at    :datetime         not null
#

# RecipeStep is the through table for relating polymorphic stepable items to
#   the recipe.
class RecipeStep < ApplicationRecord
  belongs_to :stepable, polymorphic: true, required: false
  belongs_to :recipe, required: false

  before_create :set_position

  accepts_nested_attributes_for :stepable,
                                reject_if: :all_blank,
                                allow_destroy: true

  default_scope -> { order(position: :asc) }

  private

  def set_position
    self.position = recipe.recipe_steps.count + 1
  end
end

Ответ

Started PATCH "/recipes/3" for ::1 at 2019-02-19 15:55:20 -0500
Processing by RecipesController#update as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"21JWhw/o6ysiInTGv4gJp8pTrvh5jpscpGw2Fhm2o3OyitieRz26nBGeFaZclH21zXHBhjtF7L9ujE3yILl+IQ==", "recipe"=>{"title"=>"Pizza", "description"=>"Three eggs with cilantro, tomatoes, onions, avocados and melted Emmental cheese. With a side of roasted potatoes, and your choice of toast or croissant.", "recipe_steps_attributes"=>{"0"=>{"position"=>"2", "_destroy"=>"1", "id"=>"11"}, "1"=>{"position"=>"10", "_destroy"=>"false", "id"=>"10"}}, "steps_attributes"=>{"0"=>{"title"=>"Ricotta Stuffed Ravioli", "description"=>"Two butter croissants of your choice (plain, almond or cheese). With a side of herb butter or house-made hazelnut spread.", "step_ingredients_attributes"=>{"0"=>{"ingredient_id"=>"1", "measurements_attributes"=>{"0"=>{"unit"=>"", "scalar"=>"0.7", "_destroy"=>"false", "id"=>"15"}, "1"=>{"unit"=>"", "scalar"=>"6.7", "_destroy"=>"false", "id"=>"16"}, "2"=>{"unit"=>"", "scalar"=>"5.8", "_destroy"=>"false", "id"=>"17"}}, "_destroy"=>"false", "id"=>"11"}}, "id"=>"10"}}}, "commit"=>"Save", "id"=>"3"}
  Recipe Load (0.3ms)  SELECT  "recipes".* FROM "recipes" WHERE "recipes"."id" = $1 LIMIT $2  [["id", 3], ["LIMIT", 1]]
  ↳ app/controllers/recipes_controller.rb:69
   (0.2ms)  BEGIN
  ↳ app/controllers/recipes_controller.rb:46
  RecipeStep Load (0.8ms)  SELECT "recipe_steps".* FROM "recipe_steps" WHERE "recipe_steps"."recipe_id" = $1 AND "recipe_steps"."id" IN ($2, $3) ORDER BY "recipe_steps"."position" ASC  [["recipe_id", 3], ["id", 11], ["id", 10]]
  ↳ app/controllers/recipes_controller.rb:46
  Step Load (1.0ms)  SELECT "steps".* FROM "steps" INNER JOIN "recipe_steps" ON "steps"."id" = "recipe_steps"."stepable_id" WHERE "recipe_steps"."recipe_id" = $1 AND "recipe_steps"."stepable_type" = $2 AND "steps"."id" = $3 ORDER BY "recipe_steps"."position" ASC  [["recipe_id", 3], ["stepable_type", "Step"], ["id", 10]]
  ↳ app/controllers/recipes_controller.rb:46
  StepIngredient Load (0.4ms)  SELECT "step_ingredients".* FROM "step_ingredients" WHERE "step_ingredients"."step_id" = $1 AND "step_ingredients"."id" = $2  [["step_id", 10], ["id", 11]]
  ↳ app/controllers/recipes_controller.rb:46
  Measurement Load (0.4ms)  SELECT "measurements".* FROM "measurements" WHERE "measurements"."step_ingredient_id" = $1 AND "measurements"."id" IN ($2, $3, $4)  [["step_ingredient_id", 11], ["id", 15], ["id", 16], ["id", 17]]
  ↳ app/controllers/recipes_controller.rb:46
   (0.2ms)  COMMIT
  ↳ app/controllers/recipes_controller.rb:46
Redirected to http://localhost:3100/recipes/3
Completed 302 Found in 21ms (ActiveRecord: 3.3ms)

Started GET "/recipes/3" for ::1 at 2019-02-19 15:55:21 -0500
Processing by RecipesController#show as HTML

Важная информация: "recipe_steps_attributes"=>{"0"=>{"position"=>"2", "_destroy"=>"1", "id"=>"11"}

Как видите, аргумент _destroy установлен правильно и недопустимые ошибки параметров не отправляются.Однако до сих пор даже не предпринимались попытки удалить recipe_step

. Я не включил представления для этой ошибки, потому что я считаю, что они функционируют правильно, что можно увидеть по параметрам, передаваемым в контроллер.,Если вы считаете, что в моей реализации simple form и или cocoon может быть ошибка, пожалуйста, спросите, и я добавлю эти кусочки кода.

1 Ответ

0 голосов
/ 20 февраля 2019

Проблема в том, что Recipe связан с RecipeStep двумя способами:

  1. has_many :recipe_steps
  2. has_many :recipe_steps, as: :stepable через беспокойство stepable.rb

Проблема была в том, что оба они были :recipe_steps.Я изменил задачу на: has_many :through_steps, as: :stepable, class_name: 'RecipeStep', и это решило проблему удаления.

...