Rails 5.2 多态关联与多态条件

问题描述 投票:0回答:3

型号:

class Car < ApplicationRecord
  belongs_to :user

  has_one  :listing, as: :listable
  has_one  :firm, as: :firmable
  has_one  :seller, as: :sellable
end

class Truck < ApplicationRecord
  belongs_to :user

  has_one  :listing, as: :listable
  has_one  :firm, as: :firmable
  has_one  :seller, as: :sellable
end

class Listing < ApplicationRecord
  belongs_to :listable, polymorphic: true
  has_many :favorites, dependent: :destroy
  has_many :users_who_favorited, through: :favorites, source: :user
end

假设汽车和卡车都有一个 user_id 字段....

Listing.includes(:listable)
返回早期加载的列表 AR 关系。

但是,我需要通过user_id进行过滤,所以我尝试了...

Listing.includes(:listable).where(user_id: 100)

但是我收到错误..

ActiveRecord::StatementInvalid(PG::UndefinedColumn:错误:列列表.user_id 不存在) 第 1 行:从“列表”中选择“列表”。*,其中“列表”。“user_...

因为它似乎在列表中查找 user_id 。但是,我需要对可列表表进行过滤,因此这意味着汽车或卡车表。然而 listable 是被定义的。

我也尝试过:

Listing.includes(listable:[:user]).where('users.id = 100')

但我明白...

ActiveRecord::StatementInvalid(PG::UndefinedTable:错误:缺少表“users”的 FROM 子句条目) 第 1 行:从“列表”中选择“列表”。* WHERE (users.id = 100) ... ^ :从“列表”中选择“列表”。* WHERE (users.id = 100) LIMIT $1

更新:

然后尝试了

class Listing < ApplicationRecord
  belongs_to :listable, polymorphic: true
  has_many :favorites, dependent: :destroy
  has_many :users_who_favorited, through: :favorites, source: :user

  belongs_to :car, -> { includes(:listable).where(listable: { listable_type: Car.to_s }) }, foreign_key: :listable_id
  belongs_to :truck, -> { includes(:listable).where(listable: { listable_type: Truck.to_s }) }, foreign_key: :listable_id

end

并尝试了

Listing.includes(:car, :truck)
但得到了..

ActiveRecord::ConfigurationError(无法将“Car”加入名为“listable”的关联;也许您拼写错误?)

因此,在上述方法有效之前我无法尝试以下方法。

Listing.includes(:car, :truck).where(cars: { user_id: 1 }).or(Listing.includes(:car, :truck).where(trucks: { user_id: 1 }))

但是,我可以执行 Listing.includes(:listable) 并且它确实有效,但当我添加条件时它会中断。

ruby-on-rails ruby polymorphism ruby-on-rails-5.2
3个回答
5
投票

这是我几个月来一直思考的一个非常有趣的问题。然后我找到了解决方案。

在您的

Listing
模型中,为了能够包含您的
polymorphic
模型,您需要告诉您的模型它们是相关的。

class Car < ApplicationRecord
  belongs_to :user

  has_one  :listing, as: :listable
  has_one  :firm, as: :firmable
  has_one  :seller, as: :sellable
end

class Truck < ApplicationRecord
  belongs_to :user

  has_one  :listing, as: :listable
  has_one  :firm, as: :firmable
  has_one  :seller, as: :sellable
end

class Listing < ApplicationRecord
  belongs_to :listable, polymorphic: true
  has_many :favorites, dependent: :destroy
  has_many :users, through: :favorites

  #magic happens here
  belongs_to :car, -> { includes(:listings).where(listings: { listable_type: Car.to_s }) }, foreign_key: :listable_id
  belongs_to :truck, -> { includes(:listings).where(listings: { listable_type: Truck.to_s }) }, foreign_key: :listable_id

end

现在,您只需执行以下操作即可:

Listing.includes(:car, :truck)
,它将完美运行:-)

对于您的情况:

Listing.includes(:car, :truck).where(cars: { user_id: 1 }).or(Listing.includes(:car, :truck).where(trucks: { user_id: 1 }))

0
投票

对于那些可能像我一样难以解决这个问题的人......

我的最终解决方案:

    def left_join_listable(table_name, listable_type_value)
      "LEFT OUTER JOIN \"#{table_name}\" "\
      "ON \"#{table_name}\".\"id\" = \"listings\".\"listable_id\" "\
      "AND \"listings\".\"listable_type\" = #{listable_type_value}"
    end

   def left_join_users_on(*table_names)
      join = "LEFT OUTER JOIN \"users\" ON "
      conditionals = table_names.map {|table_name| "\"users\".\"id\" = \"#{table_name}\".\"user_id\"" }.join(" OR ")
      join + conditionals
    end

Listing.joins(left_join_listable('cars',"\'Car\'"))
       .joins(left_join_listable('trucks',"\'Trucks\'"))
       .joins(left_join_users_on('cars','trucks')
       .where(users.id = (?), 100)

0
投票

我可能在这里遗漏了一些东西,但为什么不将 CarTruck 设置为像 Vehicle 这样的 STI 子类?

为什么不将 firm

seller
has_one
关联改为 belongs_to,以便每个公司或卖家可以拥有多个列表?

# Parent class for both Car and Truck
class Vehicle < ApplicationRecord
  # Note that this table has a +type+ column which is used for Single Table Inheritance
  belongs_to :user
  has_many :listings, dependent: :destroy

  # You _might_ want to move these over to Listing so that over time the
  # same vehicle can be listed by different sellers (at different times)
  belongs_to  :firm
  belongs_to  :seller
end

class Car < Vehicle
end

class Truck < Vehicle
end

class Listing < ApplicationRecord
  belongs_to :vehicle
  has_many :favorites, dependent: :destroy
  has_many :users, through: :favorites
end

class User < ApplicationRecord
  has_many :vehicles
  has_many :favorites, dependent: :destroy
  has_many :listings, through: :favorites
end

# Associative table between User and Listing
class Favorite < ApplicationRecord
  belongs_to :user
  belongs_to :listing
end

这样的设置还有什么不能查询的吗?

© www.soinside.com 2019 - 2024. All rights reserved.