ruby 通过一次表单提交创建多个记录

kpbpu008  于 12个月前  发布在  Ruby
关注(0)|答案(1)|浏览(87)

我有三个模型:用户、配料和哪个用户有哪些配料的Map-用户配料。
我目前的设置适用于一次添加1种成分。我想要的是更新代码,这样用户就可以输入一些成分,只需单击“提交”一次,而不是单独单击每个成分。我已经研究了nested_resources,但似乎不是使用它的正确位置。
做这件事的正确方法是什么?
app/models/user.rb

class User < ApplicationRecord
  ...

  has_many :user_ingredients, dependent: :destroy
  has_many :ingredients, through: :user_ingredients
  
  ...
end

app/models/ingredient.rb

class Ingredient < ApplicationRecord
  ...

  has_many :user_ingredients, dependent: :destroy
  has_many :owners, through: :user_ingredients

  ...
end

app/models/user_ingredient.rb

class UserIngredient < ApplicationRecord
  belongs_to :user
  belongs_to :ingredient

  validates :user, presence: true
  validates :ingredient, presence: true
end

app/views/user_ingredients/new.html.erb

<div>
  <%= turbo_frame_tag @user_ingredient do %>
    <%= render "form", user_ingredient: @user_ingredient %>
  <% end %>
</div>

app/views/user_ingredients/_form.html.erb

<div class="w-full mx-auto">
  <%= form_for @user_ingredient do |f| %>
    <div class="flex-row gap--md">
      <%= f.select(
        :ingredient_id,
        options_from_collection_for_select(Ingredient.where(id: f.object.ingredient_id), :id, :name, :selected => f.object.ingredient_id),
        { prompt: 'Start typing to search' },
        { id: "drDwn_ingredient",
          class: "w-full border border-black",
          required: true,
          data: { 
            controller: "selectIngredient",
            selectIngredient_url_value: autocomplete_ingredients_path,
          },
        }) %>

      <div class="flex-row gap--xxxs">
        <label>
          <input type="submit" class="add_cancel_ing gap--md" />
          <%= inline_svg_tag "svg/circle-check.svg", class: "svg_add_ing" %>
        </label>
        <%= link_to user_ingredients_path do %>
          <%= inline_svg_tag "svg/circle-xmark.svg", class: 'svg_cancel_ing' %>
        <% end %>
      </div>
    </div>
  <% end %>
</div>

app/controllers/user_ingredients_controller.rb

class UserIngredientsController < ApplicationController
  before_action :authenticate_user!
  before_action :set_user_ingredient, only: [:show, :destroy]

  def index
    @user_ingredients = current_user.user_ingredients
  end

  def new
    @user_ingredient = UserIngredient.new
  end

  def create
    @user_ingredient = UserIngredient.new(user_ingredient_params.merge(user: current_user))

    if @user_ingredient.save
      respond_to do |format|
        format.html { redirect_to user_ingredients_path, notice: 'Ingredient was successfully added to your bar!' }
        format.turbo_stream { flash.now[:notice] = 'Ingredient was successfully added to your bar!' }
      end
    else
      render :new
    end
  end

  def destroy
    @user_ingredient.destroy
    respond_to do |format|
      format.html { redirect_to user_ingredients_path, notice: "Ingredient was removed!" }
      format.turbo_stream { flash.now[:notice] = "Ingredient was removed!" }
    end
  end

  private

  ...

  def set_user_ingredient
    @user_ingredient = current_user.user_ingredients.find(params[:id])
  end

  def user_ingredient_params
    params.require(:user_ingredient).permit(:id, :ingredient_id)
  end
end

app/javascript/controllers/selectIngredient_controller.js

import { Controller } from "@hotwired/stimulus";
import { get } from "@rails/request.js";
import TomSelect from "tom-select";

export default class extends Controller {
  static values = { url: String };

  multi_select_config = function () {
    return {
      plugins: ["remove_button", "no_active_items"],
      valueField: "value",
      load: (q, callback) => this.search(q, callback),

      closeAfterSelect: true,
      persist: false,
      create: false,
    };
  };

  async search(q, callback) {
    const response = await get(this.urlValue, {
      query: { q: q },
      responseKind: "json",
    });

    if (response.ok) {
      const list = await response.json;
      callback(list);
    } else {
      console.log("Error in select_ctrl: ");
      console.log(response);
      callback();
    }
  }

  connect() {
    new TomSelect(this.element, this.multi_select_config());
  }
}
7d7tgy0s

7d7tgy0s1#

您应该使用accepts_nested_attributes_for方法为User并尝试通过User创建相关记录。
https://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
或者,您可以尝试进行自定义操作,以便一次接受具有多个记录的自定义表单。但第一种选择将更可预测,更容易支持。
对于视图,您可以使用cocoon gem。它很旧了,但还能用。或者,您也可以从中汲取灵感,定制解决方案)https://github.com/nathanvda/cocoon

相关问题