Импорт Django — экспорт повторяющихся строк при многократном импорте одного и того же файла

#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 новые).