Речь пойдет о методе bulk_create(), и не только. Когда у нас обычная таблица в базе данных без каких-либо связей всё просто. Но с "many to many" всё оказалось очень не просто.
Итак? у нас есть некая заготовка интернет-магазина, в которой есть продукты и есть заказы (модели Product и Order).
Для примера - модель Product:
В такую таблицу, массово занести данные о новых продуктах - нет проблем! Берем файл типа CSV, с перечислением новых продуктов, что нужно внести в БД:
Как можно заметить, в файле указаны основные поля по тем продуктам, что будут внесены в БД, при желании можно поля заполнить все, но пока достаточно и этого.
Итак, файл с продуктами, которые будут загружены в БД, мы подготовили. Теперь его нужно загрузить, у меня это реализовано через TextIOWrapper:
Полученную переменную (csv_file) преобразуем в словарь, при помощи DictReader(), из которого создадим список продуктов, вот таким методом:
Получается вот такой список, из экземпляров "продуктов":
Который вставляем в метод bulk_create()
Product.objects.bulk_create(products)
и создаем сколько угодно много продуктов (у меня в файле только два).
Казалось бы, все просто! Но, у меня есть не только "продукты", а еще и "заказы"! Которые так же надо научиться добавлять в базу данных, при помощи CSV-файлов с заказами.
Тут уже в полях появляются связи:
И уже не получается вот так просто, как с "продуктами", распаковать словарь в список заказов. Что то типа:
Уже с полем "user" у меня появляется проблема - программе нужен экземпляр класса User, на что она и ругается, это решается методом добавления префикса "_id" после "user".
Но если переменная products, в таком виде, как на скриншоте, присутствует в программе и она активна, то выпадает ошибка:
И вот хоть ты тресни - не понимаю как решить данную проблему, даже написал в чат курса об этом, но получил очень туманный ответ, который я прочитал с десяток раз, но идей в голове от этого не появилось:
Если для опытных программистов решение очевидно, то для меня это далеко не так.
Итак, буду искать решение проблемы самостоятельно...
Решение проблемы с загрузкой "продуктов" со связями "many to many":
Ошибка гласит: Нельзя напрямую присваивать наборы "многие ко многим". Используйте .set().
Я долго пытался решить проблему с помощью метода bulk_create. Целый день я искал решения проблемы в интернете и разных форумах программистов. Под вечер начало приходить осознание того что, возможно, я использую неподходящий метод!
Ведь что такое связь many-to-many в поле products, таблицы order?! Это отдельная таблица в которой указываются order_id и product_id.
Таким образом, чтобы появилась запись которая связывает id каких-то продуктов с заказом, нам нужно получить id этого заказа. То есть, если мы создадим несколько заказов методом bulk_create(), надо будет снова к ним возвращаться и добавлять к заказу продукты. Это получится двойная работа. Поэтому, я отказался в решении данной проблемы методом bulk_create() и решил проблему гораздо проще (или не проще):
То есть алгоритм работы такой:
- открываю файл csv, читаю его и прочитанное сохраняется в переменную
- переменная передается в цикл, в котором создаются заказы с данными из переменной row (строка с данными об одном заказе)
- после того как заказ создан - добавляю к этому заказу те продукты которые нужны, посредством еще одного цикла проходя по списку из id продуктов (возможно это тоже не самое лучшее решение, но на данный момент я не придумал ничего лучше)
Возможно кто-то спросит - а что за функция такая convert_str_to_int_list()?
Дело в том что сохраняя заказы из базы данных в файл csv, продукты записываются вот в такой формат:
Как можно видеть список продуктов это не то чтобы список, ну который в формате <class list>, это просто строка и я не могу ее передать напрямую в программу, нужно сначала отбросить из строки лишние символы
Затем получается список из "строковых" цифр и только потом "строковые цифры" становятся списком из целочисленных значений. Мудрёно. Согласен. Но этот тот вариант который мне пришел в голову на тот момент. Спустя время, возможно, я бы решил эту проблему иначе...
В общем, вечером я закончил данное домашнее задание, а на утро я прочитал сообщение в телеге:
Ну как-то так. Может статья получилась запутанной. Сорян.
И да: Это не руководство в последней инстанции, это лишь попытка осознать пройденный материал и поделиться с другими "студентами" своими умозаключениями. Дабы помочь в решении подобной проблемы.
#Direct assignment to the forward side of a many-to-many set is prohibited
#bulk_create