之前寫過兩篇跟這個外掛有關的文章,可以回顧一下:
最近有個朋友留言問我一個關於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
地址:https://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作者做的庫,那想來應該不會差
官網檔案是:https://tablib.readthedocs.io/en/stable/tutorial.html
直接把官方的程式碼例子拿來用
程式碼倉庫:https://github.com/django-import-export/django-import-export
同樣是這個書籍管理的
來看看它的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)屬性
這是原本的程式碼
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了,搞定~
其實還挺簡單的,只是官方檔案太簡陋了,連個例子的沒有,只能自己摸索一下
雖然前面都有連結,這裡再總結一下吧