Rails表单collection_select没有保存到数据库

时间:2017-03-12 04:10:51

标签: ruby-on-rails ruby collection-select

我是一个铁杆新手,并建立一个小应用程序来帮助我的工作。

我有客户端,网站和报价模型以及设置了视图的控制器。

我在quote模型上创建了一个表单,该表单从collection_select字段中的其他两个模型中提取数据。我找到的有关rails的collection_select的文档非常糟糕。我想获取客户名称和站点名称,并在报价上关联/显示名称。

我已在表单中进行了设置,但它不保存数据或显示数据。

我真的想了解collection_select的输入,因为我确信我的错误可能导致问题。

<%= f.collection_select :client, Client.all, :quote_client, :client_name , {:prompt => "Please select a client for the site"} %>

我做了一些研究,并从@juanpastas here

中学到了这一点

我的表格看起来像这样 报价/视图/ _form.html

<%= form_for(quote) do |f| %> 
    <% if quote.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(quote.errors.count, "error") %> prohibited this quote from being saved:</h2><ul>
      <% quote.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %><div class="field">
    <%= f.label :client %>
    <%= f.collection_select :client, Client.all, :quote_client, :client_name , {:prompt => "Please select a client for the site"} %>
  </div><div class="field">
    <%= f.label :site_name %>
    <%= f.collection_select :site, Site.all, :quote_site, :site_name , {:prompt => "Please select a site for the quote"} %>
  </div><div class="field">
    <%= f.label :quote_contact %>
    <%= f.text_field :quote_contact %>
  </div><div class="field">
    <%= f.label :quote_value %>
    <%= f.text_field :quote_value %>
  </div><div class="field">
    <%= f.label :quote_description %>
    <%= f.text_field :quote_description %>
  </div><div class="actions">
    <%= f.submit %>
  </div>
<% end %>

修改

答案/澄清

报价只能有一个客户和一个网站。该网站也必须属于客户。

我有一个客户端列表,通过Client.all从客户端模型调用,以及通过站点模型通过Site.all调用的站点列表。我只需要一个客户的名称和每个报价的一个站点,但希望能够以级联方式进行选择。选择客户端,然后从客户端可用的站点中选择站点。

三种模型之间建立了关系:

 class Quote < ApplicationRecord


            belongs_to :site, optional: true
            belongs_to :client, optional: true
            has_and_belongs_to_many :assets
    end

class Site < ApplicationRecord


    has_attached_file :site_image, styles: { small: "64x64", med: "100x100", large: "200x200" }
    do_not_validate_attachment_file_type :site_image


    belongs_to :client , optional: true
    has_and_belongs_to_many :assets
    has_and_belongs_to_many :quotes
end

class Client < ApplicationRecord

    has_and_belongs_to_many :sites
    has_and_belongs_to_many :assets
    has_and_belongs_to_many :quotes
end

控制器

class QuotesController < ApplicationController
  before_action :set_quote, only: [:show, :edit, :update, :destroy]

  # GET /quotes
  # GET /quotes.json
  def index
  @quotes = Quote.all
  end

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

  # GET /quotes/new
  def new
    @quote = Quote.new
  end

  # GET /quotes/1/edit
  def edit
  end

  # POST /quotes
  # POST /quotes.json
  def create
    @quote = Quote.new(quote_params)

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

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

  # DELETE /quotes/1
  # DELETE /quotes/1.json
  def destroy
    @quote.destroy
    respond_to do |format|
      format.html { redirect_to quotes_url, notice: 'Quote was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

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

    # Never trust parameters from the scary internet, only allow the white list through.
    def quote_params
      params.require(:quote).permit(:quote_client, :quote_site, :client_name, :site_name, :quote_contact, :quote_value, :quote_description)
    end
end


class SitesController < ApplicationController
  before_action :set_site, only: [:show, :edit, :update, :destroy]

  # GET /sites
  # GET /sites.json
  def index
    @sites = Site.all
    @clients = Client.all
  end

  # GET /sites/1
  # GET /sites/1.json
  def show
      @sites = Site.all
      @clients = Client.all
  end

  # GET /sites/new
  def new
    @site = Site.new
  end

  # GET /sites/1/edit
  def edit
  end

  # POST /sites
  # POST /sites.json
  def create
    @site = Site.new(site_params)

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

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

  # DELETE /sites/1
  # DELETE /sites/1.json
  def destroy
    @site.destroy
    respond_to do |format|
      format.html { redirect_to sites_url, notice: 'Site was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

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

    # Never trust parameters from the scary internet, only allow the white list through.
    def site_params
      params.require(:site).permit(:site_client, :client_name, :site_name, :site_image, :site_address, :site_contact)
    end
end

class ClientsController < ApplicationController
  before_action :set_client, only: [:show, :edit, :update, :destroy]

  # GET /clients
  # GET /clients.json
  def index
    @clients = Client.all
    @sites = Site.all
  end

  # GET /clients/1
  # GET /clients/1.json
  def show
      @clients = Client.all
      @sites = Site.all
  end

  # GET /clients/new
  def new
    @client = Client.new
  end

  # GET /clients/1/edit
  def edit
  end

  # POST /clients
  # POST /clients.json
  def create
    @client = Client.new(client_params)

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

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

  # DELETE /clients/1
  # DELETE /clients/1.json
  def destroy
    @client.destroy
    respond_to do |format|
      format.html { redirect_to clients_url, notice: 'Client was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

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

    # Never trust parameters from the scary internet, only allow the white list through.
    def client_params
      params.require(:client).permit(:client_name, :client_address, :client_phone, :client_email, :client_website)
    end
end

附加

您可能会注意到我已尝试进行扩展,以便在网站中调用客户端,并在报价中调用客户端。

1 个答案:

答案 0 :(得分:1)

首先:我假设您在三个模型之间建立了关系!从报价到客户以及从报价到网站必须有一个很好的关系。

有两个问题可能导致您的表单无法保存。

首先是你如何创建collection_select。集合选择中的第三个参数是将发送到控制器的内容。这应该是一个ID数组(我假设一个引用可以有多个客户端)。我看你叫它:quote_client。我将其重命名为:client_ids。最后,这是您要发送给控制器的内容:一系列ID。

你需要照顾的第二件事是你的控制器。如果您共享控制器代码会很好,但我假设您有一个带有quote_params方法的quotes_controller。它可能看起来像这样:

    def quote_params
      params.require(:quote).permit(:quote_contact, etc., etc.)
    end

此控制器方法必须使用form_for进行响应,因此form_for中的每个字段(如quote_contact)都应该在许可证中,否则将无法保存。如果要保存ID数组,则必须告诉此方法您需要一组ID。您可以这样做:client_ids: []

所以你的新quote_params方法应如下所示:

def quote_params
  params.require(:quote).permit(:quote_contact, client_ids: [], site_ids: [], all_other_fields...)
end

我希望这个答案能为您提供急需的帮助。如果我需要澄清更多:只要问:)

干杯

编辑:上面的答案仍然适用于那些想要保存多条记录的人,但是因为你声明你只想在这里保存一条记录,这是我更新的答案:

我总结的逻辑大致保持不变。

目前您似乎并不理解,对于理解Rails应用程序而言(IMO)至关重要的是表单映射到控制器和控制器映射到数据库的方式。如上所述,方法quote_params应允许您要保存到数据库的表单中的所有字段。这意味着许可证部分中的所有字段都应该在您的数据库中,否则它们将无法保存。如果仔细查看数据库中的引用表,您将看到它包含client_id和site_id的字段。这两个字段包含引用/客户和引用/站点关联的引用。这就是为什么你的许可证目前无效,因为你有quote_client和quote_site。数据库没有quote_client或quote_site,因此在尝试保存时不会更新关联。数据库确实有client_id和site_id,因此您应该将这些内容传递到quote params方法中。

这当然应该与form_for中的字段相对应。因此,您需要更改两件事才能使其发挥作用:

  1. 更改您的两个collection_selects,并为:quote_client交换:client_id,为:quote_site交换:site_id

  2. 更改控制器方法以反映form_for中的更改。在这里你还需要为quote_id和site_id交换quote_site和quote_client,如下所示:

    def quote_params params.require(:quote).permit(:client_id, :site_id, etc.) end

  3. 使用Rails MODELNAME_params方法时要记住的重要事项(我们称之为强参数 - &gt; READ IT!http://edgeguides.rubyonrails.org/action_controller_overview.html) 是你的表单和许可证操作都应该列出与数据库中的字段完全相同的字段,否则数据库将无法理解,并且您的记录将无法正确保存。

    我希望通过这次编辑,你会弄明白的。

    干杯