我有一个名为Place的父类。
# Parent class for Country, City and District
class Place < ActiveRecord::Base
end
class Country < Place
has_many :cities, foreign_key: "parent_id"
has_many :districts, through: :cities
end
class City < Place
belongs_to :country, foreign_key: "parent_id"
has_many :districts, foreign_key: "parent_id"
end
class District < Place
belongs_to :city, foreign_key: 'parent_id'
has_one :country, through: :city
end
架构:
create_table "places", force: true do |t|
t.string "name"
t.string "type"
t.integer "parent_id"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "places", ["parent_id"], name: "index_places_on_parent_id"
add_index "places", ["type"], name: "index_places_on_type"
以下按预期方式工作:
@country.cities # => Returns all of the cities that belong to this country
@city.districts # => Returns all of the districts that belong to this city
但这并不像我想的那样有效:
@country.districts # => Does not return all of the districts belonging to cities in this country
任何人都可以通过STI来解释我应该如何处理吗?
以下是来自@country.districts
SELECT "places".* FROM "places" INNER JOIN "places" "cities_districts_join" ON "places"."parent_id" = "cities_districts_join"."id" WHERE "places"."type" IN ('City') AND "places"."type" IN ('District') AND "cities_districts_join"."parent_id" = ? [["parent_id", 1]]
我认为问题在于它为两个关系使用相同的连接表,但我不确定是否有一个“Rails方式”来更改连接表的名称(优雅地)
答案 0 :(得分:2)
这对ActiveRecord来说是一个具有挑战性的案例。它需要推断查找districts
所需的自联接中的列是STI实例。显然,要做到这一点并不够聪明。由于唯一的表是places
,因此生成此查询并不奇怪:
SELECT "places".* FROM "places"
INNER JOIN "places" "cities_districts_join"
ON "places"."parent_id" = "cities_districts_join"."id"
WHERE "places"."type" IN ('City') <<<<< ERROR HERE
AND "places"."type" IN ('District')
AND "cities_districts_join"."parent_id" = ?
正如您所看到的,类型检查必须失败,因为一个字符串不能同时是City
和District
。如果WHERE
中的第一个条款改为
WHERE "cities_districts_join"."type" IN ('City')
我在关系上尝试了几个选项(思考:class_name
可能会这样做),但没有快乐。
您可以使用SQL解决此限制。删除has_many ... through
类中的Country
并替换为
def districts
District.find_by_sql(['SELECT * from places AS city
INNER JOIN places AS district
ON district.parent_id = city.id
WHERE city.parent_id = ?', id])
end
或许其他人会看到更优雅的方式。如果没有,您可以考虑将此作为Rails开发中的问题发布。这是一个有趣的案例。
答案 1 :(得分:-1)
我认为您需要更改模型的继承。
class Country < Place
class City < Country
class District < City
然后删除
has_one :country through: :city
线。
向下滚动以查找有关STI的信息 http://api.rubyonrails.org/classes/ActiveRecord/Base.html