【问题标题】:Rails nested form checkbox field not saving to my database modelRails 嵌套表单复选框字段未保存到我的数据库模型
【发布时间】:2019-12-13 18:21:16
【问题描述】:

我有两个模型列表和定价之间的模型关联。这些通过 Listing_Pricings 模型在多对多的基础上关联。

如果创建了列表,用户可以通过复选框字段选择我打算保存在单独模型 Listing_Pricings 中的三个定价选项之一。当用户选择定价选项时,我可以在 params 哈希中看到它,但它无法保存在我的数据库模型中。

请帮忙

我的控制器

class ListingsController < ApplicationController

    protect_from_forgery except: [:upload_photo]
    before_action :authenticate_user!, except: [:show]
    before_action :set_listing, except: [:new, :create]
    before_action :set_step, only: [:update, :edit]
    before_action :is_authorised, only: [:edit, :update, :upload_photo, :delete_photo]
    before_action :set_category, only: [:new, :edit, :show]

    def new
        @listing = current_user.listings.new
        @listing.listing_pricings
        @urgencies = Urgency.all
    end

    def create
      @listing = current_user.listings.new listing_params
      @listing.listing_pricings.first.listing_id = current_listing.id

      if @listing.save
        redirect_to edit_listing_path(@listing), notice: "Save..."
      else
        redirect_to request.referrer, flash: { error: @listing.errors.full_messages}
      end
  end

    def edit
      @urgencies = Urgency.all

      @listing = Listing.find(params[:id])

      @listing_category = @listing.category
      @category_pricings = @listing_category.pricings.all

      @listing_price = @listing.listing_pricings
    end

    def update

      @listing = Listing.find(params[:id])
      @listing_category = @listing.category

      #@category_pricings = @listing_category.pricings.all
      #@category = Category.find(params[:id])

      @category_pricings = @listing_category.pricings.paginate(page: params[:page], per_page: 5)



      @listing_pricing  = @listing.listing_pricings
      #@listing_price = @listing_pricing.first.pricing
      @listing_price = @listing.listing_pricings.build

      @urgencies = Urgency.all



      if @step == 2 && @listing.listing_pricings.each do |pricing|
        if @listing.has_single_pricing && !pricing.bronze?
          next;
        else
          if pricing[:listing_id].blank?
            #|| pricing[:description].blank? || pricing[:complete_by_date].blank? || pricing[:price].blank?
            return redirect_to request.referrer, flash: {error: "Invalid Pricing"}
            end
          end
        end
      end

      if @step == 3 && listing_params[:description].blank?
        return redirect_to request.referrer, flash: {error: "Description cannot be blank"}
      end

      if @step == 4 && @listing.photos.blank?
        return redirect_to request.referrer, flash: {error: "You don't have any photos"}
      end

      if @step == 5
        @listing_category_pricings.each do |pricing|
          if @listing.has_single_pricing || !pricing.bronze? || !pricing.silver? || !pricing.gold? || !pricing.platinum?
            next;
          else
            if pricing[:overview].blank? || pricing[:description].blank? || pricing[:complete_by_date].blank? || pricing[:price].blank?
              return redirect_to edit_listing_path(@listing, step: 2), flash: {error: "Invalid pricing"}
            end
          end
        end

        if @listing.description.blank?
          return redirect_to edit_listing_path(@listing, step: 3), flash: {error: "Description cannot be blank"}
        elsif @listing.photos.blank?
          return redirect_to edit_listing_path(@listing, step: 4), flash: {error: "You don't have any photos"}
        end
      end

      if @listing.update(listing_params)
        flash[:notice] = "Saved..."
      else
        return redirect_to request.referrer, flash: {error: @listing.errors.full_messages}
      end

      if @step < 5
        redirect_to edit_listing_path(@listing, step: @step + 1)
      else
        redirect_to users_dashboard_path
      end
    end

    def show
      @listing = Listing.find(params[:id])
      @listing_category = @listing.category
      @listing_category_pricings = @listing_category.pricings.all
      @urgencies = Urgency.all
    end

    def upload_photo
      @listing.photos.attach(params[:file])
      render json: { success: true}
    end


    def delete_photo
      @image = ActiveStorage::Attachment.find(params[:photo_id])
      @image.purge
      redirect_to edit_listing_path(@listing, step: 4)
    end

    def set_pricing_id
      Listing.update_all({pricing_id: true}, {id: params[:listing_id]} )

    end


    private

    def set_step
      @step = params[:step].to_i > 0 ? params[:step].to_i : 1
      if @step > 5
        @step = 5
      end
    end

    def set_category
      @categories = Category.all
    end

    def set_listing
      @listing = Listing.find(params[:id])
    end

    def is_authorised
      redirect_to root_path, alert: "You do not have permission" unless current_user.id == @listing.user_id
    end

    def listing_params
      params.require(:listing).permit(:title, :video, :description, :active, :category_id, :budget, :urgency_id, :has_single_pricing,:pricing_id)
    end

    def category_params
      params.require(:category).permit(:name)
    end

  end

Listing.rb

class Listing < ApplicationRecord
  belongs_to :user
  belongs_to :category
  belongs_to :urgency




  has_many :listing_pricings, dependent: :destroy
  has_many :pricings, through: :listing_pricings


  has_many_attached :photos
  has_rich_text :description

  validates :title, presence: { message: 'cannot be blank' }



  #has_many :pricings
  #accepts_nested_attributes_for :pricings
  #has_many :listing_categories
end

定价.rb

class Pricing < ApplicationRecord

  belongs_to :category, optional: false

  has_many :listing_pricings
  has_many :listings, through: :listing_pricings

  enum pricing_type: [:bronze, :silver, :gold, :platinum]
end

ListingPricing.rb

class ListingPricing < ApplicationRecord

  belongs_to :listing, optional: true, dependent: :destroy

  belongs_to :pricing, optional: true, dependent: :destroy
end

我的观点

<div class="step-content <%= 'is-active' if @step == 2 %>">
    <div class="field">
        <div class="control">

              <div class="tile is-ancestor">
                            <% @category_pricings.each do |cp| %>

                              <div class="tile is-parent">
                                <article class="tile is-child box">
                                        <div class="subtitle"><%= "#{cp.overview}" %></div>
                                        <div class="content"><%= "Deposit payable: £#{cp.price}" %></div>
                                        <div class="content"><%= "Time to complete: #{pluralize(cp.complete_by_date, 'Day')}" %></div>
                                        <tr valign="bottom"><div class="content"><%= "#{cp.description}" %></div></tr>
                                        <tr valign="bottom"><div class="content"><%= "#{cp.id}" %></div></tr>

                                    <%= f.fields_for :listing_pricings do |lp| %>
                                      <%= hidden_field_tag "pricing_id[]", cp.id %>
                                      <div class="form-group">
                                        <%= lp.check_box_tag :pricing_id, cp.id %>
                                      </div>
                                    <% end %>

                                </article>
                              </div>
                            <% end %>
              </div>
      </div>
  </div>
</div>

我的错误跟踪显示 PG::NotNullViolation:错误:“pricing_id”列中的空值违反非空约束详细信息:失败行包含 (16, 2, null, 2019-12-13 17:51:50.722906, 2019-12-13 17:51 :50.722906)。

大家有什么想法吗?

Parameters:

{"utf8"=>"✓",
 "_method"=>"patch",
 "authenticity_token"=>"tOnX6Q5YjHgXn3Xk5Wh2NioPfLrziiPVwyHkLF8BBFOjuWHdM1w8A7AdGpHdFGR3n+zlFsN2B/3IOMenXU1daA==",
 "step"=>"2",
 "listing"=>{"title"=>"Please clean my home", "category_id"=>"3", "urgency_id"=>"10", "has_single_pricing"=>"0", "description"=>"<div>This is my second listing&nbsp;</div>", "video"=>""},
 "pricing_id"=>"1",
 "commit"=>"Save & Continue",
 "id"=>"2"}

【问题讨论】:

  • 是的,只需使用pricing_ids= 而不是accepts_nested_attributes。我找不到一个好的欺骗目标,但几乎每天都会问同样的问题。 stackoverflow.com/questions/59270108/…
  • 这个问题是关于 HABTM 关联的,但同样适用于has_many through:。要分配一个关联(在连接表中创建行),只需传递一个 id 数组。 Listing.new(pricing_ids: [1,2,3])。这就是表单集合助手所做的。您不需要接受嵌套属性或 fields_for。您只需要表单上的选择/复选框和参数白名单中的permit(:foo, :bar, pricing_ids: [])
  • 不会解决问题,但是你这样做了accepts_nested_attributes_for :listing_pricings, allow_destroy: true, reject_if: proc { |att| att['name'].blank? },你没有将name列入params.require(:listing).permit(:title, :video, :description, :active, :category_id, :budget, :urgency_id, :has_single_pricing, listing_pricings_attributes: [:id, :listing_id, :pricing_id])的白名单
  • @Tun 感谢您的回复,但此解决方案并未成功更新我的 ListingPricing 模型
  • 也感谢@max,我也试过你的建议,但没有运气。我添加了我仍然收到的错误

标签: ruby-on-rails ruby ruby-on-rails-5


【解决方案1】:

我这样做是为了我的一个项目。我用PricingListingListPricing 替换我的模型。

在您的模型中,您必须在has_many ... through 关系上有dependent: :destroy(否则,取消选中复选框将不起作用)。

# app/model/listing.rb
class Listing < ApplicationRecord
  has_many :listing_pricings
  has_many :pricings, through: :listing_pricings, dependent: :destroy

  # your code
end

在您看来

- Pricing.all.each do |p|
  = f.check_box :pricing_ids, { multiple: true }, p.id, false
  = f.label :pricing_id, p.something
end

在列表控制器中,确保您在列表控制器中允许:pricing_ids

# app/controllers/listings_controller.rb

def listing_param
  params.require(:listing).permit(
    # other params you permit,
    pricing_ids: []
  )
end

【讨论】:

  • 你好@Tun 非常感谢您对此的意见。不幸的是,您的解决方案似乎对我不起作用。如果我的二级模型数据没有被保存,我仍然会受到侵犯。我添加了错误调试
  • 我在您的(更新的?)问题的参数中看不到pricing_ids
  • 我已尝试在我的参数白名单中添加 :pricing_ids。我奇怪地得到 Unpermitted parameter: :pricing_ids 并且不明白为什么
猜你喜欢
  • 2016-01-15
  • 2012-10-19
  • 2011-01-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-14
相关资源
最近更新 更多