#django #django-import-export
#django #django-импорт-экспорт
Вопрос:
Я создаю инструмент, который позволяет пользователям загружать файлы CSV для обновления базы данных с помощью этого django-import-export
инструмента. Я загрузил свой тестовый CSV-файл с одной строкой данных, затем загрузил его снова и получил дублированную строку (с новым первичным ключом, но все остальные значения одинаковы). row.import_type
Значение «обновлено», но единственное, что обновляется, — это идентификатор.
Затем я загружаю тот же файл в третий раз и получаю сообщение об ошибке:
app.models.Role.MultipleObjectsReturned: get() returned more than one Role -- it returned 2!
(Кстати, я очень ценю восклицательный знак в этом сообщении об ошибке.)
В идеале я бы получил пропущенную строку при втором импорте и третьем импорте файла. Полагаю, я бы справился с ошибкой. Содержимое файла:
Sales Role,System Role,System Plan,id
Sales Rep,Account Executive,951M-NA,
Это формат, который пользователи получают при экспорте набора данных csv. В идеале они должны экспортировать файл, изменить несколько столбцов (кроме имени, которое является полем import_id_field) и повторно загрузить данные.
В app/resources.py:
class RoleResourec(resources.ModelResource):
name = Field(attribute='name', column_name="Sales Role")
default_role = Field(attribute='default_role', column_name="System Role")
default_plan = Field(attribute='default_plan', column_name="System Plan")
class Meta:
models=Role
fields= ('id', 'name', 'default_role', 'default_plan')
import_id_fields = ('name',)
skip_unchanged = True
Из того, что я могу сказать, при втором импорте get_or_init_instance()
метод не находит объект из первого импорта, но затем находит их на третьем. Я ничего не сделал с ресурсом, чтобы настроить рабочий процесс импорта, как описано на странице рабочего процесса импорта данных.
Что здесь не так? Нужно ли мне настраивать рабочий процесс импорта или я пропустил еще один обязательный атрибут в ресурсе?
Ответ №1:
Логика пропустит строку только в том случае, если все объявленные поля одинаковы как в импортированной строке, так и в сохраненном объекте. Если какое-либо поле отличается, то будет выполнено обновление.
Чтобы это сработало, поля, которые вы объявляете, import_id_fields
должны быть уникальным совпадением для строки, в противном случае вы получите MultipleObjectsReturned
.
В вашем случае, если создаются повторяющиеся строки, это должно означать, что name
при втором запуске они отсутствуют в БД. Я предполагаю, что вы не переопределили ModelInstanceLoader
или работаете в массовом режиме, потому что это нарушило бы логику пропуска строк.
По умолчанию import_id_fields
для него установлен идентификатор строки, поэтому, если вы можете включить его в свой экспорт, то гарантированно получите уникальную строку. Очевидно, что пользователи не должны изменять это поле, иначе вы получите дубликаты.
MultipleObjectsReturned
Ошибка возникает отсюда, и это просто вызов Role.objects.get(name=<n>)
.
Комментарии:
1. Я набираю все в SO вручную, так что это источник опечатки (я никогда не могу вспомнить, как копировать из vim в системный буфер обмена). Я действительно нашел ошибку, после того как усердно работал, чтобы выяснить, как подклассировать загрузчик экземпляра, чтобы я мог ввести точку останова. Удаление ‘id’ из списка полей в Meta устранило проблему. Я напишу это как ответ.
2. если вы используете PyCharm, то легко добавлять точки останова в код библиотеки — нет необходимости создавать подклассы
3. Я нахожусь в vim в виртуальной среде, и я действительно начал копаться в исходном коде, чтобы выяснить, где поставить точку останова, но затем нашел ее в своей системе, бла-бла-бла. Короче говоря, я боюсь возиться с чужой библиотекой, вот почему я подумал о подклассах.
Ответ №2:
Вот удивительно простое решение:
class RoleResourec(resources.ModelResource):
name = Field(attribute='name', column_name="Sales Role")
default_role = Field(attribute='default_role', column_name="System Role")
default_plan = Field(attribute='default_plan', column_name="System Plan")
class Meta:
models=Role
fields= ('name', 'default_role', 'default_plan')
import_id_fields = ('name',)
skip_unchanged = True
Все, что мне нужно было сделать, это удалить 'id'
из списка полей в Meta
классе, и теперь я получаю ожидаемое поведение.
Я могу экспортировать этот файл в CSV (и id
столбец не отображается), редактировать список и повторно загружать, а система пропускает и обновляет и даже добавляет новые вещи по мере необходимости.
Комментарии:
1. Я не понимаю, как это исправляет проблему, поэтому вы можете маскировать истинную проблему. Если ‘name’ действительно уникально, то оно должно совпадать с этой уникальной строкой, а затем пропускать или обновлять в зависимости от того, остались ли другие поля неизменными. Я обновил свой ответ, чтобы добавить больше деталей.
2. Я очищаю таблицы приложения, запустив
python manage.py migrate <app> zero
, а затемpython manage.py migrate <app>
, что дает мне новый старт во всем. Я загружаю файл csv со строкой заголовка и строкой данных. Я подтверждаю, что в таблице только одна строка. Затем я импортирую точно такой же файл и получаю две строки одних и тех же данных (толькоid
новые).