3,4K подписчиков

Django. Массовая загрузка в базу данных из файла csv

175 прочитали

Речь пойдет о методе bulk_create(), и не только. Когда у нас обычная таблица в базе данных без каких-либо связей всё просто. Но с "many to many" всё оказалось очень не просто.

Итак? у нас есть некая заготовка интернет-магазина, в которой есть продукты и есть заказы (модели Product и Order).

Для примера - модель Product:

Речь пойдет о методе bulk_create(), и не только. Когда у нас обычная таблица в базе данных без каких-либо связей всё просто. Но с "many to many" всё оказалось очень не просто. Итак?

В такую таблицу, массово занести данные о новых продуктах - нет проблем! Берем файл типа CSV, с перечислением новых продуктов, что нужно внести в БД:

Речь пойдет о методе bulk_create(), и не только. Когда у нас обычная таблица в базе данных без каких-либо связей всё просто. Но с "many to many" всё оказалось очень не просто. Итак?-2

Как можно заметить, в файле указаны основные поля по тем продуктам, что будут внесены в БД, при желании можно поля заполнить все, но пока достаточно и этого.

использованы модули
использованы модули

Итак, файл с продуктами, которые будут загружены в БД, мы подготовили. Теперь его нужно загрузить, у меня это реализовано через TextIOWrapper:

пытался найти в интернетах что же делает функция TextIOWrapper, но внятного ничего не понял, так что пусть останется это как в тумане (примерно понятно из кода, но словами не могу объяснить).
пытался найти в интернетах что же делает функция TextIOWrapper, но внятного ничего не понял, так что пусть останется это как в тумане (примерно понятно из кода, но словами не могу объяснить).

Полученную переменную (csv_file) преобразуем в словарь, при помощи DictReader(), из которого создадим список продуктов, вот таким методом:

Речь пойдет о методе bulk_create(), и не только. Когда у нас обычная таблица в базе данных без каких-либо связей всё просто. Но с "many to many" всё оказалось очень не просто. Итак?-5

Получается вот такой список, из экземпляров "продуктов":

Речь пойдет о методе bulk_create(), и не только. Когда у нас обычная таблица в базе данных без каких-либо связей всё просто. Но с "many to many" всё оказалось очень не просто. Итак?-6

Который вставляем в метод bulk_create()

Product.objects.bulk_create(products)

Речь пойдет о методе bulk_create(), и не только. Когда у нас обычная таблица в базе данных без каких-либо связей всё просто. Но с "many to many" всё оказалось очень не просто. Итак?-7

и создаем сколько угодно много продуктов (у меня в файле только два).

база данных до добавления новых продуктов
база данных до добавления новых продуктов
база данных после добавления новых продуктов
база данных после добавления новых продуктов

Казалось бы, все просто! Но, у меня есть не только "продукты", а еще и "заказы"! Которые так же надо научиться добавлять в базу данных, при помощи CSV-файлов с заказами.

Тут уже в полях появляются связи:

Речь пойдет о методе bulk_create(), и не только. Когда у нас обычная таблица в базе данных без каких-либо связей всё просто. Но с "many to many" всё оказалось очень не просто. Итак?-10

И уже не получается вот так просто, как с "продуктами", распаковать словарь в список заказов. Что то типа:

Речь пойдет о методе bulk_create(), и не только. Когда у нас обычная таблица в базе данных без каких-либо связей всё просто. Но с "many to many" всё оказалось очень не просто. Итак?-11

Уже с полем "user" у меня появляется проблема - программе нужен экземпляр класса User, на что она и ругается, это решается методом добавления префикса "_id" после "user".

эта запись работает если закоментировать переменную products, тогда происходит добавление данных из переменной test в базу данных, таблицу Orders
эта запись работает если закоментировать переменную products, тогда происходит добавление данных из переменной test в базу данных, таблицу Orders

Но если переменная products, в таком виде, как на скриншоте, присутствует в программе и она активна, то выпадает ошибка:

Речь пойдет о методе bulk_create(), и не только. Когда у нас обычная таблица в базе данных без каких-либо связей всё просто. Но с "many to many" всё оказалось очень не просто. Итак?-13

И вот хоть ты тресни - не понимаю как решить данную проблему, даже написал в чат курса об этом, но получил очень туманный ответ, который я прочитал с десяток раз, но идей в голове от этого не появилось:

Речь пойдет о методе bulk_create(), и не только. Когда у нас обычная таблица в базе данных без каких-либо связей всё просто. Но с "many to many" всё оказалось очень не просто. Итак?-14

Если для опытных программистов решение очевидно, то для меня это далеко не так.

Итак, буду искать решение проблемы самостоятельно...

прошло какое-то время, прежде чем я смог решить эту проблему
прошло какое-то время, прежде чем я смог решить эту проблему

Решение проблемы с загрузкой "продуктов" со связями "many to many":

Ошибка гласит: Нельзя напрямую присваивать наборы "многие ко многим". Используйте .set().

Я долго пытался решить проблему с помощью метода bulk_create. Целый день я искал решения проблемы в интернете и разных форумах программистов. Под вечер начало приходить осознание того что, возможно, я использую неподходящий метод!

Ведь что такое связь many-to-many в поле products, таблицы order?! Это отдельная таблица в которой указываются order_id и product_id.

Речь пойдет о методе bulk_create(), и не только. Когда у нас обычная таблица в базе данных без каких-либо связей всё просто. Но с "many to many" всё оказалось очень не просто. Итак?-16

Таким образом, чтобы появилась запись которая связывает id каких-то продуктов с заказом, нам нужно получить id этого заказа. То есть, если мы создадим несколько заказов методом bulk_create(), надо будет снова к ним возвращаться и добавлять к заказу продукты. Это получится двойная работа. Поэтому, я отказался в решении данной проблемы методом bulk_create() и решил проблему гораздо проще (или не проще):

Речь пойдет о методе bulk_create(), и не только. Когда у нас обычная таблица в базе данных без каких-либо связей всё просто. Но с "many to many" всё оказалось очень не просто. Итак?-17

То есть алгоритм работы такой:

  • открываю файл csv, читаю его и прочитанное сохраняется в переменную
  • переменная передается в цикл, в котором создаются заказы с данными из переменной row (строка с данными об одном заказе)
  • после того как заказ создан - добавляю к этому заказу те продукты которые нужны, посредством еще одного цикла проходя по списку из id продуктов (возможно это тоже не самое лучшее решение, но на данный момент я не придумал ничего лучше)

Возможно кто-то спросит - а что за функция такая convert_str_to_int_list()?

Дело в том что сохраняя заказы из базы данных в файл csv, продукты записываются вот в такой формат:

Речь пойдет о методе bulk_create(), и не только. Когда у нас обычная таблица в базе данных без каких-либо связей всё просто. Но с "many to many" всё оказалось очень не просто. Итак?-18

Как можно видеть список продуктов это не то чтобы список, ну который в формате <class list>, это просто строка и я не могу ее передать напрямую в программу, нужно сначала отбросить из строки лишние символы

Речь пойдет о методе bulk_create(), и не только. Когда у нас обычная таблица в базе данных без каких-либо связей всё просто. Но с "many to many" всё оказалось очень не просто. Итак?-19

Затем получается список из "строковых" цифр и только потом "строковые цифры" становятся списком из целочисленных значений. Мудрёно. Согласен. Но этот тот вариант который мне пришел в голову на тот момент. Спустя время, возможно, я бы решил эту проблему иначе...

В общем, вечером я закончил данное домашнее задание, а на утро я прочитал сообщение в телеге:

Речь пойдет о методе bulk_create(), и не только. Когда у нас обычная таблица в базе данных без каких-либо связей всё просто. Но с "many to many" всё оказалось очень не просто. Итак?-20

Ну как-то так. Может статья получилась запутанной. Сорян.

И да: Это не руководство в последней инстанции, это лишь попытка осознать пройденный материал и поделиться с другими "студентами" своими умозаключениями. Дабы помочь в решении подобной проблемы.

#Direct assignment to the forward side of a many-to-many set is prohibited

#bulk_create