Как мне подходить к многим сквозным отношениям с Single Table Inheritance (STI) в Rails 4.0

У меня есть родительский класс под названием 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

Кто-нибудь может объяснить, как мне следует подойти ко многим с ИППП?

Обновить

Вот выходной SQL-запрос от @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" для изменения имени таблицы соединений (элегантно)

2 ответа

Решение

Это сложный случай для ActiveRecord. Он должен сделать вывод, что столбцы в самообъединении, необходимые для поиска districts являются случаями ИППП. Видимо, он недостаточно умен, чтобы понять это правильно. Поскольку единственная таблица 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. Это интересный случай.

Я думаю, вам нужно изменить наследование ваших моделей.

class Country < Place

class City < Country

class District < City

А затем удалите

has_one :country through: :city 

линия.

Прокрутите вниз, чтобы найти информацию о ИППП http://api.rubyonrails.org/classes/ActiveRecord/Base.html

Другие вопросы по тегам