0

我是一名 Rails 新手,正在构建一个小应用程序来帮助我的工作。

我有客户端、站点和报价模型和控制器,并设置了视图。

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

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

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

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

我做了一些研究并从@juanpastas 那里学到了这一点

我的表单看起来像这样引号/视图/_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

加法

您可能会注意到我试图扩展,以便在站点和站点中调用客户端,并在报价中调用客户端。

4

1 回答 1

1

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

有两个问题可能会阻止您的表单保存。

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

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

    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

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

干杯

编辑:上面的答案仍然与那些想要保存多条记录的人相关,但是因为你说你只想保存一条记录,所以我更新了答案:

我上面总结的逻辑大致相同。

您目前似乎不理解,并且(IMO)对于理解 Rails 应用程序非常重要的是表单映射到控制器和控制器映射到数据库的方式。方法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_clientfor:client_id:quote_sitefor :site_id

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

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

使用 Rails MODELNAME_params 方法(我们称之为强参数 -> 阅读它!http ://edgeguides.rubyonrails.org/action_controller_overview.html )时要记住的重要一点是,您的表单和许可操作都应该完全像它们在数据库中,否则数据库将无法理解并且您的记录将无法正确保存。

我希望通过这个编辑你会弄明白。

干杯

于 2017-03-13T00:50:54.210 回答