Вопрос или проблема
Некоторое время назад я прошел учебник по Node JS, который сильно полагался на использование ORM для доступа к базе данных. Несмотря на то, что мне вообще не понравился Node JS (он просто не для меня), мне понравилась концепция ORM. Так как Perl является моим языком сценариев по выбору, я решил углубиться в DBIx::Class, или DBIC. И я как-то застрял, пытаясь получить доступ к связанным данным с использованием соединений.
Я изучаю страницы MAN и в настоящее время читаю DBIx::Class::Manual::Joining.
Создал себе базу данных sqlite:
CREATE TABLE IF NOT EXISTS `cd` (
`id` integer primary key NOT NULL UNIQUE,
`cd_title` TEXT NOT NULL UNIQUE,
`year` Text Not Null
);
CREATE TABLE IF NOT EXISTS `track` (
`id` integer primary key NOT NULL UNIQUE,
`cd_id` INTEGER NOT NULL,
`track_title` TEXT NOT NULL UNIQUE,
FOREIGN KEY(`cd_id`) REFERENCES `cd`(`id`)
);
Insert Into cd (id, cd_title, year) Values
(1, 'CD 1', '2004'),
(2, 'CD 2', '1995'),
(3, 'CD 3', '2014'),
(4, 'CD 4', '2024')
;
Insert Into track (id,cd_id,track_title) Values
(1,1,'Track 1 CD 1'),
(2,1,'Track 2 CD 1'),
(3,1,'Track 3 CD 1'),
(4,1,'Track 4 CD 1'),
(5,1,'Track 5 CD 1'),
(6,2,'Track 1 CD 2'),
(7,2,'Track 2 CD 2'),
(8,2,'Track 3 CD 2'),
(9,3,'Track 1 CD 3'),
(10,3,'Track 2 CD 3'),
(11,3,'Track 3 CD 3'),
(12,3,'Track 4 CD 3'),
(13,3,'Track 5 CD 3'),
(14,4,'Track 2 CD 4'),
(15,4,'Track 3 CD 4'),
(16,4,'Track 4 CD 4'),
(17,4,'Track 5 CD 4')
;
По сути, 4 CD, каждый из которых имеет несколько треков. Файлы схемы были созданы с использованием dbicdump:
dbicdump -o dump_directory=. Model::Schema dbi:SQLite:cds.sqlite
Файлы схемы довольно длинные, я вас от этого избавлю. Но так как dbicdump создал их, я предполагаю, что они корректны.
После этого я написал себе небольшой тестовый скрипт:
#! /usr/bin/env perl
use v5.20;
use Data::Dumper;
use DBIx::Class;
use lib '.';
use Model::Schema qw();
my $schema = Model::Schema->connect('dbi:SQLite:cds.sqlite');
my $cds = $schema->resultset('Cd')->search(
{
'cd_title' => { like => 'CD%'}
},
{
join => 'tracks',
order_by => ['tracks.id'],
}
);
for my $cd ($cds->all()){
say $cd->cd_title;
say $cd->get_column('track_title');
}
Что-то вроде того, как говорит страница MAN. Но я не могу заставить это работать!
CD 1
DBIx::Class::Row::get_column(): No such column 'track.track_title' on Model::Schema::Result::Cd at ./test.pl line 24
shell returned 2
Он всё время жалуется, что не может найти столбец ‘track_title’ в таблице ‘track’. Я уже пробовал все возможные вариации имени столбца. Например, добавить имя таблицы, как ‘track.track_title’, но читая сообщение об ошибке, кажется, что это всё равно делает само по себе.
Похоже, он собирает все данные. Когда я убираю вторую строку ‘say’, он на самом деле выводит заголовок CD для каждого трека. Соединение, похоже, действительно работает.
Проверяя файл схемы ‘Track.pm’, я вижу, что добавляются следующие столбцы:
__PACKAGE__->add_columns(
"id",
{ data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
"cd_id",
{ data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
"track_title",
{ data_type => "text", is_nullable => 0 },
);
Еще одна вещь, которую я заметил: пример на странице MAN использует сортировку по ‘tracks.id’. Где ‘tracks’ — это имя связи, а не имя таблицы. Вот почему я пробовал ‘$cd->get_column(‘tracks.track_title’)’ в ‘say’, без эффекта.
Я действительно хотел бы преодолеть это препятствие.
Петер
Ответ или решение
Основы Perl DBIx::Class: Работа с отношениями и объединениями
Недавно вы погрузились в использование DBIx::Class (DBIC), одного из самых мощных и популярных ORM (Object-Relational Mapping) инструментов для Perl. Вы столкнулись с определенными трудностями при работе с объединениями для доступа к связанным данным. Давайте разберем ваш вопрос и найдем решение.
Общий обзор вашей ситуации
Вы создали базу данных SQLite с таблицами cd
и track
, где каждая запись в таблице cd
может иметь несколько записей в таблице track
. Это нормальная реляционная структура, которая хорошо подходит для обработки с помощью ORM. Затем вы использовали команду dbicdump
для генерации файлов схемы, которые обычно создают необходимые классы для работы с базой данных.
Ваша структура выглядит следующим образом:
- Таблица
cd
: Содержит информацию о музыкальных альбомах. - Таблица
track
: Содержит информацию о песнях, связанных с определенным альбомом (поcd_id
).
Проблема с объединением и доступом к связанным данным
Ваша ошибка проявляется, когда вы пытаетесь получить track_title
через $cd->get_column('track_title')
. Проблема заключается в том, что метод get_column
работает только с колонками текущей модели, в данном случае с Cd
, но не с связанными моделями (например, Track
).
Чтобы исправить вашу проблему и корректно получать информацию о треках, вам нужно использовать метод, который возвращает связанные записи. Универсальная практика – использовать метод tracks
для получения данных о связанных записях.
Правильный способ получения связанных данных
Вот как можно исправить ваш тестовый скрипт:
#! /usr/bin/env perl
use v5.20;
use Data::Dumper;
use DBIx::Class;
use lib '.';
use Model::Schema qw();
# Подключение к базе данных
my $schema = Model::Schema->connect('dbi:SQLite:cds.sqlite');
# Получение записей об альбомах
my $cds = $schema->resultset('Cd')->search(
{
'cd_title' => { like => 'CD%' }
},
{
join => 'tracks',
order_by => ['tracks.id'],
}
);
# Вывод информации об альбомах и связанных треках
for my $cd ($cds->all()) {
say $cd->cd_title; # Печать названия альбома
# Получение всех треков, связанных с альбомом
my @tracks = $cd->tracks; # Вызываем связь с треками
for my $track (@tracks) {
say $track->track_title; # Печать названия каждого трека
}
}
Объяснение изменений
- Получение треков: Вместо использования
get_column
, мы вызываем методtracks
, который возвращает все связанные записи из таблицыtrack
для данного альбома. - Итерация по трекам: После получения связанной коллекции треков, мы можем перебрать их с помощью цикла, выводя
track_title
.
Заключение
С помощью этого подхода вы сможете без проблем работать с связанными данными в DBIx::Class. Объединение и работа с отношениями – ключевые возможности ORM, и, изучая их, вы улучшите свои навыки в Perl и управлении данными. Если у вас возникнут дополнительные вопросы или сложности, не стесняйтесь обращаться за поддержкой. Удачи в вашем дальнейшем изучении DBIx::Class!