【问题标题】:Nested fields_for form wont create child objects rails 5嵌套fields_for表单不会创建子对象 rails 5
【发布时间】:2017-01-31 13:24:29
【问题描述】:

sale.rb "父"

class Sale < ActiveRecord::Base
  has_many :branch_history_solds
  accepts_nested_attributes_for :branch_history_solds, :reject_if => lambda { |a| a[:content].blank? }, 
                                :allow_destroy => true
end 

class SalesController < ApplicationController
  before_action :set_sale, only: [:show, :edit, :update, :destroy]

  # GET /sales
  # GET /sales.json
  def index
    @sales = Sale.all
  end

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

  # GET /sales/new
  def new
    @sale = Sale.new
    @sale.branch_history_solds.build 
  end

  # GET /sales/1/edit
  def edit
  end

  # POST /sales
  # POST /sales.json
  def create
    @sale = Sale.create(sale_params)
    # @sale.branch_history_solds.build

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

  # PATCH/PUT /sales/1
  # PATCH/PUT /sales/1.json
  def update
    @sale = Sale.find(params[:id])

    respond_to do |format|
      if @sale.update_attributes(sale_params)
        format.html { redirect_to @sale, notice: 'Sale was successfully updated.' }
        format.json { render :show, status: :ok, location: @sale }
      else
        format.html { render :edit }
        format.json { render json: @sale.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /sales/1
  # DELETE /sales/1.json
  def destroy
    @sale.destroy
    respond_to do |format|
      format.html { redirect_to sales_url, notice: 'Sale was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

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

    # Never trust parameters from the scary internet, only allow the white list through.
    def sale_params
      params.require(:sale).permit(:receipt_no, :customer_name, :phone_number, :email, :branch_id, :paid, branch_history_sold_attributes: [:id, :sold, :branch_product_id])
    end
end

branch_history_sold.rb "孩子"

class BranchHistorySold < ActiveRecord::Base
  belongs_to :sale

end

class BranchHistorySoldsController < ApplicationController

  def index
    @search = BranchHistorySold.ransack(params[:q]) 
    @branch_sold_histories = @search.result(distinct: true).group(:name).sum(:sold)
  end   

  def create 
    @sale = Sale.find(params[:sale_id]) # Find specific branch_product we will be working with
    @branch_history_sold = @sale.branch_history_solds.create(branch_history_sold_params) # Enable whitelisted attributes to get created
    flash[:notice] = "New products have been Sold from branch" # flash notice will show immediately after branch_history_sold gets created
    redirect_to branch_product_path(@branch_product) # redirect to branch_product show page
  end

  def destroy
    @sale = Sale.find(params[:sale_id]) # Find specific branch_product we will be working with
    @branch_history_sold = @sale.branch_history_solds.find(params[:id]) # Find specific branch_history_sold that will be destroyed
    @branch_history_sold.destroy # destroy branch_history_sold
    flash[:notice] = "Newly sold products have been added back to branch" # flash notice will show immediately after branch_history_sold is destroyed
    redirect_to branch_product_path(@branch_product) # redirect to branch_product show page 
  end   

  private 

  def branch_history_sold_params 
    params.require(:branch_history_sold).permit(:sold, :customer_name) # whitelisted attributes 
  end 

end

最后是我的带有 fields_for 属性的表单

<div class="container">
      <div class="row">
          <%= form_for @sale, html: { class: "form-horizontal" } do |f| %>
                <!-- Text input-->
                <div class="form-group">
                    <label class="col-md-1 control-label">R/NO</label>
                    <div class="col-md-6">
                        <%= f.collection_select(:branch_id, Branch.all, :id, :name) %>
                    </div>
                </div>
                <!-- Text input-->
                <div class="form-group">
                    <label class="col-md-1 control-label">R/NO</label>
                    <div class="col-md-6">
                        <%= f.text_field :receipt_no, placeholder: "Receipt number", class: "form-control input-md" %>
                    </div>
                </div>
                <!-- Text input-->
                <div class="form-group">
                    <label class="col-md-1 control-label" >Name</label>
                    <div class="col-md-6">
                        <%= f.text_field :customer_name, placeholder: "Prince Abalogu", class: "form-control input-md" %>
                    </div>
                </div>
                <!-- Appended Input-->
                <div class="form-group">
                    <label class="col-md-1 control-label">Number</label>
                    <div class="col-md-6">
                        <%= f.text_field :phone_number, placeholder: "08185438075", class: "form-control input-md" %>
                    </div>
                </div>
                <!-- Appended Input-->
                <div class="form-group">
                    <label class="col-md-1 control-label">E-mail</label>
                    <div class="col-md-6">
                        <%= f.text_field :email, placeholder: "example@yahoo.com", class: "form-control input-md" %>
                    </div>
                </div>

                <!-- Appended Input-->
                <%= f.fields_for :branch_history_solds, @sale.branch_history_solds.build do |b| %>
                  <div class="form-group">
                      <label class="col-md-1 control-label">Product</label>
                      <div class="col-md-6">
                        <%= b.number_field :sold, placeholder: "Quantity" %>
                      </div>
                  </div>
                    <div class="form-group">
                        <label class="col-md-1 control-label"></label>
                        <div class="col-md-6">
                            <% @branch = BranchProduct.where :branch_id, 19 %>
                            <%= b.collection_select(:branch_product_id, BranchProduct.where(branch_id: params[:branch_id] ), :id, :name ) %> Select Product
                        </div>
                    </div>
                <% end %>

                <!-- Appended Input-->
                <div class="form-group">
                    <label class="col-md-1 control-label"></label>
                    <div class="col-md-6">
                        <%= f.check_box :paid %> Paid
                    </div>
                </div>

                <!-- Button (Double) -->
                <div class="form-group">
                    <label class="col-md-1 control-label"></label>
                    <div class="col-md-8">
                        <%= f.button :submit %>
                    </div>
                </div>
          <% end %>
      </div>
    </div>

表单显示,但我现在唯一的问题是提交后它没有创建任何 branch_history_solds

【问题讨论】:

    标签: ruby-on-rails ruby ruby-on-rails-5 form-for fields-for


    【解决方案1】:

    问题在于,您用于评估嵌套记录是否应被拒绝的 lambda 将始终返回 true,因为您的表单/模型没有 content 属性:

    class Sale < ActiveRecord::Base
      has_many :branch_history_solds
      accepts_nested_attributes_for :branch_history_solds, :reject_if => lambda { |a| a[:content].blank? }, 
                                    :allow_destroy => true
    end  
    

    您还将错误的属性列入白名单branch_history_sold 而不是branch_history_solds。

    你需要把它改成实际传递的属性:

    class Sale < ActiveRecord::Base
      has_many :branch_history_solds
      accepts_nested_attributes_for :branch_history_solds, :reject_if => lambda { |a| a[:branch_product_id].blank? }, 
                                    :allow_destroy => true
    end  
    

    但是你的一般设置很奇怪,我不知道它是否只是命名但它没有多大意义。

    如果您想创建一个销售点系统或订单管理系统,您可以这样做:

    class Order
      belongs_to :customer
      has_many :line_items
      has_many :products, through: :line_items
      accepts_nested_attributes_for :line_items,
        allow_destroy: true,
        reject_if: -> { |li| li[:product_id].blank? || li[:quantity].blank? }
    end
    
    # columns:
    # - order_id [integer, index, foreign key]
    # - product_id [integer, index, foreign key]
    # - quantity [decimal or integer]
    # - price [decimal]
    # - subtotal [decimal]
    class LineItem
      belongs_to :order
      belongs_to :product
    end
    
    class Product
      has_many :line_items
      has_many :orders, through: :line_items
    end
    

    class OrdersController
    
      def new
      end
    
    
      def create
        @order = Order.new(order_params) do
          order.customer = current_user
        end
        if (@order.save)
    
        else
    
        end
      end
    
      private
      def order_params
        params.require(:order)
              .permit(:foo, :bar, line_item_attributes: [:product_id, :quantity])
      end
    end
    

    请注意,您应该只允许用户传递数量极其有限的参数 - 切勿从用户那里获取价格等信息。实际的定价逻辑应该在模型层完成。您也不想从订单模型中分离客户详细信息 - 否则每个重复订单都会重复数据。在添加搜索等附加功能之前,我会花一些时间来了解实际的域模型。

    【讨论】:

    • 如果您想跟踪库存,您应该使用其他型号 - 不要试图将其硬塞到上面。您需要一个跟踪位置(商店、分支机构等)的模型和一个跟踪某个位置的产品库存的模型。
    • @oklas 再次阅读。控制器白名单中有错字 - 不在模型中。你的评论甚至没有意义。
    猜你喜欢
    • 2018-04-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多