使用has_one关联和嵌套属性进行更新正在创建新的子对象而不是更新

时间:2014-01-19 07:20:49

标签: ruby-on-rails ruby-on-rails-4 simple-form has-one

下面的代码显示了Rails 4,cocoon gem和simple_form的以下设置。基本上,我在数据库中有很多客户端,我想通过选择下拉列表(通过电子邮件选择)将现有客户与发票相关联。当我通过电子邮件选择现有客户端时,会发生创建具有相同电子邮件的新客户端。此新客户端不与任何用户关联。为什么会这样?

编辑:我想我可能已经设置好了,以便客户端每http://requiremind.com/differences-between-has-one-and-belongs-to-in-ruby-on-rails/只能有一张发票,但仍应允许更新客户端子对象。

模型设置
    class Invoice<的ActiveRecord ::基

  belongs_to :user

  has_one :client

  has_many :invoice_items, dependent: :destroy

  accepts_nested_attributes_for :invoice_items, allow_destroy: true

  accepts_nested_attributes_for :client
end

class Client < ActiveRecord::Base

  belongs_to :user

  belongs_to :invoice

end

class User < ActiveRecord::Base

  has_many :clients, dependent: :destroy

  has_many :invoices, dependent: :destroy

end

_形成部分以通过编辑/更新

进行调用
= simple_form_for [current_user, @invoice],  html: { class: 'form-horizontal' } do |f|

  - if @invoice.errors.any?

    #error_explanation

      %h2= "#{pluralize(@invoice.errors.count, "error")} prohibited this invoice from being saved:"

      %ul

        - @invoice.errors.full_messages.each do |msg|

          %li= msg

  .form-group

    = f.label :paid, "Paid?"

    = f.input_field :paid

    %br/

  %h3 Client

  / = f.simple_fields_for :client, Client.new do |client|

  = f.simple_fields_for :client, Client.new do |client|

    = client.select :email, Client.all.map { |c| [c.email, c.email, { class: c.user.id }] }, include_blank: true

  = link_to "New Client", new_user_client_path(current_user)

  %h3 Invoice Items

  = f.simple_fields_for :invoice_items do |invoice_item|  

    = render 'invoice_item_fields', :f => invoice_item

  .links.form-group

    = link_to_add_association 'Add Invoice Item', f, :invoice_items, class: 'btn btn-primary'

  .actions

    = f.submit 'Submit'

发票控制器:

class InvoicesController < ApplicationController

  respond_to :json

  before_action :set_invoice, only: [:show, :edit, :update, :destroy]

  before_action :get_invoice_client, only: [:create, :update]

  before_filter :authenticate_user!



  # GET /invoices

  # GET /invoices.json

  def index

    @invoices = current_user.invoices

  end



  # GET /invoices/1

  # GET /invoices/1.json

  def show

  end



  # GET /invoices/new

  def new

    @invoice = current_user.invoices.build

    @invoice.client = @invoice.build_client

    @invoice_items = @invoice.invoice_items.build

  end



  # GET /invoices/1/edit

  def edit

  end



  # POST /invoices

  # POST /invoices.json

  def create

    @invoice = current_user.invoices.build(invoice_params)

    @invoice.client = @invoice_client

    respond_to do |format|

      if @invoice.save

        format.html { redirect_to [current_user,@invoice], notice: 'Invoice was successfully created.' }

        format.json { render action: 'show', status: :created, location: @invoice }

      else

        format.html { render action: 'new' }

        format.json { render json: @invoice.errors, status: :unprocessable_entity }

      end

    end

  end



  # PATCH/PUT /invoices/1

  # PATCH/PUT /invoices/1.json

  def update

    @invoice.client = @invoice_client

    binding.pry

    respond_to do |format|

      if @invoice.update(invoice_params)

        format.html { redirect_to [current_user, @invoice], notice: 'Invoice was successfully updated.' }

        format.json { head :no_content }

      else

        format.html { render action: 'edit' }

        format.json { render json: @invoice.errors, status: :unprocessable_entity }

      end

    end

  end



  # DELETE /invoices/1

  # DELETE /invoices/1.json

  def destroy

    @invoice.destroy

    respond_to do |format|

      format.html { redirect_to user_invoices_url(current_user) }

      format.json { head :no_content }

    end

  end



  private

    # Use callbacks to share common setup or constraints between actions.

    def set_invoice

      @invoice = current_user.invoices.find(params[:id])

      @invoice_items = @invoice.invoice_items

    end



    # Never trust parameters from the scary internet, only allow the white list through.

    def invoice_params

      params.require(:invoice).permit(:paid, :date_sent, :user_id, client_attributes: [:id, :email, :firstname, :lastname, :home_phone, :mobile_phone],

        invoice_items_attributes: [:id, :description, :line_total, :unit_cost, :quantity, :item, :invoice_id, :_destroy])

    end



    def get_invoice_client

      @invoice_client = Client.find_by_email(params[:invoice][:client_attributes][:email]) || nil

    end

end

2 个答案:

答案 0 :(得分:1)

尝试更改此行

= f.simple_fields_for :client, Client.new do |client|

= f.simple_fields_for :client, @invoice.client do |client|

答案 1 :(得分:0)

我不确定我理解客户端和用户之间的关系,但是如果您有一个现有的客户端表。 。

Class Client
  has_one :user

Class User
  belongs_to :client, :dependent => :destroy
  has_many :invoices, :dependent => :destroy
  has_many :invoice_items, :through => :invoices

Class Invoice
  belongs_to :user
  has_many :invoice_items, :dependent => :destroy

Class Invoice_item
  belongs_to :Invoice

您可以将客户端和用户表合并到一个表中,因为两者之间存在一对一关联,但您可以将其保留为上述。

如果您的主要表单是@client,那么

= simple_form_for @client do |f|
  ... f.client fields ...
  = f.simple_fields_for @user do |u|
    ... u.user fields ...
  = f.simple_fields_for @invoices do |i|
    ... i.invoice fields ...
    = f.simple_fields_for @invoice_items do |t|
      ... t.invoice_item fields ...

由于您使用的是Haml,因此缩进至关重要。如果将simple_field_for输出提取为单独的部分,则读取和管理将更加容易。我把它们排除在外,这样你就可以看到你的“嵌套”模型之间的关系了。

希望有所帮助。