Python Django 零基础破门而入篇(二)

本文介绍如何使用django-import-export 实现以下功能点

官方实例:https://django-import-export.readthedocs.io/en/latest/getting_started.html#creating-import-export-resource

1.导入 导出

2.批量更新

3.如何导出model中 ForeignKey 字段的其它的属性值

 项目github地址:https://github.com/wangcongxing/OpenTheDoor

如何新建django项目请参照

Python Django 零基础破门而入篇(一)

 

1.安装 django-import-export

pip install django-import-export

 

2.配置项目文件下 settings.py

修改 INSTALLED_APPS

默认值为False。它确定库是否会在数据导入中使用数据库事务,以确保安全

新增 IMPORT_EXPORT_USE_TRANSACTIONS = True

 

 

3.修改app-->admin.py 设置导出代码列头 代码如下

from django.contrib import admin
from app import models
from import_export import resources
from import_export.admin import ImportExportModelAdmin
from django.apps import apps

# Register your models here.
admin.site.site_header = "大佬管理"
admin.site.site_title = "大佬管理后台管理"


class bossInfoResource(resources.ModelResource):
    def __init__(self):
        super(bossInfoResource, self).__init__()

        field_list = models.bossInfo._meta.fields
        self.vname_dict = {}
        for i in field_list:
            self.vname_dict[i.name] = i.verbose_name

    # 默认导入导出field的column_name为字段的名称,这里修改为字段的verbose_name
    def get_export_fields(self):
        fields = self.get_fields()
        for field in fields:
            field_name = self.get_field_name(field)
            # 如果我们设置过verbose_name,则将column_name替换为verbose_name。否则维持原有的字段名
            if field_name in self.vname_dict.keys():
                field.column_name = self.vname_dict[field_name]
        return fields

    def after_import(self, dataset, result, using_transactions, dry_run, **kwargs):
        print("after_import")

    def after_import_instance(self, instance, new, **kwargs):
        print("after_import_instance")

    class Meta:
        model = models.bossInfo
        skip_unchanged = True
        report_skipped = True
        fields = (
        "id", "age", "gender", "likeFruit", "userUrl", "name", "desc", "createTime", "lastTime", "creator", "editor")


@admin.register(models.bossInfo)
class bossInfoAdmin(ImportExportModelAdmin):
    fields = (
        "name", "age", "gender",
        "likeFruit", "userUrl", "desc")
    list_display = (
        "name", "age", "gender",
        "likeFruit", "userUrl",
        "createTime",
        "lastTime",
        "creator", "editor")
    list_display_links = ("name",)
    exclude = ("createTime", "creator", "editor")
    search_fields = ("name",)
    list_filter = ("gender", "likeFruit")
    model_icon = "fa fa-tag"
    list_per_page = 20
    ordering = ["-id"]
    resource_class = bossInfoResource

    def save_model(self, request, obj, form, change):
        if form.is_valid():
            if change:
                obj.editor = request.user
            else:
                obj.creator = request.user
                obj.editor = request.user
                obj.save()
        super().save_model(request, obj, form, change)

 

 4.运行效果

 

5.导出excel 存在以下问题

1.创建者 / 修改者 如何显示用户名

2.创建时间 / 修改时间如何进行格式化

3.继续修改admin.py  重写导出 export 完整代码如下

from django.contrib import admin
from app import models
from import_export import resources
from import_export.admin import ImportExportModelAdmin
from django.apps import apps
from django.db.models import QuerySet
import tablib
# Register your models here.
admin.site.site_header = "大佬管理"
admin.site.site_title = "大佬管理后台管理"


class bossInfoResource(resources.ModelResource):
    def __init__(self):
        super(bossInfoResource, self).__init__()

        field_list = models.bossInfo._meta.fields
        self.vname_dict = {}
        self.fkey = []
        for i in field_list:
            self.vname_dict[i.name] = i.verbose_name
            self.fkey.append(i.name)

    # 默认导入导出field的column_name为字段的名称,这里修改为字段的verbose_name
    def get_export_fields(self):
        fields = self.get_fields()
        for field in fields:
            field_name = self.get_field_name(field)
            # 如果我们设置过verbose_name,则将column_name替换为verbose_name。否则维持原有的字段名
            if field_name in self.vname_dict.keys():
                field.column_name = self.vname_dict[field_name]
        return fields

    def after_import(self, dataset, result, using_transactions, dry_run, **kwargs):
        print("after_import")

    def after_import_instance(self, instance, new, **kwargs):
        print("after_import_instance")

    # 重载resources.py的export方法,修改将要导出的data的某些外键相关数据。默认导出外键id,这里修改为导出外键对应的值
    def export(self, queryset=None, *args, **kwargs):
        self.before_export(queryset, *args, **kwargs)
        if queryset is None:
            queryset = self.get_queryset()
        headers = self.get_export_headers()
        data = tablib.Dataset(headers=headers)
        # 获取所有外键名称在headers中的位置
        fk_index = {}
        for fk in self.fkey:
            fk_index[fk] = headers.index(self.vname_dict[fk])
        # --------------------- #

        if isinstance(queryset, QuerySet):
            # Iterate without the queryset cache, to avoid wasting memory when
            # exporting large datasets.
            iterable = queryset.iterator()
        else:
            iterable = queryset
        for obj in iterable:
            # --------------------- #
            # 获取将要导出的源数据,这里export_resource返回的是列表,便于更改。替换到外键的值
            # 可以对所有字段值进行二次处理
            res = self.export_resource(obj)
            res[fk_index['id']] = obj.id
            res[fk_index['age']] = obj.age
            res[fk_index['gender']] = obj.gender
            res[fk_index['likeFruit']] = obj.likeFruit
            res[fk_index['userUrl']] = obj.userUrl
            res[fk_index['name']] = obj.name
            res[fk_index['desc']] = obj.desc
            res[fk_index['createTime']] = obj.createTime
            res[fk_index['lastTime']] = obj.lastTime
            res[fk_index['creator']] = obj.creator.username
            res[fk_index['editor']] = obj.editor.username
            data.append(res)
            # --------------------- #
        self.after_export(queryset, data, *args, **kwargs)
        return data

    class Meta:
        model = models.bossInfo
        skip_unchanged = True
        report_skipped = True
        fields = (
            "id", "age", "gender", "likeFruit", "userUrl", "name", "desc", "createTime", "lastTime", "creator",
            "editor")


@admin.register(models.bossInfo)
class bossInfoAdmin(ImportExportModelAdmin):
    fields = (
        "name", "age", "gender",
        "likeFruit", "userUrl", "desc")
    list_display = (
        "name", "age", "gender",
        "likeFruit", "userUrl",
        "createTime",
        "lastTime",
        "creator", "editor")
    list_display_links = ("name",)
    exclude = ("createTime", "creator", "editor")
    search_fields = ("name",)
    list_filter = ("gender", "likeFruit")
    model_icon = "fa fa-tag"
    list_per_page = 20
    ordering = ["-id"]
    resource_class = bossInfoResource

    def save_model(self, request, obj, form, change):
        if form.is_valid():
            if change:
                obj.editor = request.user
            else:
                obj.creator = request.user
                obj.editor = request.user
                obj.save()
        super().save_model(request, obj, form, change)

 

 

 最终导出效果

 

6.导入配置

点击导入

提示都是字段名,用户肯定整不明白的

赶紧改成中文的   解决方法  

通过重写导入页面

 

7.在应用目录(app)下-->templatetags-->apptags.py

8.修改项目settings.py

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')]
        ,
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
            'libraries': {
                'apptags': 'app.templatetags.apptags'
            },
        },
    },
]

9.重写django-import-export 导入页面

在项目templates下新建 admin-->import_export--> import.html 

import.html代码如下  将{{ fields|join:", " }}  替换成 apptags.py 定义的 import_head_tag 方法即可

找到项目中import_export 文件夹中 

 

10.在项目中的templates-->admin-->import_export 下把导入导出两个页面都复制过来(export.html   import.html)

11.修改import.html  标注背景色的两处

{% extends "admin/import_export/base.html" %}
{% load static simpletags apptags %}
{% load i18n %}
{% load admin_urls %}
{% load import_export_tags %}
{% load static %}

{% block extrastyle %}{{ block.super }}
    <link rel="stylesheet" type="text/css" href="{% static "import_export/import.css" %}"/>{% endblock %}

{% block breadcrumbs_last %}
    {% trans "Import" %}
{% endblock %}

{% block content %}

    {% if confirm_form %}
        <form action="{% url opts|admin_urlname:"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="default" name="confirm" value="{% trans "Confirm import" %}">
            </div>
        </form>
    {% else %}
        <form action="" method="post" enctype="multipart/form-data">
            {% csrf_token %}

            <p>
                {% trans "This importer will import the following fields: " %}
                {# <code>{{ fields|join:", " }}</code> #}
                <code>{% import_head_tag %}</code>
            </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="default" 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 }}
                        <div class="traceback">{{ error.traceback|linebreaks }}</div>
                    </li>
                {% endfor %}
                {% for line, errors in result.row_errors %}
                    {% for error in errors %}
                        <li>
                            {% trans "Line number" %}: {{ line }} - {{ error.error }}
                            <div><code>{{ error.row.values|join:", " }}</code></div>
                            <div class="traceback">{{ error.traceback|linebreaks }}</div>
                        </li>
                    {% endfor %}
                {% endfor %}
            </ul>

        {% elif result.has_validation_errors %}

            <h2>{% trans "Some rows failed to validate" %}</h2>

            <p>{% trans "Please correct these errors in your data where possible, then reupload it using the form above." %}</p>

            <table class="import-preview">
                <thead>
                <tr>
                    <th>{% trans "Row" %}</th>
                    <th>{% trans "Errors" %}</th>
                    {% for field in result.diff_headers %}
                        <th>{{ field }}</th>
                    {% endfor %}
                </tr>
                </thead>
                <tbody>
                {% for row in result.invalid_rows %}
                    <tr>
                        <td>{{ row.number }} </td>
                        <td class="errors">
                            <span class="validation-error-count">{{ row.error_count }}</span>
                            <div class="validation-error-container">
                                <ul class="validation-error-list">
                                    {% for field_name, error_list in row.field_specific_errors.items %}
                                        <li>
                                            <span class="validation-error-field-label">{{ field_name }}</span>
                                            <ul>
                                                {% for error in error_list %}
                                                    <li>{{ error }}</li>
                                                {% endfor %}
                                            </ul>
                                        </li>
                                    {% endfor %}
                                    {% if row.non_field_specific_errors %}
                                        <li>
                                            <span class="validation-error-field-label">{% trans "Non field specific" %}</span>
                                            <ul>
                                                {% for error in row.non_field_specific_errors %}
                                                    <li>{{ error }}</li>
                                                {% endfor %}
                                            </ul>
                                        </li>
                                    {% endif %}
                                </ul>
                            </div>
                        </td>
                        {% for field in row.values %}
                            <td>{{ field }}</td>
                        {% endfor %}
                    </tr>
                {% endfor %}
                </tbody>
            </table>

        {% else %}

            <h2>{% trans "Preview" %}</h2>

            <table class="import-preview">
                <thead>
                <tr>
                    <th></th>
                    {% for field in result.diff_headers %}
                        <th>{{ field }}</th>
                    {% endfor %}
                </tr>
                </thead>
                {% for row in result.valid_rows %}
                    <tr class="{{ row.import_type }}">
                        <td class="import-type">
                            {% 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 %}
{% endblock %}

12.运行效果

导出包含创建时间,修改时间,创建者,修改者

导入 创建者修改者自动复制  选项值需要特殊处理例如多选

 

13.自动将当前登录用户 赋值每条导入对象(instance)的 创建者/修改者字段  

 

导入效果

 

 

14.最终admin.py代码

import traceback
from copy import deepcopy

from django.contrib import admin
from django.core.exceptions import ValidationError
from django.db.transaction import TransactionManagementError
from django.utils.encoding import force_text
from import_export.results import RowResult

from app import models
from import_export import resources
from import_export.admin import ImportExportModelAdmin
from django.apps import apps
from django.db.models import QuerySet
import tablib

# Register your models here.
admin.site.site_header = "大佬管理"
admin.site.site_title = "大佬管理后台管理"


class bossInfoResource(resources.ModelResource):

    def __init__(self):
        super(bossInfoResource, self).__init__()

        field_list = models.bossInfo._meta.fields
        self.vname_dict = {}
        self.fkey = []
        customeFields = (
            "id", "age", "gender", "likeFruit", "userUrl", "name", "desc",)
        for i in field_list:
            if i.name in customeFields:
                self.vname_dict[i.name] = i.verbose_name.lower()
                self.fkey.append(i.name)

    # 默认导入导出field的column_name为字段的名称,这里修改为字段的verbose_name
    def get_export_fields(self):
        fields = self.get_fields()
        for field in fields:
            field_name = self.get_field_name(field)
            # 如果我们设置过verbose_name,则将column_name替换为verbose_name。否则维持原有的字段名
            if field_name in self.vname_dict.keys():
                field.column_name = self.vname_dict[field_name]
        return fields

    def after_import(self, dataset, result, using_transactions, dry_run, **kwargs):
        print("after_import")

    def after_import_instance(self, instance, new, **kwargs):
        print("after_import_instance")

    # 重载resources.py的export方法,修改将要导出的data的某些外键相关数据。默认导出外键id,这里修改为导出外键对应的值
    def export(self, queryset=None, *args, **kwargs):
        self.before_export(queryset, *args, **kwargs)
        if queryset is None:
            queryset = self.get_queryset()
        headers = self.get_export_headers()
        headers.append("创建时间")
        headers.append("修改时间")
        headers.append("创建者")
        headers.append("修改者")
        data = tablib.Dataset(headers=headers)
        # 获取所有外键名称在headers中的位置
        fk_index = {}
        for fk in self.fkey:
            fk_index[fk] = headers.index(self.vname_dict[fk])
        # --------------------- #

        if isinstance(queryset, QuerySet):
            # Iterate without the queryset cache, to avoid wasting memory when
            # exporting large datasets.
            iterable = queryset.iterator()
        else:
            iterable = queryset
        for obj in iterable:
            # --------------------- #
            # 获取将要导出的源数据,这里export_resource返回的是列表,便于更改。替换到外键的值
            # 可以对所有字段值进行二次处理
            res = self.export_resource(obj)
            res[fk_index['id']] = obj.id
            res[fk_index['age']] = obj.age
            res[fk_index['gender']] = obj.gender
            res[fk_index['likeFruit']] = obj.likeFruit
            res[fk_index['userUrl']] = obj.userUrl
            res[fk_index['name']] = obj.name
            res[fk_index['desc']] = obj.desc
            res.append(obj.createTime)
            res.append(obj.lastTime)
            res.append(obj.creator.username)
            res.append(obj.editor.username)
            data.append(res)
            # --------------------- #
        self.after_export(queryset, data, *args, **kwargs)
        return data

    # 导入前
    def before_import_row(self, row, **kwargs):
        print(row)

    # 导入后(执行更新创建者修改)
    def after_import_row(self, row, row_result, **kwargs):
        print(row)

    # 导入数据 自动将当前登录用户 赋值给创建者和修改者
    def import_row(self, row, instance_loader, using_transactions=True, dry_run=False, **kwargs):
        print(instance_loader)
        row_result = self.get_row_result_class()()
        try:
            self.before_import_row(row, **kwargs)
            instance, new = self.get_or_init_instance(instance_loader, row)
            self.after_import_instance(instance, new, **kwargs)
            if new:
                row_result.import_type = RowResult.IMPORT_TYPE_NEW
            else:
                row_result.import_type = RowResult.IMPORT_TYPE_UPDATE
            row_result.new_record = new
            original = deepcopy(instance)
            diff = self.get_diff_class()(self, original, new)
            if self.for_delete(row, instance):
                if new:
                    row_result.import_type = RowResult.IMPORT_TYPE_SKIP
                    diff.compare_with(self, None, dry_run)
                else:
                    row_result.import_type = RowResult.IMPORT_TYPE_DELETE
                    self.delete_instance(instance, using_transactions, dry_run)
                    diff.compare_with(self, None, dry_run)
            else:
                import_validation_errors = {}
                try:
                    self.import_obj(instance, row, dry_run)
                except ValidationError as e:
                    # Validation errors from import_obj() are passed on to
                    # validate_instance(), where they can be combined with model
                    # instance validation errors if necessary
                    import_validation_errors = e.update_error_dict(import_validation_errors)
                if self.skip_row(instance, original):
                    row_result.import_type = RowResult.IMPORT_TYPE_SKIP
                else:
                    self.validate_instance(instance, import_validation_errors)
                    print(instance)
                    print("dry_run", dry_run)
                    if instance.creator is None:
                        instance.creator = kwargs["user"]
                    instance.editor = kwargs["user"]
                    self.save_instance(instance, using_transactions, dry_run)
                    self.save_m2m(instance, row, using_transactions, dry_run)
                    # Add object info to RowResult for LogEntry
                    row_result.object_id = instance.pk
                    row_result.object_repr = force_text(instance)
                diff.compare_with(self, instance, dry_run)

            row_result.diff = diff.as_html()
            self.after_import_row(row, row_result, **kwargs)

        except ValidationError as e:
            row_result.import_type = RowResult.IMPORT_TYPE_INVALID
            row_result.validation_error = e
        except Exception as e:
            row_result.import_type = RowResult.IMPORT_TYPE_ERROR
            # There is no point logging a transaction error for each row
            # when only the original error is likely to be relevant
            if not isinstance(e, TransactionManagementError):
                print(e)
            tb_info = traceback.format_exc()
            row_result.errors.append(self.get_error_result_class()(e, tb_info, row))
        return row_result

    class Meta:
        model = models.bossInfo
        skip_unchanged = True
        report_skipped = True
        fields = (
            "id", "age", "gender", "likeFruit", "userUrl", "name", "desc",)


@admin.register(models.bossInfo)
class bossInfoAdmin(ImportExportModelAdmin):
    fields = (
        "name", "age", "gender",
        "likeFruit", "userUrl", "desc")
    list_display = (
        "name", "age", "gender",
        "likeFruit", "userUrl",
        "createTime",
        "lastTime",
        "creator", "editor")
    list_display_links = ("name",)
    exclude = ("createTime", "creator", "editor")
    search_fields = ("name",)
    list_filter = ("gender", "likeFruit")
    model_icon = "fa fa-tag"
    list_per_page = 20
    ordering = ["-id"]
    resource_class = bossInfoResource

    def save_model(self, request, obj, form, change):
        if form.is_valid():
            if change:
                obj.editor = request.user
            else:
                obj.creator = request.user
                obj.editor = request.user
                obj.save()
        super().save_model(request, obj, form, change)

 

15.给导入导出页面 (export.html    import.html) 新增返回按钮

{% extends "admin/import_export/base.html" %}
{% load static simpletags apptags %}
{% load i18n %}
{% load admin_urls %}
{% load import_export_tags %}
{% load static %}

{% block extrastyle %}{{ block.super }}
    <link rel="stylesheet" type="text/css" href="{% static "import_export/import.css" %}"/>{% endblock %}

{% block breadcrumbs_last %}
    {% trans "Import" %}
{% endblock %}

{% block content %}

    {% if confirm_form %}
        <form action="{% url opts|admin_urlname:"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="default" name="confirm" value="{% trans "Confirm import" %}">
            </div>
        </form>
    {% else %}
        <form action="" method="post" enctype="multipart/form-data">
            {% csrf_token %}

            <p>
                {% trans "This importer will import the following fields: " %}
                {# <code>{{ fields|join:", " }}</code> #}
                <code>{% import_head_tag %}</code>
            </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="button" class="el-button el-button--default is-plain" style="float: left"
                       onclick="javascript:window.history.go(-1)" value="返回"/>
                <input type="submit" class="default" 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 }}
                        <div class="traceback">{{ error.traceback|linebreaks }}</div>
                    </li>
                {% endfor %}
                {% for line, errors in result.row_errors %}
                    {% for error in errors %}
                        <li>
                            {% trans "Line number" %}: {{ line }} - {{ error.error }}
                            <div><code>{{ error.row.values|join:", " }}</code></div>
                            <div class="traceback">{{ error.traceback|linebreaks }}</div>
                        </li>
                    {% endfor %}
                {% endfor %}
            </ul>

        {% elif result.has_validation_errors %}

            <h2>{% trans "Some rows failed to validate" %}</h2>

            <p>{% trans "Please correct these errors in your data where possible, then reupload it using the form above." %}</p>

            <table class="import-preview">
                <thead>
                <tr>
                    <th>{% trans "Row" %}</th>
                    <th>{% trans "Errors" %}</th>
                    {% for field in result.diff_headers %}
                        <th>{{ field }}</th>
                    {% endfor %}
                </tr>
                </thead>
                <tbody>
                {% for row in result.invalid_rows %}
                    <tr>
                        <td>{{ row.number }} </td>
                        <td class="errors">
                            <span class="validation-error-count">{{ row.error_count }}</span>
                            <div class="validation-error-container">
                                <ul class="validation-error-list">
                                    {% for field_name, error_list in row.field_specific_errors.items %}
                                        <li>
                                            <span class="validation-error-field-label">{{ field_name }}</span>
                                            <ul>
                                                {% for error in error_list %}
                                                    <li>{{ error }}</li>
                                                {% endfor %}
                                            </ul>
                                        </li>
                                    {% endfor %}
                                    {% if row.non_field_specific_errors %}
                                        <li>
                                            <span class="validation-error-field-label">{% trans "Non field specific" %}</span>
                                            <ul>
                                                {% for error in row.non_field_specific_errors %}
                                                    <li>{{ error }}</li>
                                                {% endfor %}
                                            </ul>
                                        </li>
                                    {% endif %}
                                </ul>
                            </div>
                        </td>
                        {% for field in row.values %}
                            <td>{{ field }}</td>
                        {% endfor %}
                    </tr>
                {% endfor %}
                </tbody>
            </table>

        {% else %}

            <h2>{% trans "Preview" %}</h2>

            <table class="import-preview">
                <thead>
                <tr>
                    <th></th>
                    {% for field in result.diff_headers %}
                        <th>{{ field }}</th>
                    {% endfor %}
                </tr>
                </thead>
                {% for row in result.valid_rows %}
                    <tr class="{{ row.import_type }}">
                        <td class="import-type">
                            {% 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 %}
{% endblock %}

 

14.项目运行安装包版本

需要留意tablib 库版本

posted @ 2020-08-24 08:45  汪丛兴  阅读(1054)  评论(0编辑  收藏  举报