【问题标题】:Rails 4 RC1 polymorphic association unable to createRails 4 RC1多态关联无法创建
【发布时间】:2013-05-14 19:53:02
【问题描述】:

第三次更新:好的,我想现在我很接近了。我现在正在创建数据,并且正在数据库中设置参数。我将业务控制器强参数更改为:

def business_params
      params.require(:business).permit(:name, :description, :address_attributes=>[:line1, :line2, :city, :state, :zip])

当我编辑现有业务时,出于某种原因,我创建了两个地址。一个是空的,一个是完整的参数。所以,如果有人有最后的推动力,我认为我非常接近:)。

地址模型

class Address < ActiveRecord::Base
belongs_to :addressable, polymorphic: true
end

商业模式:

    class Business < ActiveRecord::Base

     #each business belongs to user, through user_id
      belongs_to :owner
      has_one :address, as: :addressable
      accepts_nested_attributes_for :address

      # each business has many customers and has many services
      has_many :customers
      has_many :services

    validates :owner_id, presence:true
    validates_presence_of :name
    #validates_length_of :state, is: 2

end

业务负责人:

class BusinessesController < ApplicationController
  before_action :get_owner
  before_action :set_business, only: [:show, :edit, :update, :destroy]
  #helper_method :sort_column, :sort_direction


  def index
    @businesses = @owner.businesses
  end

  def show
    #@customer = @business.customers.order(sort_column + " " + sort_direction)
  end

  def new
    @owner = Owner.find(params[:owner_id])
    @business = @owner.businesses.build
    @address = @business.build_address(params[:address])

  end

  def edit
    @address = @business.build_address(params[:address])
  end

  def create
    @business = @owner.businesses.new(business_params)
    @address = @business.create_address(params[:address])

    respond_to do |format|
      if @business.save
        format.html { redirect_to owner_businesses_url(@owner), notice: 'Business was successfully created.' }
        format.json { render action: 'show', status: :created, location: @business }
      else
        format.html { render action: 'new' }
        format.json { render json: @business.errors, status: :unprocessable_entity }
      end
    end
  end

  def update
    @address = @business.create_address(params[:address])
    respond_to do |format|
      if @business.update(business_params)
        format.html { redirect_to owner_businesses_url(@owner), notice: 'Business was successfully updated.' }
        format.json { head :no_content }
      else
        format.html { render action: 'edit' }
        format.json { render json: @business.errors, status: :unprocessable_entity }
      end
    end
  end

  def destroy
    @business.destroy
    respond_to do |format|
      format.html { redirect_to owner_businesses_url(@owner) }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_business
      @business = @owner.businesses.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def business_params
      params.require(:business).permit(:name, :description, :address_attributes=>[:line1, :line2, :city, :state, :zip])
    end

    def get_owner
      @owner = Owner.find(params[:owner_id])
    end

    #def sort_column
    #  Customer.column_names.include?(params[:sort]) ? params[:sort] : "first_name"    
    #end

    #def sort_direction
    #  %w[asc desc].include?(params[:direction]) ? params[:direction] : "asc"
    #end
  end

表单视图:

        <%= form_for([@owner, @business]) do |f| %>
      <% if @business.errors.any? %>
        <div id="error_explanation">
          <h2><%= pluralize(@business.errors.count, "error") %> prohibited this business from being saved:</h2>

      <ul>
      <% @business.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :name %><br />
    <%= f.text_field :name %>
  </div>
  <div class="field">
    <%= f.label :description %><br />
    <%= f.text_area :description %>
  </div>

  <%= f.fields_for :address do |address| %>
  <%= p address.object %>
  <div class="field">
    <%= address.label :line1 %><br>
    <%= address.text_field :line1 %>
  </div>
  <div class="field">
    <%= address.label :line2 %><br>
    <%= address.text_field :line2 %>
  </div>
  <div class="field">
    <%= address.label :city %><br>
    <%= address.text_field :city %>
  </div>
  <div class="field">
    <%= address.label :state %><br>
    <%= address.text_field :state %>
  </div>
  <div class="field">
    <%= address.label :zip %><br>
    <%= address.number_field :zip %>
  </div>

  <% end %>

  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

现在我正在创建两个地址。一个是空的,一个是带参数的。快到了:)。

【问题讨论】:

    标签: ruby-on-rails ruby-on-rails-4


    【解决方案1】:

    有些事情告诉我,我几乎是个白痴,因为我很难让多态关联在我正在开发的 Rails 4.0 RC1 应用程序中工作。作为一个白痴,我确实要求 Stack Overflow 帮助解决我看到的问题,并转发了 Ruby on Rails 的 Google Group 的问题。没有答案,因此,这个问题成为我自己有效解决的第一个问题。

    这样很好,对吧?

    无论如何,我的问题很简单。我希望在企业、客户、所有者等之间共享街道/邮寄/帐单地址。我不想编写三个不同的表来做同样的事情。多态关联似乎是我从此类关联中收集到的方法。我四处寻找这方面的指导,并且确实有一些东西可以模仿。

    http://kconrails.com/2010/10/19/common-addresses-using-polymorphism-and-nested-attributes-in-rails/>这个网站很有用,虽然过时了。我正在使用 Rails 4。我相信那是在 Rails 2 的末尾写的。它确实帮助我缩短了时间,因为我以前从未做过 has_one 关联,而且我最初的问题是在 Rails 控制台中,我无法执行以下操作:

    b = Owner.first.businesses.first
    b.address.create!(params n' stuff)
    

    我在创建时不断收到 no_method 错误。那是我真正专注于上面的文章并意识到他也没有使用 create 的时候。他正在使用:

    b = Owner.first.businesses.first
    b.build_address(params n' stuff) -- he actually wrote @customer.build_address
    

    build_association 是 has_one 的东西。我现在非常清楚这一点。当然,我本可以提前阅读文档,但是,我在此期间阅读了它们。如果您有兴趣,可以通过http://guides.rubyonrails.org/association_basics.html#has_one-association-reference 有效地阅读此内容。

    我非常仔细地遵循该文档,但它并不完全正确。我的业务控制器 DID 的新方法中的“build_address”项允许显示我的表单中的 fields_for。但是,没有任何东西被写入数据库。正在为所有 nil 值的地址输入一条空记录,并且根本没有附加到为其添加它的业务。而且,显然,如果要实际“编辑”业务而不是创建新业务,您将根本看不到地址表单,因此我认为我们至少需要在编辑字段中使用 build_address 方法。

    我是对的,但是仍然没有保存任何内容。

    我一如既往地去 Railscasts 看看 Ryan 有什么。而且,http://http://railscasts.com/episodes/154-polymorphic-association-revised>他有一些多态的东西,当我为不同的模型添加 cmets 时会很棒。但这对我想在这里做的事情没有直接的好处。我在 Stack Overflow 上阅读了很多。检查了 Github 上 Rails 存储库中的问题列表。阅读 Rails Google 组。没有什么能真正说明我所看到的。

    很大程度上是因为我看到了 Rails 4 问题。

    Rails 4 内置了强大的参数。因此我在地址控制器中添加了以下内容来解决这个问题:

    # Never trust parameters from the scary internet, only allow the white list through.
    def address_params
      params.require(:address).permit(:line1, :line2, :city, :state, :zip)
    end
    

    如果您为此使用 rails g scaffold,那么在 Rails 4 中,这就是脚手架自然会为您制作的内容。强参数替换了以前版本的 Rails 模型中的 attr_accessible。因此,该模型只是对地址和可寻址的多态关联的引用,并且业务添加了 has_one :address,as:addressable 以及accepts_nested_attributes_for :address。这一切都很好,但仍然没有任何东西被保存。我什至在 appliction_controller.rb 中添加了 address_params 哈希。

    我现在意识到我很愚蠢。地址的添加发生在业务控制器中,并且永远不会看到此参数哈希。即使在 application_controller.rb 中也没有任何改变。所以不可能是这样的。

    我看到参数在日志中被传递,并且尝试插入,但对“地址”的未经许可的参数引用不断显示。

    我现在正在寻求帮助并尝试对其进行调整。

    我看到 https://github.com/rails/strong_parameters#nested-parameters> 这部分在 github 的强参数存储库中。我不明白为什么 Accepts_nested_attributes_for 不会为我执行此操作,但我进入业务控制器并将那里的 params 哈希调整为如下所示:

    # Never trust parameters from the scary internet, only allow the white list through.
    def business_params
      params.require(:business).permit(:name, :description, :address_attributes => [:line1, :line2, :city, :state, :zip])
    end
    

    我读过的一篇文章说 has_one 关联传递了一个 :address_attributes,所以我把它放在那里。现在情况不同了。数据仍未保存,但我没有收到任何未经许可的错误。在帮助站点上仍然没有答案。

    我决定调整我的新、编辑、创建和更新方法。

    在新建和编辑中我有:

    @business.build_address
    

    我在 new 和 edit 中更改了它并添加以创建和更新以下内容:

    @business.build_address(params[:address]) -- NOTE, :address_attributes also appears to work there.
    

    现在正在保存数据。所有字段都进入数据库。问题是两个人。一个是空的,一个在每次新的和每次更新时都是完整的。因此,我从业务控制器中的创建和更新方法中删除了上面的代码。现在我创建了一个地址。正确。自我感觉良好,我打开公司编辑地址,得到一个空白地址表。没有任何东西被传回表单视图。

    因此,我将业务控制器中的编辑方法更改为如下所示:

    @business.address ||= @business.build_address(params[:address])

    这基本上是说如果有@business.address,则显示它,否则将构建@business.address。

    现在我正在滚动。我现在在编辑表单时看到了保存的地址。我编辑地址。编辑被保存并再次显示。但是与此相关的旧地址仍然存在。 addressable_id 简单地更改为 nil 并写入新的地址记录。那不是我想要的。仍然没有来自帮助站点的帮助。日志中没有任何内容。

    我认为这里应该使用更新方法。所以我在业务控制器的更新方法中加入了下面这行:

    @business.address.update(params[:address])

    没有行为改变,但现在日志显示一个新的未经允许的参数。

    它将“ID”显示为未经许可。因此,我将业务控制器参数哈希更改为:

    # Never trust parameters from the scary internet, only allow the white list through.
    def address_params
      params.require(:address).permit(:id, :line1, :line2, :city, :state, :zip)
    end
    

    然后,一切正常。我可以创建一个有地址的企业。事实上,即使我没有为地址填写任何内容,也会创建地址(此时没有验证)。我可以更新该地址,无论是完整的还是空的,它都会更新现有记录。有用。我不确定这是最好还是最干净的方法,但我会继续寻找。

    存在一个问题,如果我手动删除与企业关联的已创建地址,然后尝试编辑该企业,则我在上面添加的更新方法会引发错误,因为没有要更新的记录。没有那个我大概可以活下去。我认为我可以为每个企业(以及最终的用户、所有者、客户等)创建记录,即使它是空的,然后只允许通过关联而不是直接删除。

    但我可能会调整代码以找到一种仅创建完整地址而不是将其写为空的方法。

    无论如何,目前情况还不错。

    我很想知道是否有什么可以改进或应该改变的。

    现在,我开始做下一件事。

    【讨论】:

    • 很好的答案!我遇到了另一个与多态 has_one 关联有关的问题:InverseOfAssociationNotFoundError。我会尝试使用您的解决方案,但我几乎相信它不会对我的问题产生影响。 :S 我的代码看起来和你的一样>.
    【解决方案2】:

    在您的控制器中,您需要建立模型之间的关系,所以不要:

     @business = @owner.businesses.new(business_params)
    

    试试

     @business = @owner.businesses.build(business_params)
    

    【讨论】:

    • 谢谢道奇。这就是错误所在,但是您指出的特定行已经起作用。不工作的是地址部分。 @business.build_address(params[:address]) 我认为是必要的。我现在有一条未经许可的参数消息,这与以前不同。 @business.address.create 方法用于 has_many 关系。
    • 所以,我做了更多的改变并且接近了。我现在得到所有地址字段的未经许可的参数。我使用 Rails 4.0 的强参数将“address_attributes => {} 作为 business_params 的一部分。这样我看到哈希传递和输入,但由于无法使用这些属性而被拒绝。在地址控制器中,我将 address_params 从私有方法中移出。但我认为它永远不会到达那里,所以我必须将它移到某个地方的助手。
    • 您是否已将 attr_accessible :address_attributes 添加到需要访问这些属性的模型中?
    • 这是 Rails 4 道奇。所以它使用强参数。我在 application_controller.rb 和地址控制器本身的“私有”之外有强大的地址参数。我猜这将与我正在构建“:地址”的视图或业务控制器中的某些东西有关,在那里我可能通过 address_attributes=>[] 而不是 address_attributes=>{} 就像在 4.0 中一样'认为它不像 3.0 那样接受哈希作为属性。
    • 哦,我明白了,我需要对 Rails 4 进行更多研究!
    猜你喜欢
    • 2016-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多