Расширьте форму импорта django-import-export, чтобы указать фиксированное значение для каждой импортируемой строки - PullRequest
0 голосов
/ 14 сентября 2018

Я использую django-import-export 1.0.1 с интеграцией администратора в Django 2.1.1. У меня две модели

from django.db import models

class Sector(models.Model):
    code = models.CharField(max_length=30, primary_key=True)

class Location(models.Model):
    code = models.CharField(max_length=30, primary_key=True)
    sector = ForeignKey(Sector, on_delete=models.CASCADE, related_name='locations')

и их можно легко импортировать / экспортировать, используя ресурсы модели

from import_export import resources
from import_export.fields import Field
from import_export.widgets import ForeignKeyWidget

class SectorResource(resources.ModelResource):
    code = Field(attribute='code', column_name='Sector')
    class Meta:
        model = Sector
        import_id_fields = ('code',)

class LocationResource(resources.ModelResource):
    code = Field(attribute='code', column_name='Location')
    sector = Field(attribute='sector', column_name='Sector',
                   widget=ForeignKeyWidget(Sector, 'code'))
    class Meta:
        model = Location
        import_id_fields = ('code',)

и действия по импорту / экспорту могут быть интегрированы в администратор с помощью

from django.contrib import admin
from import_export.admin import ImportExportModelAdmin

class SectorAdmin(ImportExportModelAdmin):
    resource_class = SectorResource

class LocationAdmin(ImportExportModelAdmin):
    resource_class = LocationResource

admin.site.register(Sector, SectorAdmin)
admin.site.register(Location, LocationAdmin)

Для Reasons ™ я хотел бы изменить эту настройку так, чтобы можно было импортировать электронную таблицу Locations, которая не содержит столбец Sector; значение sector (для каждой импортируемой строки) должно быть взято из дополнительного поля в ImportForm в admin.

Такое поле действительно можно добавить, переопределив import_action на ModelAdmin, как описано в Расширение формы импорта администратора для django import_export . Следующий шаг, чтобы использовать это значение для всех импортированных строк, там отсутствует, и я не смог выяснить, как это сделать.

1 Ответ

0 голосов
/ 04 декабря 2018

РЕДАКТИРОВАТЬ (2): Решено с помощью сеансов.Наличие get_confirm_import_form хука все же действительно поможет в этом, но еще лучше будет иметь существующий ConfirmImportForm перенос по всем представленным полям и значениям из начальной формы импорта.

РЕДАКТИРОВАТЬ: Извините, я думал, что это было прибито, но мой собственный код не работал, а я думал, что это было.Это не не решает проблему передачи поля формы sector в ConfirmImportForm, что необходимо для завершения импорта.В настоящее время ищем решение, которое не включает вставку всего import_action() в подкласс ImportMixin.Хук get_confirm_import_form() очень поможет здесь.

Я все еще работаю над решением для себя, и когда у меня будет его, я тоже обновлю его.


Непереопределить import_action.Это большой сложный метод, который вы не хотите копировать.Что еще более важно, как я обнаружил сегодня: есть более простые способы сделать это.

Сначала (как вы упомянули) создайте пользовательскую форму импорта для Location, которая позволяет пользователю выбрать Sector:

class LocationImportForm(ImportForm):
    sector = forms.ModelChoiceField(required=True, queryset=Sector.objects.all())

В Resource API есть хук before_import_row(), который вызывается один раз для каждой строки.Итак, реализуйте это в своем классе LocationResource и используйте его для добавления столбца Sector:

def before_import_row(self, row, **kwargs):
    sector = self.request.POST.get('sector', None)
    if contract:
        self.request.session['import_context_sector'] = sector
    else:
        # if this raises a KeyError, we want to know about it.
        # It means that we got to a point of importing data without
        # contract context, and we don't want to continue.
        try:
            sector = self.request.session['import_context_sector']
        except KeyError as e:
            raise Exception("Sector context failure on row import, " +
                                 f"check resources.py for more info: {e}")
    row['sector'] = sector

( Примечание: Этот код использует сеансы Django для переноса sector значение из формы импорта на экран подтверждения импорта. Если вы не используете сеансы, вам нужно найти другой способ сделать это.)

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

Обратите внимание, что self.request не существует по умолчанию ModelResource - мы должны установить его, предоставив LocationResource пользовательскийКонструктор:

def __init__(self, request=None):
    super()
    self.request = request

(Не беспокойтесь о self.request, задерживающемся. Каждый экземпляр LocationResource не сохраняется после одного запроса.)

request isn 'Обычно t передается конструктору ModelResource, поэтому нам нужно добавить его в dict kwargs для этого вызова.К счастью, в Django Import / Export есть специальный хук для этого.Переопределите метод ImportExportModelAdmin get_resource_kwargs в LocationAdmin:

def get_resource_kwargs(self, request, *args, **kwargs):
    rk = super().get_resource_kwargs(request, *args, **kwargs)
    rk['request'] = request
    return rk

И это все, что вам нужно.

...