django-import-exportで管理画面からCSVをインポートできるようにした

最近作っているもので、CSVでマスターデータを作って流し込みがしたくなりました。
調べているとdjango-import-export というライブラリがあったので試してみました。

環境

  • Python v3.7.2
  • Django v2.2
  • django-import-export v1.2.0

最終的にこうなる

Djangoの管理画面に登録したモデル(?)の一覧ページと変更ページの右上にインポートとエクスポートのボタンが表示され、ここから進んだ先の画面でインポート/エクスポートできるようになります。ボタンがどこに表示されるのか分からなくてちょっと探したので自分のためにメモ。

管理画面のデータの一覧画面右上にインポート・エクスポートのボタンが表示されている様子

やってみる

設定方法なんかは入門のページに載っていますが、自分のために書いておきます。

Projectファイルなど一部省略しますが、App内は以下のようなファイル構成の想定です。

app
├ :
├ models.py
├ admin.py
└ adminResources.py

流れはこんな感じ。

  1. モデルの定義
  2. データの用意
  3. リソースを定義
  4. 管理画面への登録
  5. 管理画面から確認

モデルの定義

モデルを定義しておきます。

app/models.py

from django.db import models


class Food(models.Model):
    name = models.CharField('名前', max_length=128)

    class Meta:
        db_table = 'food'
        verbose_name = '食べ物'
        verbose_name_plural = '食べ物一覧'

    def __str__(self):
        return(self.name)

データの用意

Excelやスプレッドシートでデータを用意。今回はスプレッドシートでこんな感じにデータを用意しました。

スプレッドシートでデータを用意

パッと思いついたのが男子高校生みたいなラインナップで我ながら笑った

今回必要なのかなと思って id 列も作ってしまいましたが、id は AUTO INCREMENT な処理があるため上手くインポートできなくなります。
後述しますが、インポート対象から任意の列を除外することも可能 です。

用意できたらCSVでエクスポートしておきます。

リソースを定義

app/adminResources.py

from import_export import resources

from .models import Food


class FoodResource(resources.ModelResource):

    class Meta:
        model = Food
        skip_unchanged = True
        report_skipped = False
        import_id_fields = ('name', )

ココが今回のキモです。

import_fieldsimport_id_fields があるけど、id部分を上手く加算してくれる import_id_fields がオススメ。
このプロパティにCSV内のデータにあるものの中からインポート対象の列を指定する。

また、id 部分は exclude() にidを指定して除外しておく。( import_id_fields があるからもしかしたら要らないかも。)

2019年6月25日追記 import_id_fields を使っている場合、 ``exclude()`` で id を指定する必要はなかった。 追加していたことで、importされるデータのid列の番号が飛んで1から始まらない状態になってしまう。

さらに、CSVインポートで運用していくと二回目以降に重複したデータを入れることになりかねないんだけど、 skip_unchanged = True を入れておくことで、重複箇所で変更の無いものはスキップしつつインポートをかけてくれるようになる。

管理画面への登録

app/admin.py

from django.contrib import admin
from import_export.admin import ImportExportModelAdmin
from .adminsResources import FoodResource
from .models import Food


@admin.register(Food)
class FoodAdmin(ImportExportModelAdmin):
    resource_class = FoodResource

定義したリソースクラスを管理画面で使えるようにするため admin.py で紐づける。

やってみて

一応これで管理画面に「最終的にこうなる」で表示したようなボタンが表示されているはず。

インポート自体は少し遅いけど、そもそもCSVで入れられるようになった事とファイルを選択した後で変更分の差分表示をしてくれるのはありがたいなと思います。