Моя вложенная модель рельсов не распознает атрибут _destroy - PullRequest
0 голосов
/ 24 апреля 2020

У меня есть родительская модель (коктейль) и ребенок (доза).

У меня есть вложенная форма, в которой я хочу иметь возможность создавать / удалять объекты доз на go.

Часть создания работает без атрибута _destroy в сильных параметрах, но когда я добавляю _destroy к атрибуту ha sh, чтобы иметь возможность удалить, я получаю ошибку unknown attribute '_destroy' for Dose. Я не уверен, где я ошибаюсь.

Также у меня есть идея, что я слишком усложняю свой код, и должен быть более простой способ создания доз, чем тот, который я делаю (это add_doses метод даже необходим?). Если бы я мог получить некоторую обратную связь по этому поводу, я был бы очень признателен.

Коктейль:

class Cocktail < ApplicationRecord
  validates :name, uniqueness: true, presence: true

  has_many :doses, dependent: :destroy
  has_many :ingredients, through: :doses
  accepts_nested_attributes_for :doses, allow_destroy: true
end

Доза:

class Dose < ApplicationRecord
  validates :description, presence: true
  belongs_to :cocktail
  belongs_to :ingredient
  validates :cocktail, uniqueness: { scope: :ingredient }
 end

Контроллер:

class CocktailsController < ApplicationController
  before_action :set_task, only: %i[show edit update]

  def index
    @cocktails = Cocktail.all
  end

  def show; end

  def new
    @cocktail = Cocktail.new
    @cocktail.doses.build
  end

  def create
    @cocktail = Cocktail.new(cocktail_params)
    @cocktail.save
    add_doses
    redirect_to cocktails_path
  end

  def edit; end

  def update
    @cocktail.update(cocktail_params)
    add_doses
    redirect_to cocktail_path(@cocktail)
  end

  private

  def set_task
    @cocktail = Cocktail.find(params[:id])
  end

  def cocktail_params
    params[:cocktail][:name] = params[:cocktail][:name].downcase.titleize
    params.require(:cocktail).permit(:name)
  end

  def add_doses
    @cocktail.doses.destroy_all
    strong_params = params.require(:cocktail).permit(doses_attributes: [:description, :ingredient_id, :_destroy, :id])
    params[:cocktail][:doses_attributes].each_key do |key|
      @cocktail.doses.create(strong_params[:doses_attributes][key])
    end
  end
end

Мой основной вид формы:

<%= simple_form_for @cocktail do |f| %>
  <%= f.input :name, required: true %>
  <%= f.nested_fields_for :doses do |dose| %>
    <%= render '/cocktails/partials/doses_fields', f: dose %>
  <% end %>
  <div class="btn-group" role="group" aria-label="Basic example">
    <%= link_to_add_association 'add dose', f, :doses, partial: '/cocktails/partials/doses_fields', class: "btn btn-secondary" %>
    <%= f.button :submit, class: "btn btn-secondary" %>
  </div>
<% end %>

Мой частичный вид для добавления новых доз:

<div class='nested-fields'>
  <div class="field">
    <%= f.text_field :description %>
  </div>
    <%= f.association :ingredient, collection: Ingredient.all %>
    <%= link_to_remove_association "remove dose", f %>
</div>

Если вы также хотите указать на все, что не так с моим кодом, не делайте сдерживайся и будь худшим;)

1 Ответ

2 голосов
/ 24 апреля 2020

Сначала исправьте ваши методы #create и #update, чтобы они проверяли, действительно ли запись сохранена перед перенаправлением!

Затем добавьте вложенные параметры в белый список и добавьте метод add_doses, который это творческая, но крайне некорректная попытка дублирования функциональности, предоставляемой вложенными атрибутами.

class CocktailsController < ApplicationController
  before_action :set_cocktail, only: %i[show edit update]

  def index
    @cocktails = Cocktail.all
  end

  def show; end

  def new
    @cocktail = Cocktail.new
    @cocktail.doses.build
  end

  def create
    @cocktail = Cocktail.new(cocktail_params)
    if @cocktail.save
      redirect_to cocktails_path
    else
      render :new
    end
  end

  def edit; end

  def update
    if @cocktail.update(cocktail_params)
      redirect_to @cocktail
    else
      render :edit
    end
  end

  private

  def set_cocktail
    @cocktail = Cocktail.find(params[:id])
  end

  def cocktail_params
    # this should be done in the model
    params[:cocktail][:name] = params[:cocktail][:name].downcase.titleize
    params.require(:cocktail)
          .permit(
             :name,
             doses_attributes: [
               :id,
               :_destroy,
               :description,
               :ingredient_id
             ]
          )
  end
end

Если вы хотите разрешить пользователям удалять вложенные записи, просто установите флажок:

<div class='nested-fields'>
  <div class="field">
    <%= f.text_field :description %>
  </div>
    <%= f.association :ingredient, collection: Ingredient.all %>
    <%= f.input :_destroy, as: :boolean, label: 'Remove' %>
</div>

Вам также необходимо исправить свою проверку:

class Dose < ApplicationRecord
  validates :description, presence: true
  belongs_to :cocktail
  belongs_to :ingredient
  validates :cocktail_id, uniqueness: { scope: :ingredient_id }
end

При создании проверки уникальности необходимо настроить ее для проверки уникальности фактических столбцов, а не ассоциаций.

...