
Шляхи | Ruby on Rails
Вступ до Шляхів (Routes) у Ruby on Rails
Перед тим, як почати розглядати основні концепції моделей в Ruby on Rails, варто детальніше розібратися, що саме таке моделі та для чого вони використовуються.
Модель у Ruby on Rails є основним елементом патерну проектування Model-View-Controller (MVC), що використовується для розробки веб-додатків. Модель представляє собою об'єкт, який відповідає за зберігання та обробку даних додатку. Модель забезпечує доступ до даних та їх збереження, що здійснюється через зв'язок з базою даних.
Основне призначення моделей полягає у збереженні та зборі даних з бази даних, та забезпеченні безпечного доступу до них. Крім цього, моделі відповідають за обробку та валідацію вхідних даних, які надходять від користувача, перед тим, як вони будуть збережені у базі даних.
Структура моделі у Ruby on Rails включає в себе всі необхідні атрибути та методи, що визначають поведінку моделі. Атрибути представляють собою характеристики об'єкту, що можуть бути збережені в базі даних, наприклад, ім'я, прізвище, електронна пошта тощо. Методи використовуються для виконання операцій з моделлю, таких як створення, читання, оновлення та видалення записів. Структура моделі передбачає визначення таблиці в базі даних, яка відповідає конкретній моделі, а також зв'язки між моделями. Для визначення таблиці в базі даних, яка відповідає моделі, використовується конвенція найменувань. Назва моделі записується у форматі CamelCase, а назва таблиці в базі даних - у форматі snake_case, тобто з малих літер та розділених символом "_".
Наприклад, якщо у нашому додатку є модель User, то таблиця в базі даних буде мати назву "users". ActiveRecord автоматично знає, яка таблиця в базі даних відповідає конкретній моделі, тому не потрібно вказувати це явно.
Зв'язок між моделями та базою даних у Ruby on Rails забезпечується за допомогою ActiveRecord. ActiveRecord є ORM (Object-Relational Mapping), який забезпечує маппінг об'єктів додатку на таблиці в базі даних. Це означає, що ActiveRecord дозволяє працювати з базою даних за допомогою об'єктів, що спрощує взаємодію з базою даних та забезпечує більш чистий та структурований код.
Інакше кажучи, ActiveRecord дозволяє виконувати CRUD-операції (створення, читання, оновлення та видалення) на базі даних, використовуючи об'єкти моделей. Це означає, що замість написання складних SQL-запитів, можна використовувати методи ActiveRecord для взаємодії з базою даних.
Для визначення зв'язку між двома моделями необхідно визначити, який тип зв'язку використовується (один до одного, один до багатьох, багато до багатьох) та визначити, яка модель є "батьківською", а яка "дочірньою".
Один до одного зв'язок - це зв'язок між двома таблицями бази даних, в якому кожен запис в одній таблиці посилається на один і тільки один запис в іншій таблиці, і навпаки. Цей тип зв'язку можна використовувати, коли у вас є таблиці, які містять додаткову інформацію про об'єкти, що вже зберігаються в базі даних, наприклад, таблиця користувачів та таблиця профілів користувачів.
У Rails для визначення зв'язку один до одного використовується метод has_one. Наприклад, якщо у вас є таблиця користувачів та таблиця профілів користувачів, то клас моделі користувача може виглядати так:
class User < ApplicationRecord
has_one :profile
end
А клас моделі профілю користувача - так:
class Profile < ApplicationRecord
belongs_to :user
end
У цьому прикладі метод has_one встановлює зв'язок один до одного між таблицями користувачів та профілів користувачів, а метод belongs_to вказує, що модель профілю користувача належить моделі користувача.
Тепер, коли ви маєте визначені класи моделей з відповідними асоціаціями, ви можете легко отримати профіль користувача, використовуючи метод profile:
user = User.find(1)
profile = user.profile
Якщо ж ви хочете знайти користувача, пов'язаного з профілем, ви можете використовувати метод user:
profile = Profile.find(1)
user = profile.user
Отже, зв'язок "один до одного" в ActiveRecord використовується, коли між двома моделями існує відношення, що відповідає принципу "один до одного". Це відношення може бути налаштоване з використанням методів belongs_to та has_one, які забезпечують доступ до пов'язаних записів та виконання операцій з ними. Також варто зазначити, що при створенні зв'язку "один до одного" в ActiveRecord автоматично створюється обмінний ключ між двома таблицями, що дозволяє здійснювати швидкий доступ до пов'язаних даних.
Зв'язок "один до багатьох" між двома моделями, коли в одній моделі є багато записів, які посилаються на записи в іншій моделі. Зв'язок "один до багатьох" встановлюється за допомогою методу has_many в одній моделі та методу belongs_to в іншій.
Розглянемо приклад з двома моделями - User та Post. У нашому додатку кожен користувач може мати декілька постів, але кожен пост може мати лише одного автора. Тому ми будемо мати зв'язок "один до багатьох" між User та Post.
# Модель User
class User < ApplicationRecord
has_many :posts
end
# Модель Post
class Post < ApplicationRecord
belongs_to :user
end
У цьому коді метод has_many встановлює зв'язок між User та Post. Він вказує, що в моделі User може бути багато постів, і зберігає зв'язок між User та Post в базі даних.
Метод belongs_to вказує, що кожен пост належить одному користувачеві. Він зберігає зв'язок між Post та User в базі даних.
Тепер, коли ми маємо встановлений зв'язок між User та Post, ми можемо легко отримувати всі пости, які належать користувачеві, або користувача, який написав певний пост. Наприклад, щоб отримати всі пости користувача з ідентифікатором 1, ми можемо написати:
user = User.find(1)
user_posts = user.posts
Це поверне об'єкт ActiveRecord::Relation, який містить всі пости, які належать користувачу з ідентифікатором 1.
Також ми можемо легко отримати автора для певного посту, використовуючи метод belongs_to. Наприклад, щоб отримати автора для посту з ідентифікатором 1, ми можемо написати:
post = Post.find(1)
post_author = post.user
Один до багатьох зв'язок в моделі використовується тоді, коли в одній моделі можуть бути пов'язані з багатьма записами в іншій моделі. Для цього використовуються асоціації, зокрема метод has_many в батьківській моделі та метод belongs_to в дочірній моделі. Цей тип зв'язку є дуже поширеним і використовується в багатьох реальних проектах.
У Rails для встановлення багато до багатьох зв'язку між моделями можна використовувати два способи: has_and_belongs_to_many та has_many :through.
has_and_belongs_to_many використовують, коли зв'язок між моделями є простим і не містить жодної додаткової інформації. Наприклад, якщо ми маємо моделі User та Group, і кожен користувач може належати до багатьох груп, а кожна група може містити багатьох користувачів, то ми можемо встановити зв'язок таким чином:
class User < ApplicationRecord
has_and_belongs_to_many :groups
end
class Group < ApplicationRecord
has_and_belongs_to_many :users
end
Rails автоматично створить таблицю бази даних з назвою groups_users, яка міститиме зв'язки між користувачами та групами.
Якщо зв'язок між моделями містить додаткову інформацію, наприклад, якщо ми маємо моделі User, Product та Order, і кожен користувач може замовляти багато продуктів, кожен продукт може бути замовлений багатьма користувачами, і кожен замовлення може містити багато продуктів, то ми можемо використовувати has_many :through.
class User < ApplicationRecord
has_many :orders
has_many :products, through: :orders
end
class Product < ApplicationRecord
has_many :orders
has_many :users, through: :orders
end
class Order < ApplicationRecord
belongs_to :user
belongs_to :product
end
У цьому прикладі ми використовуємо модель Order як "посередника" між користувачами та продуктами. Кожен зв'язок між User та Product проходить через Order. Для того, щоб знайти всі продукти, які були замовлені конкретним користувачем, можна використовувати такий код:
class User < ApplicationRecord
has_many :orders
has_many :products, through: :orders
end
class Product < ApplicationRecord
has_many :orders
has_many :users, through: :orders
end
class Order < ApplicationRecord
belongs_to :user
belongs_to :product
end
class User < ApplicationRecord
has_and_belongs_to_many :groups
end
У цьому прикладі ми використовуємо модель Order як "посередника" між користувачами та продуктами. Кожен зв'язок між User та Product проходить через Order. Для того, щоб знайти всі продукти, які були замовлені конкретним користувачем, можна використовувати такий код:
user = User.find(1)
user.products
Використання has_many :through дозволяє нам встановити взаємозв'язок "багато до багатьох" між двома моделями через третю модель, що дозволяє використовувати додаткові атрибути та логіку.
За допомогою has_many :through також можна виконувати різні операції, такі як збереження атрибутів зв'язаної моделі під час створення запису через метод create, або фільтрацію записів за допомогою методу where. Також можна використовувати has_many :through для виконання різних запитів з об'єктами зв'язаних моделей.
З іншого боку, has_and_belongs_to_many - це більш простий спосіб встановити зв'язок "багато до багатьох" між двома моделями, без необхідності використовувати третю модель як зв'язуючу таблицю. Однак, використання has_and_belongs_to_many обмежує можливість додавання додаткових атрибутів та логіки.
У Rails поліморфні зв'язки (polymorphic associations) - це зв'язки між таблицями, де одна таблиця може належати до будь-якої з кількох таблиць.
Наприклад, уявімо ситуацію, де ми маємо три таблиці: comments, posts та articles. В кожному коментарі може бути лише одна прив'язка до поста, але один пост може мати багато коментарів. Ми можемо використовувати поліморфні зв'язки, щоб зробити це.
В даному випадку ми можемо визначити поліморфний зв'язок між таблицею comments, posts та articles, який дозволить нам зв'язати кожен коментар з відповідним постом та статею. За допомогою поліморфних зв'язків, ми можемо створити одну асоціацію, яка буде працювати для багатьох таблиць.
Для визначення поліморфного зв'язку в моделі Comment ми можемо використати метод belongs_to, який дозволяє нам вказати, що коментар може належати до будь-якого запису із таблиць posts або articles. Наприклад:
class Comment < ApplicationRecord
belongs_to :commentable, polymorphic: true
end
class Post < ApplicationRecord
has_many :comments, as: :commentable
end
class Article < ApplicationRecord
has_many :comments, as: :commentable
end
У цьому випадку ми визначили, що Comment може належати до будь-якої моделі, яка використовує поліморфізм. В моделі Post та Article ми вказали, що можливі коментарі, які належать до цих моделей.
Коли ми звертаємось до зв'язаних записів, ми можемо використовувати звичайний синтаксис Rails. Наприклад, щоб отримати всі коментарі, які належать до запису з Post з id 1, ми можемо зробити наступне:
post = Post.find(1)
comments = post.comments
Це поверне об'єкт ActiveRecord::Relation, який містить всі коментарі для посту з id 1. Цей об'єкт є поліморфним, оскільки його поведінка залежить від контексту використання. Аналогічно і для articles:
article = Article.find(1)
articles = article.comments
Зокрема, ми можемо використовувати цей об'єкт для здійснення подальших запитів до коментарів. Наприклад, ми можемо відфільтрувати коментарі за певною умовою, використовуючи метод where:
comments = Post.find(1).comments.where(approved: true)
Метод where поверне об'єкт ActiveRecord::Relation, який містить тільки ті коментарі, для яких значення поля approved дорівнює true.
Також ми можемо використовувати метод count для отримання загальної кількості коментарів до посту:
comments_count = Post.find(1).comments.count
Метод count поверне ціле число - загальну кількість коментарів до посту з id 1.
Загалом, поліморфізм дозволяє нам створювати більш універсальні моделі, які можуть бути пов'язані з об'єктами різних типів і мати з ними різні взаємини. Це допомагає зменшити кількість дублювання коду та робити додаток більш гнучким і масштабованим.
В Ruby on Rails є два важливих механізми, що дозволяють контролювати вхідні дані та автоматично виконувати код при виникненні певних подій моделі: це валідація та колбеки.
Валідація - це процес перевірки правильності введення даних. В Ruby on Rails валідації використовуються для перевірки, чи відповідають дані певним критеріям. Якщо валідація не пройдена, дані не зберігаються в базі даних, а користувач отримує повідомлення про помилку.
Для визначення валідацій в Ruby on Rails використовуються спеціальні методи, які викликаються при збереженні моделі. Найпоширеніші типи валідації включають в себе:
- перевіряє, що поле не порожнє. Давайте розглянемо приклад використання валідації присутності для моделі User, яка містить поля name та email:
class User < ApplicationRecord
validates :name, presence: true
validates :email, presence: true
end
У цьому прикладі валідація присутності застосовується до поля name та email. Якщо будь-яке з цих полів не буде встановлено, то збереження запису буде заборонено, і у змінній errors будуть міститися повідомлення про помилки.
user = User.new(name: "John", email: nil)
user.save # => false
user.errors.full_messages # => ["Email can't be blank"]
У цьому випадку збереження не відбулося через те, що поле email не було встановлено, і в змінній errors міститься повідомлення про помилку.
- перевіряє, що значення поля є унікальним в межах таблиці. Наприклад, якщо ми маємо модель User з полем email, ми можемо переконатися, що кожен користувач має унікальний електронний адрес за допомогою валідації унікальності.
class User < ApplicationRecord
validates :email, presence: true, uniqueness: true
end
У цьому прикладі, ми використовуємо метод validates для валідації поля email. Опція uniqueness говорить про те, що значення поля має бути унікальним. Якщо значення поля не є унікальним, то валідація не буде пройдена, і буде повернуто повідомлення про помилку.
- перевіряє, що значення поля відповідає певному формату, наприклад, електронна адреса або номер телефону. Для валідації формату використовується метод format з аргументом - регулярним виразом, який визначає потрібний формат.
Наприклад, для валідації формату електронної пошти можна визначити наступну валідацію в моделі:
class User < ApplicationRecord
validates :email, format: { with: URI::MailTo::EMAIL_REGEXP }
end
У цьому прикладі використовується вбудований в Ruby регулярний вираз URI::MailTo::EMAIL_REGEXP, який перевіряє, чи відповідає значення поля email формату електронної пошти. Якщо значення не відповідає формату, то виникає помилка валідації, яку можна обробити в контролері або відобразити користувачеві на сторінці.
При виконанні такої валідації необхідно впевнитись, що регулярний вираз відповідає потрібному формату, інакше користувачам можуть бути відхилені валідні значення поля. Також слід пам'ятати, що валідація формату може не виявити всіх можливих помилок, тому слід бути обережним при обробці даних користувачів
- перевіряє, що значення поля є числом, та може бути відповідним діапазоном. Для цього можна використовувати наступні типи валідації: numericality, greater_than, greater_than_or_equal_to, less_than, less_than_or_equal_to.
Наприклад, щоб перевірити, чи є число в межах від 1 до 100, можна використати валідацію numericality з опцією greater_than_or_equal_to та less_than_or_equal_to:
class Product < ApplicationRecord
validates :price, numericality: { greater_than_or_equal_to: 1, less_than_or_equal_to: 100 }
end
Цей код перевіряє, чи є значення поля price числом та чи знаходиться в межах від 1 до 100. Якщо значення не задовольняє ці умови, то виникає помилка валідації та об'єкт не зберігається в базі даних.
- перевіряє, що довжина поля знаходиться в певному діапазоні. Наприклад, ви можете вимагати, щоб заголовок новини був не менше 10 символів і не більше 100 символів.
Приведемо приклад валідації довжини поля "name" моделі "User", який обмежує довжину рядка від 3 до 50 символів:
class User < ActiveRecord::Base
validates :name, length: { in: 3..50 }
end
Також, ви можете використовувати інші опції валідації довжини, такі як minimum, maximum та is. Наприклад, такий код обмежує довжину поля "description" моделі "Product" до максимальної довжини 500 символів:
class Product < ActiveRecord::Base
validates :description, length: { maximum: 500 }
end
Окрім вищенаведених типів валідацій, в Ruby on Rails є багато інших вбудованих валідацій, а також можливість визначення власних.
Колбеки - це методи, які автоматично викликаються при виникненні певних подій моделі, таких як збереження, оновлення або видалення. Колбеки використовуються для автоматичної обробки даних перед їх збереженням або після цього, для забезпечення правильного функціонування додатку. У Ruby on Rails є багато вбудованих колбеків, які можна використовувати, а також можливість створювати власні.
Існують такі типи часто використовуванх колбеків(але це ще не всі):
1. before_validation: виконується перед виконанням валідації моделі.
2. after_validation: виконується після виконання валідації моделі.
3. before_save: виконується перед збереженням моделі.
4. after_save: виконується після збереження моделі.
5. before_create: виконується перед створенням нового запису в базі даних.
6. after_create: виконується після створення нового запису в базі даних.
7. before_update: виконується перед оновленням запису в базі даних.
8. after_update: виконується після оновлення запису в базі даних.
9. before_destroy: виконується перед видаленням запису з бази даних.
10. after_destroy: виконується після видалення запису з бази даних.
Наприклад, якщо вам потрібно додати автоматичний обробник після збереження моделі, ви можете використовувати колбеки після збереження. Розглянемо приклад:
class Post < ActiveRecord::Base
after_save :send_notification
private
def send_notification
# the logic of sending a message
end
end
У цьому прикладі після збереження моделі Post викликається метод send_notification, який відправляє повідомлення.
Є ще декілька методів колбеків, які можуть бути корисними при роботі з моделями в Ruby on Rails. Один з таких методів - before_validation. Цей метод дозволяє виконати певні дії перед проведенням валідації моделі. Наприклад, ви можете змінити значення деякого атрибута, який згодом буде валідований. Ось приклад використання методу before_validation:
class User < ActiveRecord::Base
before_validation :normalize_email
validates :email, presence: true, uniqueness: true
private
def normalize_email
self.email = email.downcase.strip
end
end
В цьому прикладі метод before_validation викликає приватний метод normalize_email, який змінює значення атрибуту email перед проведенням валідації. Це допомагає уникнути проблем з некоректними або неправильно введеними email-адресами.
Методи before_update та after_update виконуються перед і після оновлення запису в базі даних відповідно. Для прикладу, якщо ми хочемо підрахувати кількість коментарів до запису після кожного оновлення, ми можемо використовувати метод after_update. Ось як це можна зробити:
class Post < ApplicationRecord
has_many :comments
after_update :update_comments_count
private
def update_comments_count
self.comments_count = comments.count
save
end
end
У цьому прикладі метод update_comments_count викликається після оновлення запису в базі даних і оновлює лічильник коментарів для цього запису.
Іншим методом колбеків є before_destroy, який виконується перед видаленням запису з бази даних. Наприклад, якщо ми хочемо перевірити, чи не має запис коментарів, перед його видаленням, ми можемо використати метод before_destroy:
class Post < ApplicationRecord
has_many :comments
before_destroy :check_for_comments
private
def check_for_comments
unless comments.empty?
errors.add(:base, "Cannot delete post with comments")
throw :abort
end
end
end
У цьому прикладі метод check_for_comments викликається перед видаленням запису з бази даних і перевіряє, чи не має запис коментарів. Якщо у запису є коментарі, він додає помилку до об'єкту помилок моделі і викликає метод throw :abort, щоб відмінити видалення запису.
Використання багатьох колбеків на моделлях може стати причиною труднощів в розумінні та підтримці коду. Колбеки можуть бути складними у розумінні через те, що вони автоматично викликаються під час визначених подій, і не завжди очевидно, які методи та дії будуть виконуватися в певний момент.
Багато колбеків також можуть призвести до зменшення продуктивності та ефективності додатка. Це пов'язано з тим, що кожен колбек, незалежно від того, чи потрібен він на даному етапі виконання, виконується, що може призвести до зайвих запитів до бази даних та зниження швидкості відповіді додатка. В загальному - не вартує ними зловживати.
У цій статті ми розглянули моделі у фреймворку Ruby on Rails, їх структуру та основні методи. Моделі є важливим елементом веб-додатка, оскільки вони відповідають за взаємодію з базою даних. Завдяки використанню моделей ми можемо легко створювати, зчитувати, оновлювати та видаляти дані з бази даних.