Rails 5.2 多态关联与多态条件
Rails 5.2 Polymorphic Association with polymorphic conditional
型号:
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
并假设 Car 和 Truck 都有一个 user_id 字段....
Listing.includes(:listable)
returns 一个 eargerloaded listings AR 关系。
但是,我需要按 user_id 进行筛选,所以我尝试...
Listing.includes(:listable).where(user_id: 100)
但是我得到一个错误..
ActiveRecord::StatementInvalid (PG::UndefinedColumn: ERROR: column listings.user_id does not exist)
LINE 1: SELECT "listings".* FROM "listings" WHERE "listings"."user_...
因为它似乎在查找 user_id 列表。但是,我需要在 listables table 上进行过滤,因此这意味着 Car 或 Truck tables。然而 listable 被定义了。
我也试过:
Listing.includes(listable:[:user]).where('users.id = 100')
但我明白了...
ActiveRecord::StatementInvalid (PG::UndefinedTable: ERROR: missing FROM-clause entry for table "users")
LINE 1: SELECT "listings".* FROM "listings" WHERE (users.id = 100) ...
^
: SELECT "listings".* FROM "listings" WHERE (users.id = 100) LIMIT
更新:
然后试了
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 (Can't join 'Car' to association named 'listable'; perhaps you misspelled it?)
因此,在上面的方法起作用之前,我无法尝试下面的方法。
Listing.includes(:car, :truck).where(cars: { user_id: 1 }).or(Listing.includes(:car, :truck).where(trucks: { user_id: 1 }))
但是,我可以做到 Listing.includes(:listable) 它确实有效,当我添加条件时它会中断。
这是一个非常有趣的问题,我问了好几个月。然后我找到了解决办法。
在您的 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(:listable).where(listings: { listable_type: Car.to_s }) }, foreign_key: :listable_id
belongs_to :truck, -> { includes(:listable).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 }))
对于那些可能和我一样难以解决这个问题的人...
我的最终解决方案:
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)
型号:
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
并假设 Car 和 Truck 都有一个 user_id 字段....
Listing.includes(:listable)
returns 一个 eargerloaded listings AR 关系。
但是,我需要按 user_id 进行筛选,所以我尝试...
Listing.includes(:listable).where(user_id: 100)
但是我得到一个错误..
ActiveRecord::StatementInvalid (PG::UndefinedColumn: ERROR: column listings.user_id does not exist) LINE 1: SELECT "listings".* FROM "listings" WHERE "listings"."user_...
因为它似乎在查找 user_id 列表。但是,我需要在 listables table 上进行过滤,因此这意味着 Car 或 Truck tables。然而 listable 被定义了。
我也试过:
Listing.includes(listable:[:user]).where('users.id = 100')
但我明白了...
ActiveRecord::StatementInvalid (PG::UndefinedTable: ERROR: missing FROM-clause entry for table "users") LINE 1: SELECT "listings".* FROM "listings" WHERE (users.id = 100) ... ^ : SELECT "listings".* FROM "listings" WHERE (users.id = 100) LIMIT
更新:
然后试了
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 (Can't join 'Car' to association named 'listable'; perhaps you misspelled it?)
因此,在上面的方法起作用之前,我无法尝试下面的方法。
Listing.includes(:car, :truck).where(cars: { user_id: 1 }).or(Listing.includes(:car, :truck).where(trucks: { user_id: 1 }))
但是,我可以做到 Listing.includes(:listable) 它确实有效,当我添加条件时它会中断。
这是一个非常有趣的问题,我问了好几个月。然后我找到了解决办法。
在您的 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(:listable).where(listings: { listable_type: Car.to_s }) }, foreign_key: :listable_id
belongs_to :truck, -> { includes(:listable).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 }))
对于那些可能和我一样难以解决这个问题的人...
我的最终解决方案:
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)