我想将django-import-export
用于基于类的视图。
在https://django-import-export.readthedocs.org/en/latest/getting_started.html的文档中,我看到了导出为csv的示例
>>> dataset = BookResource().export()
>>> print dataset.csv
id,name,author,author_email,imported,published,price,categories
2,Some book,1,,0,2012-12-05,8.85,1
但是如果我想返回Excel文件,我应该使用哪个基于类的视图?只需View
?
答案 0 :(得分:8)
Jamgreen,
基于https://github.com/bmihelac/django-import-export的实施并考虑模型"国家"例如,名称和abreviation属性:
首先,在国家模型的末尾定义资源:
class CountryResource(resources.ModelResource):
class Meta:
model = Country
然后,实现基于类的视图:
class CountryExport(View):
def get(self, *args, **kwargs ):
dataset = CountryResource().export()
response = HttpResponse(dataset.csv, content_type="csv")
response['Content-Disposition'] = 'attachment; filename=filename.csv'
return response
class CountryImport(View):
model = Country
from_encoding = "utf-8"
#: import / export formats
DEFAULT_FORMATS = (
base_formats.CSV,
base_formats.XLS,
base_formats.TSV,
base_formats.ODS,
base_formats.JSON,
base_formats.YAML,
base_formats.HTML,
)
formats = DEFAULT_FORMATS
#: template for import view
import_template_name = 'Country/import.html'
resource_class = None
def get_import_formats(self):
"""
Returns available import formats.
"""
return [f for f in self.formats if f().can_import()]
def get_resource_class(self):
if not self.resource_class:
return modelresource_factory(self.model)
else:
return self.resource_class
def get_import_resource_class(self):
"""
Returns ResourceClass to use for import.
"""
return self.get_resource_class()
def get(self, *args, **kwargs ):
'''
Perform a dry_run of the import to make sure the import will not
result in errors. If there where no error, save the user
uploaded file to a local temp file that will be used by
'process_import' for the actual import.
'''
resource = self.get_import_resource_class()()
context = {}
import_formats = self.get_import_formats()
form = ImportForm(import_formats,
self.request.POST or None,
self.request.FILES or None)
if self.request.POST and form.is_valid():
input_format = import_formats[
int(form.cleaned_data['input_format'])
]()
import_file = form.cleaned_data['import_file']
# first always write the uploaded file to disk as it may be a
# memory file or else based on settings upload handlers
with tempfile.NamedTemporaryFile(delete=False) as uploaded_file:
for chunk in import_file.chunks():
uploaded_file.write(chunk)
# then read the file, using the proper format-specific mode
with open(uploaded_file.name,
input_format.get_read_mode()) as uploaded_import_file:
# warning, big files may exceed memory
data = uploaded_import_file.read()
if not input_format.is_binary() and self.from_encoding:
data = force_text(data, self.from_encoding)
dataset = input_format.create_dataset(data)
result = resource.import_data(dataset, dry_run=True,
raise_errors=False)
context['result'] = result
if not result.has_errors():
context['confirm_form'] = ConfirmImportForm(initial={
'import_file_name': os.path.basename(uploaded_file.name),
'input_format': form.cleaned_data['input_format'],
})
context['form'] = form
context['opts'] = self.model._meta
context['fields'] = [f.column_name for f in resource.get_fields()]
return TemplateResponse(self.request, [self.import_template_name], context)
def post(self, *args, **kwargs ):
'''
Perform a dry_run of the import to make sure the import will not
result in errors. If there where no error, save the user
uploaded file to a local temp file that will be used by
'process_import' for the actual import.
'''
resource = self.get_import_resource_class()()
context = {}
import_formats = self.get_import_formats()
form = ImportForm(import_formats,
self.request.POST or None,
self.request.FILES or None)
if self.request.POST and form.is_valid():
input_format = import_formats[
int(form.cleaned_data['input_format'])
]()
import_file = form.cleaned_data['import_file']
# first always write the uploaded file to disk as it may be a
# memory file or else based on settings upload handlers
with tempfile.NamedTemporaryFile(delete=False) as uploaded_file:
for chunk in import_file.chunks():
uploaded_file.write(chunk)
# then read the file, using the proper format-specific mode
with open(uploaded_file.name,
input_format.get_read_mode()) as uploaded_import_file:
# warning, big files may exceed memory
data = uploaded_import_file.read()
if not input_format.is_binary() and self.from_encoding:
data = force_text(data, self.from_encoding)
dataset = input_format.create_dataset(data)
result = resource.import_data(dataset, dry_run=True,
raise_errors=False)
context['result'] = result
if not result.has_errors():
context['confirm_form'] = ConfirmImportForm(initial={
'import_file_name': os.path.basename(uploaded_file.name),
'input_format': form.cleaned_data['input_format'],
})
context['form'] = form
context['opts'] = self.model._meta
context['fields'] = [f.column_name for f in resource.get_fields()]
return TemplateResponse(self.request, [self.import_template_name], context)
class CountryProcessImport(View):
model = Country
from_encoding = "utf-8"
#: import / export formats
DEFAULT_FORMATS = (
base_formats.CSV,
base_formats.XLS,
base_formats.TSV,
base_formats.ODS,
base_formats.JSON,
base_formats.YAML,
base_formats.HTML,
)
formats = DEFAULT_FORMATS
#: template for import view
import_template_name = 'Country/import.html'
resource_class = None
def get_import_formats(self):
"""
Returns available import formats.
"""
return [f for f in self.formats if f().can_import()]
def get_resource_class(self):
if not self.resource_class:
return modelresource_factory(self.model)
else:
return self.resource_class
def get_import_resource_class(self):
"""
Returns ResourceClass to use for import.
"""
return self.get_resource_class()
def post(self, *args, **kwargs ):
'''
Perform the actual import action (after the user has confirmed he
wishes to import)
'''
opts = self.model._meta
resource = self.get_import_resource_class()()
confirm_form = ConfirmImportForm(self.request.POST)
if confirm_form.is_valid():
import_formats = self.get_import_formats()
input_format = import_formats[
int(confirm_form.cleaned_data['input_format'])
]()
import_file_name = os.path.join(
tempfile.gettempdir(),
confirm_form.cleaned_data['import_file_name']
)
import_file = open(import_file_name, input_format.get_read_mode())
data = import_file.read()
if not input_format.is_binary() and self.from_encoding:
data = force_text(data, self.from_encoding)
dataset = input_format.create_dataset(data)
result = resource.import_data(dataset, dry_run=False,
raise_errors=True)
# Add imported objects to LogEntry
ADDITION = 1
CHANGE = 2
DELETION = 3
logentry_map = {
RowResult.IMPORT_TYPE_NEW: ADDITION,
RowResult.IMPORT_TYPE_UPDATE: CHANGE,
RowResult.IMPORT_TYPE_DELETE: DELETION,
}
content_type_id=ContentType.objects.get_for_model(self.model).pk
'''
for row in result:
LogEntry.objects.log_action(
user_id=request.user.pk,
content_type_id=content_type_id,
object_id=row.object_id,
object_repr=row.object_repr,
action_flag=logentry_map[row.import_type],
change_message="%s through import_export" % row.import_type,
)
'''
success_message = _('Import finished')
messages.success(self.request, success_message)
import_file.close()
url = reverse('%s_list' % (str(opts.app_label).lower()))
return HttpResponseRedirect(url)
模板import.html具有以下代码:
<h1>{% trans "Importar" %} {{ opts.app_label }}</h1>
{% if confirm_form %}
<form action="{% url "process_import" %}" method="POST">
{% csrf_token %}
{{ confirm_form.as_p }}
<p>
{% trans "Below is a preview of data to be imported. If you are satisfied with the results, click 'Confirm import'" %}
</p>
<div class="submit-row">
<input type="submit" class="btn" name="confirm" value="{% trans "Confirm import" %}">
</div>
</form>
{% else %}
<form action="{{ form_url }}" method="post" id="{{ opts.module_name }}_form" enctype="multipart/form-data">
{% csrf_token %}
<p>
{% trans "This importer will import the following fields: " %}
{% for f in fields %}
{% if forloop.counter0 %}
,
{% endif %}
<tt>{{ f }}</tt>
{% endfor %}
</p>
<fieldset class="module aligned">
{% for field in form %}
<div class="form-row">
{{ field.errors }}
{{ field.label_tag }}
{{ field }}
{% if field.field.help_text %}
<p class="help">{{ field.field.help_text|safe }}</p>
{% endif %}
</div>
{% endfor %}
</fieldset>
<div class="submit-row">
<input type="submit" class="btn" value="{% trans "Submit" %}">
</div>
</form>
{% endif %}
{% if result %}
{% if result.has_errors %}
<h2>{% trans "Errors" %}</h2>
<ul>
{% for error in result.base_errors %}
<li>{{ error.error }}</li>
{% endfor %}
{% for line, errors in result.row_errors %}
{% for error in errors %}
<li>
{% trans "Line number" %}: {{ line }} - {{ error.error }}
<div class="traceback">{{ error.traceback|linebreaks }}</div>
</li>
{% endfor %}
{% endfor %}
</ul>
{% else %}
<h2>
{% trans "Preview" %}
</h2>
<table>
<thead>
<tr>
<th></th>
{% for field in fields %}
<th>{{ field }}</th>
{% endfor %}
</tr>
</thead>
{% for row in result.rows %}
<tr>
<td>
{% if row.import_type == 'new' %}
{% trans "New" %}
{% elif row.import_type == 'skip' %}
{% trans "Skipped" %}
{% elif row.import_type == 'delete' %}
{% trans "Delete" %}
{% elif row.import_type == 'update' %}
{% trans "Update" %}
{% endif %}
</td>
{% for field in row.diff %}
<td>
{{ field }}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
{% endif %}
{% endif %}
并且urls.py应包含:
#export
url(r'export/$', login_required(CountryExport.as_view()), name='country_export'),
#import
url(r'import/$', login_required(CountryImport.as_view()), name='country_import'),
url(r'process_import/$', login_required(CountryProcessImport.as_view()), name='process_import'),
答案 1 :(得分:1)
我今天做的完全一样。我试图使视图相当通用,并允许在URL中传递值,因此它比简单地返回XLS文件更复杂。
但无论如何,这是我的方法;
class PlaceResource(resources.ModelResource):
class Meta:
model = Place
class ExportPlacesView(View):
model = Place
def get(self, request, *args, **kwargs):
client_name = str(self.client).replace(' ', '_').lower()
if 'query' in kwargs:
if kwargs['query'] == u'complete':
complete_runners = Place.objects.filter(
~models.Q(runner__completed_on=None),
console=self.console).values_list(
'client',
flat=True).distinct()
dataset = PlaceResource().export(
Place.objects.filter(
console=self.console, client=self.client,
runner__in=complete_runners
)
)
filename = '{}_completed_runners'.format(client_name)
else:
dataset = PlaceResource().export(Place.objects.filter(
console=self.console, client=self.client,
))
filename = '{}_Runners'.format(client_name)
export_type = kwargs['format']
_dataset_methods = {
'csv': dataset.csv,
'xls': dataset.xls
}
response = HttpResponse(
_dataset_methods[export_type], content_type=export_type
)
response[
'Content-Disposition'] = 'attachment; filename={filename}.{ext}'.format(
filename=filename,
ext=export_type
)
return response
答案 2 :(得分:0)
我有问题&#39;列表&#39;对象没有属性&#39; get&#39;这是我的模特:
class Mascota(models.Model):
nombre = models.CharField(max_length=50)
sexo = models.CharField(max_length=10)
edad_aproximada = models.IntegerField(blank=True)
fecha_rescate = models.DateField()
persona = models.ForeignKey(Persona, null=True, blank=True, on_delete=models.CASCADE)
vacuna = models.ManyToManyField(Vacuna, blank=True)
def __str__(self):
return '{}'.format(self.nombre)
class MascotaResource(resources.ModelResource):
class Meta:
model = Mascota
我的forms.py是:
class MascotaForm(forms.ModelForm):
class Meta:
model = Mascota
fields = [
'nombre',
'sexo',
'edad_aproximada',
'fecha_rescate',
'persona',
'vacuna',
]
labels = {
'nombre': 'Nombre',
'sexo': 'Sexo',
'edad_aproximada': 'Edad aproximada',
'fecha_rescate':'Fecha de rescate',
'persona': 'Adoptante',
'vacuna': 'Vacunas',
}
widgets = {
'nombre': forms.TextInput(attrs={'class':'form-control'}),
'sexo': forms.TextInput(attrs={'class':'form-control'}),
'edad_aproximada': forms.TextInput(attrs={'class':'form-control'}),
'fecha_rescate': forms.TextInput(attrs={'class':'form-control'}),
'persona': forms.Select(attrs={'class':'form-control'}),
'vacuna': forms.CheckboxSelectMultiple(),
}
问题是: