gpt4 book ai didi

json - Rails 嵌套属性不会从隐藏表单输入中的 JSON 字符串创建对象

转载 作者:行者123 更新时间:2023-12-01 12:39:21 25 4
gpt4 key购买 nike

您好,我有一个表格包,我通过表格填充并使用参数。该表具有到另一个表位置的 FK,该位置包含包裹的纬度、经度和地址。 Location 表使用 GeoKit。

我的表单包含包裹字段和允许用户输入位置名称的字段。 Google map 帮助用户使用自动完成功能填写详细信息,并将结果以 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 不再在参数的包对象中传递。

封装模型为

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

创建方法是

def create
@package = current_user.packages.build(package_params)
if @package.save
......
end

package.save 失败。这是我收到的错误。

ActiveRecord::AssociationTypeMismatch in PackagesController#create Location(#70350522152300) expected, got String(#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' %>

最佳答案

问题

您收到的错误 AssociationTypeMismatch 是由于将 origin:destination: 放在您的 strong_params 中引起的。 Rails 认为您正在尝试像 @post.comment = @comment 那样关联对象。

即使对您的参数进行了适当的序列化和反序列化,这种方法也行不通。 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就是通过一个父对象来关联和保存子对象。在您的情况下,您正在尝试使用 accepts_nested_attributes_for 方法通过子对象(包)关联并保存两个父对象(起点和终点)。 Rails 不会以这种方式工作。

来自 docs 的第一行(强调我的):

Nested attributes allow you to save attributes on associated records through the parent.

在您的代码中,您试图通过 child 关联并保存/更新它。


解决方案

解决方案一

您需要的是表单中的 origin_idlocation_id,从您的模型中排除 accepts_nested_attributes,因为您不需要它,然后使用 ID 保存您的包裹:

params.require(:package).permit(:width, :length, :height, :whatever_else, :origin_id, :location_id)

然后,在提交表单之前使用 AJAX 请求将这两个位置的 origin_iddestination_id 插入到隐藏字段中。如果这些位置尚不存在,您可以使用 find_or_create_by 方法在检索时创建这些位置。

解决方案2

  • 在 Controller 的 before_action 中查找或创建父资源 @destination & @origin
  • @origin@destination 关联到 @package

您不需要为任何内容接受_nested_attributes_。您可以像往常一样保存包(确保修改 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

关于json - Rails 嵌套属性不会从隐藏表单输入中的 JSON 字符串创建对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26474378/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com