> For the complete documentation index, see [llms.txt](https://evgenii-afanasev.gitbook.io/ruby-course/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://evgenii-afanasev.gitbook.io/ruby-course/12_database/8_db_access_from_ruby.md).

# Работа с БД из кода

## Sequel для выполнения запросов

Настало время для работы с SQL прямо из Ruby кода. Для того, чтобы это было возможно, необходимо установить библиотеку sequel

```ruby
gem 'sequel', '~> 5.69'
```

Также СУБД бывают различные и каждая из СУБД предоставляет свой собственный API для различных языков программирования. В Ruby для работы с SQLite необходим дополнительный гем

Теперь мы можем получить доступ до Sequel в нашем коде.

Для того, чтобы начать посылать запросы, нам нужно установить соединение с базой данных:

```ruby
require 'sequel'

DB = Sequel.connect('sqlite://shop.db') # указываем путь до файла БД относительно .rb файла
# Также можно использовать именованные параметры на основе Hash. В данном случае необходимо обязательно укзаать значение для adapter
# DB = Sequel.connect(adapter: :sqlite, user: 'user', password: 'password', host: 'host', port: port, database: 'database_name.db')
```

### DDL операции

Sequel предоставляет нам удобный DSL (domain-specific language) для выполнения DDL операций. Например, так будет выглядеть операция для создания таблицы items:

```ruby
# В качестве аргумента передает название таблицы в виде символа, а также блок кода
DB.create_table :items do
  primary_key :id # внутри которого с помощью методов описываем нашу таблицу
  column :manufacture, String, null: false
  column :model, String, null: false 
  column :color, String, null: false
  column :price, Integer null: false,
  column :date_added, Time, null: false
end
```

В данном примере мы вызывали метод `.column` в который передавали:

1. название колонки
2. класс Ruby, который имеет сопоставление с типами SQL
3. параметры, передаваемые в виде хэша

## DML операции

После того как таблица создана, рассмотрим пример запроса для получения товаров стоимостью выше 10 000 рублей отсортированные по цене в порядке убиывания

```ruby
table = DB[:items]

result = table
  .where{price > 10_000} # создаем блок кода с передачей условия в него
  .order(Sequel.desc(:price)) # вызываем метод для сортировки

p result # Получаем массив значений в виде хэша
```

Мы можем также добавлять значения в нашу таблицу:

```ruby
table.insert(
  manUfacture: "apple", 
  model: "Iphone 14", 
  color: "Red", 
  price: 120_000,
  date_added: Time.now
) 
```

Точно также как и удалять:

```ruby
table.where{id == 1}.delete
```

## Sequel как ORM

Чаще всего встречается использование ORM для работы с базой данных. ORM - Object Relational Mapping. По сути, это работа с таблицами как с Ruby объектами, где каждый атрибут объекта - атрибут таблицы. Давайте создадим такой класс для нашей таблицы items.

```ruby
require 'sequel'

# Sequel обязывает нас называть наши классы как таблицы в единственном числе
# Он использует название класса для определния названия таблицы
# если нам нужно изменить это поведение, 
# то необходимо наследоваться с использованием названия в качестве аргумента (Sequel::Model(:items))
class Item < Sequel::Model
end

Item.where{price > 10_000}.order(Sequel.desc(:price)) 
```

Важно при этом создать объект соединения с помощью `Sequel.connect(...)`, так как при создании модели будет использоваться неявно(без использования переменной) ранее созданное соеднинение. Сначала создаем соединение, потом инициализируем модели с помощью `require`.

### Ассоциации

Кроме того, в моделях нам доступны ассоциации. Ассоциации позволяют одной модели ссылаться на другие в случае, если между ними есть связь в виде FK.

Рассмотрим основные связи:

* many\_to\_one - ассоциация описывает отношение "многие к одному" между двумя таблицами в базе данных. Это означает, что одна запись в "родительской" таблице может быть связана с множеством записей в "дочерней" таблице, но каждая запись "дочерней" таблицы имеет связь только с одной записью из "родительской" таблицы.

Пример использования ассоциации many\_to\_one для двух таблиц: `students` и `classrooms`. Здесь один класс (classroom) может иметь много студентов (students), но каждый студент может быть только в одном классе.

Структура двух таблиц:

`students`

| id | name  | classroom\_id |
| -- | ----- | ------------- |
| 1  | Alice | 1             |
| 2  | Bob   | 1             |

`classrooms`

| id | name       |
| -- | ---------- |
| 1  | Math Class |

Объявление моделей с ассоциацией в Sequel:

```ruby
class Student < Sequel::Model
  many_to_one :classroom
end

class Classroom < Sequel::Model
  one_to_many :students
end
```

Теперь вы можете получить класс (classroom) каждого студента с помощью ассоциации many\_to\_one:

```ruby
student = Student.where(id: 1).first
classroom = student.classroom
puts classroom.name # Выводит: "Math Class"
```

В данном случае, ассоциация many\_to\_one позволила установить связь между таблицами таким образом, что для каждого объекта Student можно легко получить соответствующий объект `Classroom`, без необходимости писать дополнительные SQL-запросы.

* one\_to\_many - ассоциация описывает отношение "многие ко многим" между двумя таблицами в базе данных. Это означает, что одна запись в первой таблице может быть связана с несколькими записями во второй таблице, и наоборот, одна запись во второй таблице может быть связана с несколькими записями в первой таблице.

Для реализации такого отношения обычно используется третья таблица, называемая "связующей" (join) таблицей, которая содержит внешние ключи (foreign keys) для обеих таблиц и связывает их записи.

Пример использования ассоциации many\_to\_many для таблиц `students`, `courses`, и связующей таблицы `enrollments`. Здесь один студент может быть записан на несколько курсов, и каждый курс может иметь несколько студентов.

Структура таблиц:

`students`

| id | name  |
| -- | ----- |
| 1  | Alice |

`courses`

| id | name    |
| -- | ------- |
| 1  | Math    |
| 2  | History |

`enrollments`

| student\_id | course\_id |
| ----------- | ---------- |
| 1           | 1          |
| 1           | 2          |

Объявление моделей с ассоциацией в Sequel:

```ruby
class Student < Sequel::Model
  many_to_many :courses, join_table: :enrollments
end

class Course < Sequel::Model
  many_to_many :students, join_table: :enrollments
end
```

Теперь вы можете получить все курсы, на которые записан студент, или все студенты, которые записаны на курс, с помощью ассоциации many\_to\_many:

```ruby
student = Student.where(id: 1).first
courses = student.courses
courses.each { |course| puts course.name } # Выводит: "Math", "History"

course = Course.where(id: 1).first
students = course.students
students.each { |student| puts student.name } # Выводит: "Alice"
```

Таким образом, ассоциация many\_to\_many позволяет установить связь многие ко многим между двумя таблицами с использованием связующей таблицы, и предоставляет удобный способ получения и работы с этими связями на уровне объектов Ruby и моделей Sequel.

Обратите внимание на следующие правила и рекомендации по составлению названий таблиц, классов и ассоциаций в контексте таблиц базы данных и объектно-реляционного отображения, такого как Sequel:

1. Таблицы:
   * Обычно названия таблиц должны быть во множественном числе и в нижнем регистре (например, `students`, bo\`oks).
   * Используйте подчеркивания для разделения слов в названиях таблиц (например, book\_authors, order\_items).
   * Старайтесь делать названия короткими и ясными, чтобы они отражали назначение таблицы.
2. Классы и модели:
   * Имена классов и моделей должны быть в единственном числе и в формате CamelCase (например, Student, Book).
   * Каждое слово в имени класса должно начинаться с заглавной буквы и не содержать подчеркиваний (например, BookAuthor, OrderItem).
3. Ассоциации:
   * Названия ассоциаций `one_to_many` и `many_to_many` должны быть во множественном числе и в нижнем регистре, чтобы они соответствовали названиям таблиц (например, `students`, `books`).
   * Названия ассоциации `many_to_one` должно быть в единственном числе и в нижнем регистре, чтобы оно соответствовали названию класса и модели в нижнем регистре (например, `student`, `book`).

В случае если названия классов или ассоциаций отличаются от названий таблиц или не выполняют какое-либо из выше перечисленных правил, то необходимо будет вручную проставлять нзавания классов/таблиц. В противном случае работать это не будет.

## Задание university

## Задание todo\_api

Напишите используя Sinatra и Sequel приложение для работы со списком дел, которое:

* Добавляет новое дело в базу данных
* Изменяет дело по его идентификатору
* Удаляет дело по его идентификатору
* Показывает список дел **постранично**

Рекомендации:

* Требований как должна выглядеть модель дела здесь нет, поэтому можете включить свою фантазию и придумать
* Для того, чтобы модель Sequel могла бы быть преобразована в JSON, вам необходимо [включить JSON сериализацию](https://sequel.jeremyevans.net/rdoc-plugins/classes/Sequel/Plugins/JsonSerializer.html)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://evgenii-afanasev.gitbook.io/ruby-course/12_database/8_db_access_from_ruby.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
