Хотя я не буду вдаваться в подробности, суть в том, что мы определяем контейнер pg, который будет использовать Postgres 14, доступный через порт по умолчанию, используя переменные среды для настройки пользователя и базы данных. и, наконец, создание тома, который позволит данным сохраняться между его запуском и выключением. Мы также добавляем папку mkdir db
или любой другой файловый менеджер, который вы используете. Запуск docker-compose up
запустит сервер. Опцию -d
можно использовать, если вы хотите запустить ее в фоновом режиме.
Теперь, когда ваша база данных работает, нам нужно настроить параметры базы данных, а также создать схему для нашей таблицы статей. Существует несколько сегментов для управления миграциями, однако я собираюсь просто сохранить и запустить SQL вручную. Если в вашем проекте будет больше нескольких таблиц, использование инструмента миграции может быть очень полезным, особенно для проектов, которые вы планируете сохранить в течение некоторого времени. Давайте создадим новую папку
CREATE SCHEMA IF NOT EXISTS "test" AUTHORIZATION "blog_user";
Технически нам это пока не нужно, однако это понадобится позже, в
CREATE TABLE IF NOT EXISTS "articles"
(
"id" BIGINT GENERATED ALWAYS AS IDENTITY NOT NULL
PRIMARY KEY,
"title" TEXT NOT NULL,
"body" TEXT NOT NULL,
"created_at" TIMESTAMP NOT NULL,
"updated_at" TIMESTAMP NOT NULL,
"deleted_at" TIMESTAMP NULL
);
Мы просто храним некоторые стандартные значения вместе с временными метками и целочисленным первичным ключом с автоинкрементом.
Поскольку наш сервер Postgres работает внутри контейнера Docker, нам нужно использовать команду docker для запуска файлов миграции из контейнера:
docker exec -it pg psql blog_user -d postgres -f /migrations/ 000_setup.sql
docker exec -it pg psql blog_user -d postgres -f /migrations /001_articles.sql
Сохраняющиеся статьи
Продолжая с того места, на котором мы остановились в предыдущем разделе, мы работали над сохранением наших статей в базе данных.
Первое, что нам нужно сделать, это включить модуль DB::Serializable
в нашу сущность Article
. Как упоминалось ранее, этот модуль позволяет нам создать его экземпляр из DB::ResultSet
, который представляет собой результат запроса, сделанного к базе данных.
Поскольку у нас есть несколько вещей, которые должны произойти, прежде чем статья будет фактически сохранена, давайте продолжим и создадим несколько абстракций для решения этой проблемы. Конечно, если бы мы использовали ORM, у нас были бы встроенные способы сделать это, но будет полезно увидеть, как это можно сделать довольно легко, а также это станет хорошим переходом к другой функции Athena — DI.
Учитывая, что все, что нам нужно, это запустить некоторую логику перед сохранением чего-либо, мы можем просто создать метод с именем #before_save
, который мы можем вызывать. Как вы уже догадались — перед тем, как мы сохраним объект в базу данных. В конечном итоге это будет выглядеть так:
protected def before_save : Nil
if @id.nil?
@created_at = Time.utc
end
@updated_at = Time.utc
end
Я сделал метод защищенным, поскольку он более внутренний и не является частью общедоступного API. В случае новой записи, когда идентификатора еще нет, мы устанавливаем update_at
обновляется при каждом сохранении, учитывая, что именно для этого и предназначена эта временная метка.
В некоторых Crystal ORM, а также в Ruby ActiveRecord
обычно имеется метод #save
непосредственно на объекте, который обрабатывает его сохранение в базе данных. Лично я не являюсь поклонником этого подхода, поскольку считаю, что он нарушает принцип DB::Serializable
.