Стаття українською. Ruby on Rails, STI and Active Record Associations (belongs_to)

Oleksii Voitsekhovych
3 min readAug 27, 2022

З’явилась у мене задача — зберегти адрес клієнта. Штат, місто, вулиця. Звичайно можна піти простим шляхом — додати таблиці states, cities, streets — в них додати поля на кшталт:

id number

name string

parent_id number

і реалізувати це через зв’язок belongs_to. Але — в даному випадку це буде занадто, тому що можна простіше :)

Використаємо STI — Single Table Inheritance .

Що каже нам документація:

“Sometimes, you may want to share fields and behavior between different models. Let’s say we have Car, Motorcycle, and Bicycle models. We will want to share the color and price fields and some methods for all of them, but having some specific behavior for each, and separated controllers too.”

Тобто, коли, як в нашому випадку, нам потрібно зберігати однакові дані — не потрібно створювати багато таблиць, можна все покласти в одну.

Приступаємо до найсмачнішого, до кодінгу :)

Створюємо дефолтний проект.

rails new sti_example

Створюємо міграцію для базового класу AddressPart

rails generate model address_part type:string name:string

І створюємо три моделі — State, City, Street

bin/rails generate model state --parent=AddressPartbin/rails generate model city --parent=AddressPartbin/rails generate model street --parent=AddressPart

Загалом що ми маємо? Таблицю address_parts і чотири моделі — AddressPart, State, City, Street.

Спочатку перевіримо — працює чи ні.

rails db:migrate — Створили таблицю.rails c — відкрили консоль і додали тестові записи

State.create(name: ‘CA’)

City.create(name: ‘Sacramento’)

Street.create(name: ‘Ace Ct’)

тепер потрібно перевірити — що дані правильно збереглись в таблицю.

p ActiveRecord::Base.connection.exec_query(‘select * from address_parts’)

і що ми бачимо? в таблиці є три записи, в type — вказано що саме там зберігається.

Це добре. Але тепер потрібно зробити зв’язок parent-child для тих даних. Закриваємо консоль командою — exit. Видаляємо ті дані що є і створюємо ще одну міграцію.

rails db:reset — перестворили базу даних. а так як у нас там лише одна таблиця — то по суті ми просто почистили дані.rails generate migration add_parentid_to_address_part parentid:integerrails db:migrate

Тепер потрібно модифікувати моделі. Зовсім трохи :)

street.rb
city.rb
state.rb

Тепер додаємо дані в таблицю. (знову ж — запустивши консоль rails)

state = State.create(name: ‘CA’)

city = City.create(name: ‘Sacramento’)

Зробимо зв’язок штата і міста.

state.city << city

street = Street.create(name: ‘Ace Ct’)

і додаємо цю вулицю в місто

city.street << street

Для чистоти експерименту — додамо це одне місто без вулиць і ще одну вулицю в Sacramento

street = Street.create(name: ‘Aerojet Dr.’)

city.street << street

city = City.create(name: ‘San Diego’)

state.city << city

і дивимось в таблицю що у нас вийшло

p ActiveRecord::Base.connection.exec_query(‘select * from address_parts’)

що ми тут бачимо.

У штата CA — є два child записи (з parrentid = 1) — це міста Sacramento і San Diego.

У міста Sacramento є два child записи (з parentid = 2) — це вулиці Aerojet Dr. та Ace Ct.

І є місто San Diego — у которого вулиць немає :)

Також у нас працюють всі зв’язки. Тобто — виконання наступних команд покаже нам структуру даних ( що саме покажуть команди — це вам домашня вправа :) )

state = State.first

p state

p state.city

p state.city.first.street

street = Street.first

p street

p street.city

p street.city.state

На цьому дякую що прочитали. Успішних вам проектів. А в наступній статті ми побалакаємо як за допомогою Faker gem — зробити собі трохи адресів.

--

--