我正在与Django import / export库一起使用XLSX / CSV文件批量上传模型。
我有两种模式-公司和竞争对手。竞争对手与公司有多对多关系。我希望管理员用户能够上传一堆竞争对手的名称,并能够选择他们都对应的公司。我要在另一列中标记与之对应的公司的ID。我该怎么办?
我已经按照图书馆Getting Started页上的说明进行操作,但我只是无法保留公司ID。
这些是我的模特:
# app/models.py
from django.db import models
class Company(models.Model):
name = models.CharField(max_length=200)
website = models.CharField(max_length=200)
class Competitor(models.Model):
company = models.ForeignKey(
Company,
on_delete=models.CASCADE,
verbose_name='The related company'
)
competitor_name = models.CharField(max_length=200)
competitor_website = models.CharField(max_length=200)
在forms.py
中,我定义了自定义表单,允许用户从已定义的公司记录列表中进行选择。
# app/forms.py
from django import forms
from import_export.admin import ImportForm, ConfirmImportForm
from .models import Company, Competitor
class CompetitorImportForm(ImportForm):
company = forms.ModelChoiceField(
queryset=Company.objects.all(),
required=True
)
class CompetitorConfirmImportForm(ConfirmImportForm):
company = forms.ModelChoiceField(
queryset=Company.objects.all(),
required=True
)
在resources.py
上设置我的进出口资源
from import_export import resources
from .models import Company, Competitor
class CompanyResource(resources.ModelResource):
class Meta:
model = Company
class CompetitorResource(resources.ModelResource):
class Meta:
model = Competitor
skip_unchanged = True
report_skipped = True
在admin.py
中,我将ImportExportMixIn
细分为自定义界面并允许公司选择。
# app/admin.py
from django.contrib import admin
from import_export.admin import ImportExportModelAdmin, ImportExportMixin
from .models import Company, Competitor
from .forms import CompetitorImportForm, CompetitorConfirmImportForm
from .resources import CompanyResource, CompetitorResource
class CompanyAdmin(ImportExportModelAdmin):
list_display = ('id', 'name', 'website', 'theme', 'active', 'frequency')
resource_class = CompanyResource
class CustomCompetitorAdmin(ImportExportMixin, admin.ModelAdmin):
resource_class = CompetitorResource
def get_import_form(self):
return CompetitorImportForm
def get_confirm_import_form(self):
return CompetitorConfirmImportForm
def get_form_kwargs(self, form, *args, **kwargs):
# pass on `author` to the kwargs for the custom confirm form
if isinstance(form, CompetitorImportForm):
if form.is_valid():
company = form.cleaned_data['company']
kwargs.update({'company': company.id})
return kwargs
admin.site.register(Company, CompanyAdmin)
admin.site.register(Competitor, CustomCompetitorAdmin)
我当前收到的错误消息是:
Line number: 1 - (1048, "Column 'company_id' cannot be null")
因为我的竞争对手模型中不允许包含null。但是,如果它会从所选公司提取ID,就不会为空。
答案 0 :(得分:0)
经过反复试验,我遵循this guide找到了解决方案。
首先在before_import_row
的资源类中使用resources.py
钩子。
# app/resources.py
class CompetitorResource(resources.ModelResource):
def __init__(self, request=None):
"""
This class uses Django sessions to carry the 'company' value from the import form to the import confirmation
screen. We're not using sessions, so this gives another way to get the extra request data in. It works for
both the dry-run preview and the actual import.
:param request: Since 'self.request' doesn't exist in the default ModelResource we have to install it by giving
competitor resource a custom constructor.
"""
super(CompetitorResource, self).__init__()
self.request = request
class Meta:
model = Competitor
export_order = ('company', 'competitor_name')
skip_unchanged = True
report_skipped = True
def before_import_row(self, row, **kwargs):
company = self.request.POST.get('company', None)
if company:
self.request.session['import_context_company'] = company
else:
# If this raises a KeyError we want to know about it. It means that we got to a point of importing data
# without company context, and we do not want to continue.
try:
company = self.request.session['import_context_company']
except KeyError as e:
raise Exception(f'Company context failure on row import, check resources.py for more info: {e}')
row['company'] = company
然后使用admin.py
钩子将其插入get_resource_kwargs
。
# app/admin.py
class CustomCompetitorAdmin(ImportExportModelAdmin):
list_display = ('id', 'competitor_name', 'competitor_website', 'company')
resource_class = CompetitorResource
def get_import_form(self):
return CompetitorImportForm
def get_confirm_import_form(self):
return CompetitorConfirmImportForm
def get_resource_kwargs(self, request, *args, **kwargs):
"""
The request isn't usually passed to the ModelResource constructor, so we need to add it to the kwargs dict for
that call. Fortunately, Django Import/Export has a dedicated hook for that.
:param request:
:param args:
:param kwargs:
:return:
"""
rk = super().get_resource_kwargs(request, *args, **kwargs)
rk['request'] = request
return rk
我现在可以从表单的下拉列表中选择一个外键字段,并将其作为表列。