【问题标题】:Polymorphic Associaton transaction fails in Rails 5Rails 5 中的多态关联事务失败
【发布时间】:2017-07-01 20:34:51
【问题描述】:

我无法将我的关联保存在 localhost:3000/controller_name/new 中。我相信这是由于belongs_tofailing validation,但我不确定如何解决它(除了通过requires:false/optional: true 删除关联,我不确定其后果)。我在 tutorial 之后创建了我的关联,但它是针对以前版本的 rails 的。

我有一个多态地址表,可以属于事件、企业、用户等。我正在尝试将其添加到事件中。

地址迁移——可以看到引用addressable

class CreateAddresses < ActiveRecord::Migration[5.1]
  def change
    create_table :addresses do |t|     
      t.string :address

      t.decimal :latitude,  null: false, precision: 10, scale: 6, index: true
      t.decimal :longitude, null: false, precision: 10, scale: 6, index: true

      t.references :addressable, polymorphic: true, index: true 
    end
  end
end

地址模型:

class Address < ApplicationRecord
    belongs_to :addressable, polymorphic: true
end

事件模型:

class Event < ApplicationRecord
    has_one :address, as: :addressable
    accepts_nested_attributes_for :address
end

事件控制器:

class EventsController < ApplicationController
  #...stuff...

  # GET /events/new
  def new
    @event = Event.new
    @event.address = @event.build_address
    #@event.address = Address.new(addressable: @event)
    #@event.address = @event.create_address
    #@event.address =  @addressable.User.new
  end

  #...stuff...

您可以看到我尝试了多种方法来创建事件的地址,它们主要创建以下项目,使用 addressable 的项目导致 Nil 崩溃。

#<Address id: nil, address: nil, latitude: nil, longitude: nil, addressable_type: "Event", addressable_id: nil>

事件表单(使用 Simple_form gem):

<%= simple_form_for @event do |f| %>
  <%= f.input :name %>
  <%= f.input :description %>

  <%= f.simple_fields_for :address do |address| %>
    <%= render :partial => 'shared/address/form', :locals => {:f => address} %>
  <% end %>

  <%= f.button :submit %>
<% end %>

地址形式部分:

<!-- Google Maps Must be loaded -->
<% content_for :head do %>
    <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCMh8-5D3mJSXspmJrhSTtt0ToGiA-JLBc&libraries=places"></script>
<% end %>

<div id="map"></div>

<%= f.input :address %>
<%= f.input :latitude %>
<%= f.input :longitude %>

表单渲染良好。当我尝试保存时,我得到了

Started POST "/events" for 127.0.0.1 at 2017-07-01 16:06:23 -0400
Processing by EventsController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"R0zlSs3UUNV3x8sQn5ocmE4jP12uOsFza7FezBAuhP4sw2MhF1OhixF8sAfDsLpfMEX7x5rhJ9HZfbKna8ncEA==", "event"=>{"name"=>"asd", "description"
=>"asd", "address_attributes"=>{"address"=>"asd", "latitude"=>"1", "longitude"=>"1"}}, "commit"=>"Create Event"}
   (0.1ms)  begin transaction
   (0.1ms)  rollback transaction

我一直在new 页面上。如果我将byebug 插入create 并打印出@event.errors 它会显示:

#<ActiveModel::Errors:0x007fb29c34a9a8 @base=#<Event id: nil, name: "asd", description: "asd", min_users: nil, max_users: nil, start_time: nil, recurring: nil, created_at: nil, upd
ated_at: nil>, @messages={:"address.addressable"=>["must exist"]}, @details={:"address.addressable"=>[{:error=>:blank}]}>

如何创建 address.addressable?正如一些 SO 答案所暗示的那样,关闭需要验证的后果是什么?

【问题讨论】:

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


    【解决方案1】:

    运行 rake db:schema:dump,然后检查 db/schema.rb 中的文件,确保您有以下 2 个字段 * t.integer :addressable_id, * t.string :addressable_type 有关更多详细信息,这里是有关 Activerecord Polymorphic Associations 的链接,如果您的架构有问题,那么您可以按照以下方式运行迁移

    t.references :addressable, polymorphic: true, index: true
    

    由于事件通过多态关联有多个地址,您可以使用this link创建地址 以下是示例代码

    @address = @event.addresses.build(attributes = {}, ...)
    

    你可以使用@address 而不是@event.address

    【讨论】:

    • 我还不能测试它,但我知道地址确实有可寻址类型,我会加入其中。我在原文中包含了使用@event.build_address 的输出。我认为问题在于这会使 Id 为空?
    • 如果你已经设置了连接,例如 has_one 或 has_many 当你使用@event.addresses.build(rails提供的方法)时,它会自动处理id,你不必插入/手动输入
    • 当我在事件控制器的new 函数中尝试`@address = @event.address.build` 时,我得到了undefined method 'build' for nil:NilClass。和undefined method 'addresses' for #&lt;Event:0x007f3f288c0820&gt; Did you mean? address address=@event.address.build@event.build_address 有效,但在传递给我的表单 {"id"=&gt;nil, "address"=&gt;nil, "latitude"=&gt;nil, "longitude"=&gt;nil, "addressable_type"=&gt;"Event", "addressable_id"=&gt;nil} 时具有这些属性。 IDK 为什么 id 不存在。
    • 你必须使用create来创建@事件!不是新的,所以之前的命令 @event = Event.create!(event_params....) 这样你的 @event 将首先被创建并有一个 id 见 guides.rubyonrails.org/active_record_basics.html
    • 这件事发生在我之前,你必须找出导致验证失败的原因,我相信你在事件模型中有一些验证规则,那么你应该追踪逻辑,以使创建不失败,即我做的一个
    【解决方案2】:

    发现了这些问题并写了一封blog post 深入讨论这个问题。基本上我遇到了两个不同的问题

    1.我收到的错误 - "address.addressable"=&gt;["must exist"]。 address.addressable 是“父”表的元组 ID。就我而言,它是事件 ID。当我们尝试将其保存在控制器的创建函数中时,此错误试图告诉我新地址中缺少该外键。就像我在问题中所说,您可以设置optional:true 忽略此问题,并且在保存事件后它会以某种方式神奇地填充。或者您可以在保存之前在创建函数中手动分配它。

    def create
      @event = Event.new(event_params)
      @event.address.addressable = @event #<<<<<<<<<<< manually assign address.addressable
      respond_to do |format|
        if @event.save #Saves the event. Addressable has something to reference now, but validation does not know this in time
          format.html { redirect_to @event, notice: 'Event was successfully created.' }
          format.json { render :show, status: :created, location: @event }
        else
          #dont forget to remove the 'byebug' that was here
          format.html { render :new }
          format.json { render json: @event.errors, status: :unprocessable_entity }
        end
      end
    end
    

    2.Event ID 是一个字符串。在我的地址迁移中,我使用了t.references :addressable, polymorphic: true, index: true,它是整数类型和addressable_id 的别名。我需要更改我的迁移以使用字符串 ID - 所以删除 references 行并添加它。

    t.string :addressable_id, index: true
    t.string :addressable_type, index: true
    

    这在博文中有更详细的介绍。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多