将 django-import-export 与基于类的视图一起使用 [英] Use django-import-export with class based views

查看:35
本文介绍了将 django-import-export 与基于类的视图一起使用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想将 django-import-export 与基于类的视图一起使用.

https://django-import-export 的文档中.readthedocs.org/en/latest/getting_started.html 我看到了一个导出为 csv 的例子

<预><代码>>>>数据集 = BookResource().export()>>>打印数据集.csvID、名称、作者、author_email、导入、已发布、价格、类别2,某本书,1,,0,2012-12-05,8.85,1

但是如果我想返回一个 Excel 文件,我应该使用哪个基于类的视图?只是查看?

解决方案

Jamgreen,

基于 https://github.com/bmihelac/django-import-export 的实施并考虑一个模型国家"作为示例,名称和缩写属性:

首先,在 Country 模型文件的末尾定义资源:

class CountryResource(resources.ModelResource):元类:型号 = 国家

然后,实现基于类的视图:

class CountryExport(View):def get(self, *args, **kwargs):数据集 = CountryResource().export()响应 = HttpResponse(dataset.csv, content_type="csv")response['Content-Disposition'] = '附件;文件名=文件名.csv'返回响应类 CountryImport(查看):型号 = 国家from_encoding = "utf-8"#:导入/导出格式DEFAULT_FORMATS = (base_formats.CSV,base_formats.XLS,base_formats.TSV,base_formats.ODS,base_formats.JSON,base_formats.YAML,base_formats.HTML,)格式 = DEFAULT_FORMATS#: 导入视图模板import_template_name = '国家/地区/import.html'资源类 = 无def get_import_formats(self):"""返回可用的导入格式."""return [f for f in self.formats if f().can_import()]def get_resource_class(self):如果不是 self.resource_class:返回模型资源工厂(self.model)别的:返回 self.resource_classdef get_import_resource_class(self):"""返回用于导入的 ResourceClass."""返回 self.get_resource_class()def get(self, *args, **kwargs):'''执行导入的 dry_run 以确保导入不会导致错误.如果没有错误,保存用户上传文件到本地临时文件,该文件将被使用'process_import' 用于实际导入.'''资源 = self.get_import_resource_class()()上下文 = {}import_formats = self.get_import_formats()form = ImportForm(import_formats,self.request.POST 或 None,self.request.FILES 或 None)如果 self.request.POST 和 form.is_valid():输入格式 = 导入格式[int(form.cleaned_data['input_format'])]()import_file = form.cleaned_data['import_file']# 首先总是将上传的文件写入磁盘,因为它可能是# 内存文件或其他基于设置上传处理程序使用 tempfile.NamedTemporaryFile(delete=False) 作为上传文件:对于 import_file.chunks() 中的块:upload_file.write(块)# 然后使用正确的特定格式模式读取文件随着打开(uploaded_file.name,input_format.get_read_mode()) 作为uploaded_import_file:# 警告,大文件可能超出内存数据 = Uploaded_import_file.read()如果不是 input_format.is_binary() 和 self.from_encoding:数据 = force_text(数据,self.from_encoding)数据集 = input_format.create_dataset(data)结果 = resource.import_data(数据集,dry_run=True,raise_errors=False)上下文['结果'] = 结果如果不是 result.has_errors():上下文['confirm_form'] = ConfirmImportForm(initial={'import_file_name': os.path.basename(uploaded_file.name),'input_format': form.cleaned_data['input_format'],})上下文['形式'] = 形式context['opts'] = self.model._metacontext['fields'] = [f.column_name for f in resource.get_fields()]返回模板响应(self.request,[self.import_template_name],上下文)def post(self, *args, **kwargs):'''执行导入的 dry_run 以确保导入不会导致错误.如果没有错误,保存用户上传文件到本地临时文件,该文件将被使用'process_import' 用于实际导入.'''资源 = self.get_import_resource_class()()上下文 = {}import_formats = self.get_import_formats()form = ImportForm(import_formats,self.request.POST 或 None,self.request.FILES 或 None)如果 self.request.POST 和 form.is_valid():输入格式 = 导入格式[int(form.cleaned_data['input_format'])]()import_file = form.cleaned_data['import_file']# 首先总是将上传的文件写入磁盘,因为它可能是# 内存文件或其他基于设置上传处理程序使用 tempfile.NamedTemporaryFile(delete=False) 作为上传文件:对于 import_file.chunks() 中的块:upload_file.write(块)# 然后使用正确的特定格式模式读取文件随着打开(uploaded_file.name,input_format.get_read_mode()) 作为uploaded_import_file:# 警告,大文件可能超出内存数据 = Uploaded_import_file.read()如果不是 input_format.is_binary() 和 self.from_encoding:数据 = force_text(数据,self.from_encoding)数据集 = input_format.create_dataset(data)结果 = resource.import_data(数据集,dry_run=True,raise_errors=False)上下文['结果'] = 结果如果不是 result.has_errors():上下文['confirm_form'] = ConfirmImportForm(initial={'import_file_name': os.path.basename(uploaded_file.name),'input_format': form.cleaned_data['input_format'],})上下文['形式'] = 形式context['opts'] = self.model._metacontext['fields'] = [f.column_name for f in resource.get_fields()]返回模板响应(self.request,[self.import_template_name],上下文)类 CountryProcessImport(View):型号 = 国家from_encoding = "utf-8"#:导入/导出格式DEFAULT_FORMATS = (base_formats.CSV,base_formats.XLS,base_formats.TSV,base_formats.ODS,base_formats.JSON,base_formats.YAML,base_formats.HTML,)格式 = DEFAULT_FORMATS#: 导入视图模板import_template_name = '国家/地区/import.html'资源类 = 无def get_import_formats(self):"""返回可用的导入格式."""return [f for f in self.formats if f().can_import()]def get_resource_class(self):如果不是 self.resource_class:返回模型资源工厂(self.model)别的:返回 self.resource_classdef get_import_resource_class(self):"""返回用于导入的 ResourceClass."""返回 self.get_resource_class()def post(self, *args, **kwargs):'''执行实际的导入操作(在用户确认他希望进口)'''opts = self.model._meta资源 = self.get_import_resource_class()()Confirm_form = ConfirmImportForm(self.request.POST)如果confirm_form.is_valid():import_formats = self.get_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())数据 = import_file.read()如果不是 input_format.is_binary() 和 self.from_encoding:数据 = force_text(数据,self.from_encoding)数据集 = input_format.create_dataset(data)结果 = resource.import_data(数据集,dry_run=False,raise_errors=True)# 将导入的对象添加到 LogEntry加法 = 1改变 = 2删除 = 3logentry_map = {RowResult.IMPORT_TYPE_NEW:添加,RowResult.IMPORT_TYPE_UPDATE:改变,RowResult.IMPORT_TYPE_DELETE:删除,}content_type_id=ContentType.objects.get_for_model(self.model).pk'''对于结果行: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 到 import_export" % row.import_type,)'''success_message = _('导入完成')消息.success(self.request,success_message)import_file.close()url = reverse('%s_list' % (str(opts.app_label).lower()))返回 HttpResponseRedirect(url)

模板 import.html 有以下代码:

{% trans "Importar" %} {{ opts.app_label }}

{% if confirm_form %}<form action="{% url "process_import" %}" method="POST">{% csrf_token %}{{confirm_form.as_p }}<p>{% trans "下面是要导入的数据预览.如果对结果满意,请点击'确认导入'" %}</p><div class="submit-row"><input type="submit" class="btn" name="confirm" value="{% trans "Confirm import" %}">

</表单>{% 别的 %}<form action="{{ form_url }}" method="post" id="{{ opts.module_name }}_form" enctype="multipart/form-data">{% csrf_token %}<p>{% trans "此导入程序将导入以下字段:" %}{% for f 在字段 %}{% if forloop.counter0 %},{% 万一 %}<tt>{{ f }}</tt>{% 结束为 %}</p><fieldset class="模块对齐">{% 用于表单 %} 中的字段<div class="form-row">{{ field.errors }}{{ field.label_tag }}{{ 场地 }}{% if field.field.help_text %}<p class="help">{{ field.field.help_text|safe }}</p>{% 万一 %}

{% 结束为 %}</fieldset><div class="submit-row"><input type="submit" class="btn" value="{% trans "Submit" %}">

</表单>{% 万一 %}{% 如果结果 %}{% 如果 result.has_errors %}<h2>{% trans "Errors" %}</h2><ul>{% for error in result.base_errors %}<li>{{ error.error }}</li>{% 结束为 %}{% 为行,result.row_errors 中的错误%}{% for error in error %}<li>{% trans "行号" %}: {{ line }} - {{ error.error }}<div class="traceback">{{ error.traceback|linebreaks }}</div>{% 结束为 %}{% 结束为 %}{% 别的 %}<h2>{% trans "预览" %}<表格><头><tr><th></th>{% 用于字段中的字段 %}<th>{{字段}}</th>{% 结束为 %}</tr></thead>{% for row in result.rows %}<tr><td>{% if row.import_type == 'new' %}{% trans "New" %}{% elif row.import_type == '跳过' %}{% trans "Skipped" %}{% elif row.import_type == '删除' %}{% trans "删除" %}{% elif row.import_type == '更新' %}{% trans "更新" %}{% 万一 %}</td>{% 用于 row.diff 中的字段 %}<td>{{ 场地 }}</td>{% 结束为 %}</tr>{% 结束为 %}{% 万一 %}{% 万一 %}

并且 urls.py 应该包含:

#exporturl(r'export/$', login_required(CountryExport.as_view()), name='country_export'),#进口url(r'import/$', login_required(CountryImport.as_view()), name='country_import'),url(r'process_import/$', login_required(CountryProcessImport.as_view()), name='process_import'),

I want to use django-import-export with class based views.

In the docs at https://django-import-export.readthedocs.org/en/latest/getting_started.html I see an example of exporting as 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

but which class based view should I use if I want to return an Excel file? Just View?

解决方案

Jamgreen,

Based on implementation at https://github.com/bmihelac/django-import-export and considering a model "Country" as example with name and abreviation atributes:

First, define at the end of Country models file the Resource:

class CountryResource(resources.ModelResource):
    class Meta:
        model = Country

Then, implement the class based views:

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)

the template import.html has the following code:

<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 %}

and the urls.py should contain:

#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'),

这篇关于将 django-import-export 与基于类的视图一起使用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
Python最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆