首页 > 解决方案 > Rails ActiveRecord 嵌套 .create!使用 PSQL POINT 类型

问题描述

我正在尝试使用 Rails 和 ActiveRecord 创建一个深度嵌套的对象。

Order.create!(params)

该模型包含一个Address具有coordinatesPSQL 类型 POINT 字段的子模型。

因此,当我尝试创建它时,出现以下错误。我猜它试图通过使用提供的字段首先加载类型,但由于没有比较Address而挂断在(POINT) 类型上(根据https://www.postgresql.org/docs/13 /functions-geometry.html)。coordinates=~=

所以我的问题......有没有办法向 ActiveRecord 提供提示以帮助它正确执行此查询,或者我应该如何解决这个问题?

 Address Load (0.7ms)  SELECT "addresses".* FROM "addresses" WHERE "addresses"."firstname" = '-' AND "addresses"."lastname" = '-' AND "addresses"."phone" = '-' AND "addresses"."coordinates" = '-74.0064172,40.7049028' AND "addresses"."address1" = '110 Wall St' AND "addresses"."address2" = '' AND "addresses"."city" = 'New York' AND "addresses"."zipcode" = '10005' AND "addresses"."state_id" = 1 AND "addresses"."country_id" = 1 LIMIT 1
  ↳ app/services/cart/create_cart_service_decorator.rb:21:in `call'
PG::UndefinedFunction: ERROR:  operator does not exist: point = unknown
LINE 1: ..."phone" = '-' AND "addresses"."coordinates" = '-74.006...
                                                             ^
HINT:  No operator matches the given name and argument types. You might need to add explicit type casts.

这是一个独立的文件来重现这一点。

require "bundler/inline"

gemfile(true) do
  source "https://rubygems.org"

  git_source(:github) { |repo| "https://github.com/#{repo}.git" }

  # Activate the gem you are reporting the issue against.
  gem "activerecord", "6.1.3.2"
  gem "pg"
end

require "active_record"
require "minitest/autorun"
require "logger"

# This connection will do for database-independent bug reports.
ActiveRecord::Base.establish_connection(adapter: "postgresql", database: "test-db")
ActiveRecord::Base.logger = Logger.new(STDOUT)

ActiveRecord::Schema.define do
  create_table :addresses, force: true do |t|
    t.point :coordinates
  end
end

class Address < ActiveRecord::Base
end

class BugTest < Minitest::Test
  def test_nested_saving
    address = Address.find_or_create_by!({ coordinates: ActiveRecord::Point.new(0, 0) })

    assert address
  end
end

标签: ruby-on-railspostgresqlgeometryrails-activerecordspree

解决方案


您需要将参数显式类型转换为 ActiveRecord::Point 实例,从某些版本开始就支持该实例:

x, y = params[:coordinates].split(',').map(&:to_f)
params[:coordinates] = ActiveRecord::Point.new(x, y)

推荐阅读