首页 > 解决方案 > 多态关联的 PG::UndefinedColumn 错误(仅在控制器中,在控制台中工作)

问题描述

我有一个多态关联,可以在 rails 控制台中正常工作,但无论我如何构造它,从控制器调用时都无法正常工作。

基本代码:

# in property.rb

has_many :property_contacts, as: :property_contactable, dependent: :destroy
has_many :contacts, through: :property_contacts

# in property_contact.rb

belongs_to :contact, optional: true
belongs_to :property_contactable, polymorphic: true, optional: true

# in properties_controller.rb

def show
  @property = Property.find(params[:id])
end

# in schema.rb

create_table "property_contacts", force: :cascade do |t|
  t.bigint "property_id" # old, before polymorphic relationship added, will remove after data migration
  t.bigint "contact_id"
  t.string "property_contactable_type"
  t.bigint "property_contactable_id"
  # ...
end

# and output from psql, just to show that there doesn't appear to be any disconnect between the schema and postgres itself

# project_name_development=# \d property_contacts
#                                                  Table "public.property_contacts"
#           Column           |              Type              | Collation | Nullable |                    Default                    
# ---------------------------+--------------------------------+-----------+----------+-----------------------------------------------
#  property_id               | bigint                         |           |          | 
#  contact_id                | bigint                         |           |          | 
# # ...
#  property_contactable_type | character varying              |           |          | 
#  property_contactable_id   | bigint                         |           |          | 

在一个视图中,如果我调用@property.property_contacts,我会收到此错误:

ActiveRecord::StatementInvalid - PG::UndefinedColumn: ERROR:  column property_contacts.property_contactable_id does not exist
LINE 1: ...operty_contacts".* FROM "property_contacts" WHERE "property_...
                                                             ^:

如果我强制查询在控制器本身中运行(使用类似的东西@property_contacts = @property.property_contacts.load),那么我会得到指向控制器中该行的相同错误。

如果我从查询中省略property_contactable_id并仅基于该列构建查询property_contactable_type,则会收到一条错误消息,指出该property_contactable_type列也不存在。

如果我尝试绕过关联并直接构建查询,像这样,我会得到同样的错误:

# in controller:

@property = Property.find(params[:id])
@property_contacts = PropertyContact.for(@property)

# in property_contact.rb

  def self.for(property)
    # tried both versions, same result
    # where("property_contactable_id = ? AND property_contactable_type LIKE ?", property.id, property.class.name)
    # where("property_contacts.property_contactable_id = ? AND property_contacts.property_contactable_type LIKE ?", property.id, property.class.name)
  end

但是,当我从 rails 控制台运行它们时,所有这些都运行得很好,例如:

property = Property.find(123)
# both versions work
property_contacts = property.property_contacts
property_contacts = PropertyContact.for(property)

如果我调用to_sql查询,我得到的输出与控制器中的调用与控制台中的调用略有不同:

# from console:
"SELECT \"property_contacts\".* FROM \"property_contacts\" WHERE \"property_contacts\".\"property_contactable_id\" = 4675010 AND \"property_contacts\".\"property_contactable_type\" = 'Property'" 
# from controller:
"SELECT \"property_contacts\".* FROM \"property_contacts\" WHERE (property_contacts.property_contactable_id = 4675010 AND property_contacts.property_contactable_type = 'Property')"

我尝试在我的控制器中运行这些查询的原始 sql 版本并得到相同的错误。我尝试了一些不同的变化:

sql = "SELECT \"property_contacts\".* FROM \"property_contacts\" WHERE \"property_contacts\".\"property_contactable_id\" = #{@property.id} AND \"property_contacts\".\"property_contactable_type\" = 'Property'"
sql = "SELECT property_contacts.* FROM property_contacts WHERE property_contacts.property_contactable_id = #{@property.id} AND property_contacts.property_contactable_type = '#{@property.class.name}' ORDER BY property_contacts.created_at desc"
sql = "SELECT property_contacts.* FROM property_contacts WHERE property_contactable_id = #{@property.id} AND property_contactable_type = '#{@property.class.name}'"
sql = "SELECT property_contacts.* FROM property_contacts WHERE property_contacts.property_contactable_type = '#{@property.class.name}' ORDER BY property_contacts.created_at desc"
sql = "SELECT \"property_contacts\".* FROM \"property_contacts\" WHERE \"property_contacts\".\"property_contactable_id\" = 4675010 AND \"property_contacts\".\"property_contactable_type\" = 'Property'"

@property_contacts = PropertyContact.find_by_sql(sql)
@property_contacts = ActiveRecord::Base.connection.execute(sql)

任何人都可以发现我做错了什么,或者给我其他我可以尝试的建议吗?

标签: ruby-on-railspostgresqlactiverecord

解决方案


发现了问题——我正在处理的应用程序带有一个手动多租户模块,而我创建的迁移只是迁移公共模式。它在整个应用程序中并不经常使用,因此很容易忘记它的存在。我通过控制台与公共架构进行交互,但通过 Web 服务器与特定租户的架构(没有我添加的新列)交互。


推荐阅读