django-import-export關(guān)聯(lián)外鍵在后臺管理中導(dǎo)入導(dǎo)出的辦法

本文介紹django-import-export在關(guān)聯(lián)外鍵時,導(dǎo)出可以導(dǎo)出可讀字樣(而非關(guān)聯(lián)id),導(dǎo)入通過可讀字眼進行導(dǎo)入(而非關(guān)聯(lián)id)

模型設(shè)計如下:


# models.py
from django.db import models

# Create your models here.


class User(models.Model):
    gender_by_choice = (
        ('male', '男'),
        ('female', '女'),
    )

    username = models.CharField(max_length=10, unique=True, verbose_name='姓名')
    age = models.IntegerField(verbose_name='年齡')
    gender = models.CharField(max_length=6, choices=gender_by_choice, verbose_name='性別')
    nickname = models.CharField(max_length=10, verbose_name='昵稱')

    def __str__(self):
        return self.username

    class Meta:
        verbose_name = '用戶'
        verbose_name_plural = '用戶'


class Book(models.Model):
    owner = models.ForeignKey(User, null=True, blank=True, on_delete=models.SET_NULL, verbose_name='歸屬者')
    name = models.CharField(max_length=64, verbose_name='書名')

    def __str__(self):
        return self.name

    class Meta:
        verbose_name = '書'
        verbose_name_plural = '書'

可以看到上述的Book模型中的owner關(guān)聯(lián)了User表,先按照django-import-export的官網(wǎng)所述的方法配置,看看會遇到什么問題

  • 配置admin.py

# admin.py
from import_export import resources
from import_export.admin import ImportExportModelAdmin

class BookResource(resources.ModelResource):
    class Meta:
        model = Book


class BookAdmin(ImportExportModelAdmin):
    resource_class = BookResource
    # 這里加幾個顯示字段
    list_display = ['name', 'owner']


class UserResource(resources.ModelResource):
    class Meta:
        model = User


class UserAdmin(ImportExportModelAdmin):
    resource_class = UserResource
    # 這里加幾個顯示字段
    list_display = ['username', 'age', 'gender', 'nickname']


admin.site.register(Book, BookAdmin)
admin.site.register(User, UserAdmin)

如下圖,右上角已經(jīng)有了導(dǎo)入導(dǎo)出按鈕,django-import-export已經(jīng)加載成功

錄入兩條用戶數(shù)據(jù)和三條書籍信息,如下圖:

image.png

image.png

如果將書籍導(dǎo)出,會得到如下效果,owner會顯示user表的id信息,并不是我們想要的結(jié)果:


image.png

導(dǎo)入會有同樣的問題,導(dǎo)入時owner中的值必須是user中的id,而不能是張三、李四,所以我們需要對導(dǎo)入和導(dǎo)出做一些調(diào)整,以實現(xiàn)我們的需求:


from django.contrib import admin
from .models import Book, User
from import_export import resources
from django.apps import apps
from import_export.admin import ImportExportModelAdmin
from django.db import models
import tablib
import collections

class BookResource(resources.ModelResource):
    def __init__(self):
        super(BookResource, self).__init__()
        # 獲取tables應(yīng)用下Book模型中的所有字段,請根據(jù)自己的應(yīng)用將tables更改
        field_list = apps.get_model('tables', 'Book')._meta.fields
        self.vname_dict = {}
        self.fkey = []
        for i in field_list:
            self.vname_dict[i.name] = i.verbose_name    # 獲取所有字段的verbose_name并存放在字典
            if(isinstance(i, models.ForeignKey)):
                self.fkey.append(i.name)    # 獲取所有ForeignKey字段的name存放在列表

    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(fk)

        iterable = queryset
        for obj in iterable:
            # 獲取將要導(dǎo)出的源數(shù)據(jù),這里export_resource返回的是列表,便于更改。替換到外鍵的值
            res = self.export_resource(obj)
            """
            這里是關(guān)鍵,將owner的值到User中獲取對應(yīng)的對象,并截取起可讀名稱username,
            這里用的是get,所以在User的模型中username必須是unique
            """
            res[fk_index['owner']] = User.objects.get(id=res[fk_index['owner']]).username
            data.append(res)
        self.after_export(queryset, data, *args, **kwargs)
        return data

    def before_import(self, dataset, using_transactions, dry_run, **kwargs):
        dict = []
        for row in dataset.dict:
            tmp = collections.OrderedDict()
            books = Book.objects.all()
            for item in row:
                if item == 'owner':
                    """
                    這里是關(guān)鍵,通過可讀名稱到User表中找到對應(yīng)id,并加到導(dǎo)入的數(shù)據(jù)中去
                    """
                    tmp[item] = User.objects.get(username=row[item]).id
                else:
                    tmp[item] = row[item]
            """
            這里是關(guān)鍵,將數(shù)據(jù)進行比對,如果數(shù)據(jù)相同,就把原先在Book表中的id加到需要導(dǎo)入的數(shù)據(jù)中去,
            這樣就不會新增和原先一模一樣的數(shù)據(jù),類似于create_or_update方法
            """
            for book in books:
                if row['name'] == book.name:
                    tmp['id'] = book.id
            dict.append(tmp)
        dataset.dict = dict
        return dataset

    class Meta:
        model = Book


class BookAdmin(ImportExportModelAdmin):
    resource_class = BookResource


class BookAdmin(ImportExportModelAdmin):
    resource_class = BookResource
    list_display = ['name', 'owner']


class UserResource(resources.ModelResource):
    class Meta:
        model = User


class UserAdmin(ImportExportModelAdmin):
    resource_class = UserResource
    list_display = ['username', 'age', 'gender', 'nickname']


admin.site.register(Book, BookAdmin)
admin.site.register(User, UserAdmin)

請?zhí)貏e注意,關(guān)聯(lián)查詢的字段應(yīng)該在模型中設(shè)定unique=True,如果不唯一,在User表中可能會查到多條數(shù)據(jù),這樣Book表是不知道要關(guān)聯(lián)那條記錄的

將書籍導(dǎo)出后的效果如下:


image.png

再來說導(dǎo)入,導(dǎo)入的表如下表:


image.png

提交后的校驗界面如下:


image.png

確認導(dǎo)入后,數(shù)據(jù)如下:


image.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容