Django-Import-Export插件控制數據導入流程
- 2022 年 8 月 29 日
- 筆記
- django, Python / Django
前言
之前寫過兩篇跟這個插件有關的文章,可以回顧一下:
最近有個朋友留言問我一個關於django-import-export插件的問題
為了形象表達這個問題,我舉個書籍管理的例子來描述一下
資料庫表
id name price 1 book1 10 2 book2 20 3 book3 30 要導入的Excel表
id name price tax 4 book4 40 5 5 book5 50 6 6 book6 60 7 可以看到,Excel里每本書都有價格和稅兩個屬性,但資料庫只有價格一個屬性
導入的時候,需要把每本書的價格+稅,才是要存入數據的最終價格
在以前,這種問題場景我會建議直接用pandas來處理數據然後導入,django-import-export插件只用來做數據導出,因為它的文檔很簡陋,給的例子很難解決實際問題,往往某個需求用pandas手動處理只需要很少時間,用這個插件還得去啃源碼和簡陋的文檔,效率太低了。
不過本著折騰的精神,還是來研究一下這個用django-import-export到底能不能實現這個功能。(結果當然是可以的,不然也沒有這篇文章了)
分析
首先是看官網文檔,有一個節點叫import data workflow
地址://django-import-export.readthedocs.io/en/latest/import_workflow.html
import_data
(dataset, dry_run=False, raise_errors=False)The
import_data()
method ofResource
is responsible for importing data from a given dataset.
dataset
is required and expected to be atablib.Dataset
with a header row.
dry_run
is a Boolean which determines if changes to the database are made or if the import is only simulated. It defaults toFalse
.
raise_errors
is a Boolean. IfTrue
, import should raise errors. The default isFalse
, which means that eventual errors and traceback will be saved inResult
instance.
根據文檔,在導入數據的時候,我們可以通過import_data
這個hook來對要導入的數據進行處理
然後這個hook有個參數,dataset
,這個是tablib的東西
關於這個tablib,我之前沒用過,查了一下,是requests作者做的庫,那想來應該不會差
官網文檔是://tablib.readthedocs.io/en/stable/tutorial.html
寫程式碼
直接把官方的程式碼例子拿來用
程式碼倉庫://github.com/django-import-export/django-import-export
同樣是這個書籍管理的
Models程式碼
來看看它的model設計
class Book(models.Model):
name = models.CharField('Book name', max_length=100)
author = models.ForeignKey(Author, blank=True, null=True, on_delete=models.CASCADE)
author_email = models.EmailField('Author email', max_length=75, blank=True)
imported = models.BooleanField(default=False)
published = models.DateField('Published', blank=True, null=True)
published_time = models.TimeField('Time published', blank=True, null=True)
price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)
added = models.DateTimeField(blank=True, null=True)
categories = models.ManyToManyField(Category, blank=True)
def __str__(self):
return self.name
很多欄位
要導入的數據
依然是官方提供的,各種格式都有,我選csv的,比較方便
id,name,author_email
1,Some book,[email protected]
轉換成表格長這樣
id | name | author_email |
---|---|---|
1 | Some book | [email protected] |
可以看到欄位比model定義的少很多
我們要在導入的時候,給dataset加上價格(price)屬性
Resources程式碼
這是原本的程式碼
class BookResource(ModelResource):
class Meta:
model = Book
def for_delete(self, row, instance):
return self.fields['name'].clean(row) == ''
return super(BookResource, self).import_data(
dataset, dry_run, raise_errors, use_transactions,
collect_failed_rows, rollback_on_validation_errors, **kwargs
現在我們要加一個hook來處理導入的數據
程式碼如下
def import_data(self, dataset: tablib.Dataset, dry_run=False, raise_errors=False,
use_transactions=None, collect_failed_rows=False,
rollback_on_validation_errors=False, **kwargs):
cols = []
for item in dataset['id']:
cols.append(int(item) * 99)
dataset.append_col(cols, header='price')
print(dataset)
return super(BookResource, self).import_data(
dataset, dry_run, raise_errors, use_transactions,
collect_failed_rows, rollback_on_validation_errors, **kwargs
)
使用DataSet
的append_col
方法來添加一個新的列
關於這個DataSet
的更多操作請參考Tablib的文檔
這部分的具體操作可以根據實際需求來做修改,這裡我直接簡單粗暴的把ID乘以99
處理完DataSet
之後記得要執行父類的import_data
,完成數據導入的操作。
效果
在admin後台執行導入,可以得到以下的結果
可以看到price屬性變成99
ID | NAME | AUTHOR | AUTHOR_EMAIL | IMPORTED | PUBLISHED | PUBLISHED_TIME | PRICE | ADDED | CATEGORIES | |
---|---|---|---|---|---|---|---|---|---|---|
New | 1 | Some book | [email protected] | 0 | 99 |
就OK了,搞定~
其實還挺簡單的,只是官方文檔太簡陋了,連個例子的沒有,只能自己摸索一下
參考資料
雖然前面都有鏈接,這裡再總結一下吧
- 插件文檔://django-import-export.readthedocs.io/en/latest/import_workflow.html
- 插件項目主頁://github.com/django-import-export/django-import-export
- Tablib文檔://tablib.readthedocs.io/en/stable/tutorial.html