您好我有一个表格包,我填写表格并使用参数。此表有一个FK到另一个表位置,用于保存包的lat,lng和地址。 Location表使用GeoKit。
我的表单包含包的字段和允许用户输入位置名称的字段。 Google地图可帮助用户使用自动填充功能填写详细信息,并将结果以json的形式保存在隐藏字段中。
我正在尝试使用强大的参数
private
def package_params
params.require(:package).permit( :state, :delivery_date, :length, :height, :width, :weight, destination: [:id, :address, :lat, :lng], origin: [:id, :address, :lat, :lng] )
end
我也试过
private
def package_params
params.require(:package).permit( :state, :delivery_date, :length, :height, :width, :weight, destination_attributes: [:id, :address, :lat, :lng], origin_attributes: [:id, :address, :lat, :lng] )
end
但是起源&目标_attributes不再在params的包对象中传递。
包模型
class Package < ActiveRecord::Base
belongs_to :user
has_many :bids, dependent: :destroy
belongs_to :origin, :class_name => 'Location', :foreign_key => 'origin'
belongs_to :destination, :class_name => 'Location', :foreign_key => 'destination'
has_many :locations, autosave: true
accepts_nested_attributes_for :origin, :destination
....
end
位置模型是
class Location < ActiveRecord::Base
acts_as_mappable
validates :address, presence: true
validates :lat, presence: true
validates :lng, presence: true
end
create方法是
def create
@package = current_user.packages.build(package_params)
if @package.save
......
end
package.save失败了。这是我收到的错误。
PackagesController #create中的ActiveRecord :: AssociationTypeMismatch 位置(#70350522152300)预计,得到字符串(#70350507797560)
我可以想到几个解决方法,但我想让它工作,所以我从中学习。我已经尝试过阅读rails api并在谷歌上搜索了几天,但我还是没能把它弄得太干净了。
帖子数据是
Parameters: {
"utf8"=>"✓",
"authenticity_token"=>"ZYkfpQBu6fvX7ZmzRw2bjkU+3i6mH0M7JLeqG4b99WI=",
"origin_input"=>"Kimmage, Dublin, Ireland",
"package"=>{
"origin"=>"{
\"address\":\"Kimmage, Dublin, Ireland\",
\"lat\":53.32064159999999,
\"lng\":-6.298185999999987}",
"destination"=>"{
\"address\":\"Lucan, Ireland\",
\"lat\":53.3572085,
\"lng\":-6.449848800000041}",
"length"=>"22",
"width"=>"222",
"height"=>"22",
"weight"=>"0 -> 5",
"delivery_date"=>"2014-10-31"},
"destination_input"=>"Lucan, Ireland",
"commit"=>"Post"}
我知道原点和目的地没有被反序列化,但我不知道它们为什么不是。我是否必须手动反序列化字符串,我可以在package_params中执行此操作吗?
创建此内容的表单如下
<%= form_for(@package, :html => {:class => "form-horizontal", :role => 'form'}) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="form-group">
<input type="text" name="origin_input" placeholder="From" onFocus="geolocate(); autocompleteLocation(this,package_origin)" class="form-control" />
<%= f.hidden_field :origin, class: "form-control" %>
</div>
<div class="form-group">
<input type="text" name="destination_input" placeholder="Destination" onFocus="geolocate(); autocompleteLocation(this,package_destination)" class="form-control" />
<%= f.hidden_field :destination, class: "form-control" %>
</div>
<div class="form-inline form-group">
<div class="input-group col-md-3">
<%= f.text_field :length, placeholder: "L", class: "form-control" %>
<span class="input-group-addon">cm</span>
</div>
<div class="input-group col-md-3">
<%= f.text_field :width, placeholder: "W", class: "form-control" %>
<span class="input-group-addon">cm</span>
</div>
<div class="input-group col-md-3">
<%= f.text_field :height, placeholder: "H", class: "form-control" %>
<span class="input-group-addon">cm</span>
</div>
</div>
<div class="form-group input-group">
<p>Please select the weight range of your package, Weights are in kg</p>
<% options = options_from_collection_for_select(@weights, 'weight', 'weight') %>
<%= f.select :weight, options, class: "form-control dropdown" %>
</div>
<div class="form-group">
<%= f.date_field :delivery_date, class: "form-control" %>
</div>
<%= f.submit "Post", class: "btn btn-large btn-primary", id: "package_post" %>
<% end %>
<%= render 'shared/places_autocomplete' %>
答案 0 :(得分:4)
您收到的错误AssociationTypeMismatch
是由于您在strong_params中添加了origin:
和destination:
。 Rails认为你试图关联对象就像你做@post.comment = @comment
一样。
即使正确的序列化&amp;你的params反序列化这种方法不起作用。 Rails会看到你正在尝试使用strong_params:
# Not deserialized
@package.origin = '{ \"address\":\"Kimmage, Dulbin, Ireland\", ... }'
# Deserialized. However, this still won't work.
@package.origin = { address: "Kimmage, Dublin, Ireland", ...}
Rails在两种情况下都想要一个对象。您可以使用正确反序列化的案例进入控制台进行测试:
$ rails c
irb(main): p = Package.new
irb(main): p.destination = { address: "Kimmage, Dublin, Ireland" } # => Throws ActiveRecord::AssociationTypeMismatch.
那么,为什么它不起作用?因为Rails不是将它传递给实际对象,而是将您传递的内容解释为字符串或散列。为了通过strong_params关联对象,Rails会查找并使用accepts_nested_attributes
方法(您已经尝试过)。但是,如下所述,这不会为您解决问题。
此处的问题是您尝试关联数据的方式。使用接受嵌套属性是通过父对象关联和保存子对象。在您的情况下,您尝试使用accepts_nested_attributes_for方法通过子对象(包)关联并保存两个父对象(origin和amp; destination)。 Rails不会以这种方式工作。
来自docs的第一行(强调我的):
嵌套属性允许您通过父保存关联记录的属性。
在您的代码中,您尝试通过孩子关联并保存/更新。
您需要的是表单中的origin_id
和location_id
,不包括模型中的accepts_nested_attributes
,因为您不再需要它,然后使用ID&#39; S:
params.require(:package).permit(:width, :length, :height, :whatever_else, :origin_id, :location_id)
然后,在提交表单之前使用AJAX请求,将这两个位置的origin_id
和destination_id
插入隐藏字段。如果它们不存在,您可以使用find_or_create_by方法在检索时创建这些位置。
@destination & @origin
@origin
和@destination
与@package
你不需要accept_nested_attributes_for任何东西。您可以像平常一样保存包(确保修改package_params)。
class PackagesController < ApplicationController
before_action :set_origin, only: [:create]
before_action :set_destination, only: [:create]
def create
@package = current_user.packages.build(package_params)
@package.destination = @destination
@package.origin = @origin
if @package.save
# Do whatever you need
else
# Do whatever you need
end
end
private
# Create the package like you normally would
def package_params
params.require(:package).permit( :state, :delivery_date, :length, :height, :width, :weight)
end
def set_origin
# You can use Location.create if you don't need to find a previously stored origin
@origin = Location.find_or_create_by(
address: params[:package][:origin][:address],
lat: params[:package][:origin][:lat],
lng: params[:package][:origin][:lng],
)
end
def set_destination
# You can use Location.create if you don't need to find a previously stored destination
@destination = Location.find_or_create_by(
address: params[:package][:destination][:address],
lat: params[:package][:destination][:lat],
lng: params[:package][:destination][:lng],
)
end
end
要确保您拥有包含有效来源和目的地的套餐,请在模型中验证:
class Package < ActiveRecord::Base
validates :origin, presence: true
validates :destination, presence: true
validates_associated :origin, :destination
end