見出し画像

django管理画面へのデータ一括入出力機能追加

webアプリケーションを作る中で、データベースへ大量のデータを登録する場面が出て来ます。ポートフォリオ作成時、動作確認や見栄えのためにデータをそれなりに登録する程度であれば、管理画面から手入力でも良いのですが、出来ればCSVやExcelデータなどから一括でデータをデータベースへ登録したいと思います。
 実際の活用シーンでは、djangoで作成した社内アプリのデータベースに既存のCSVデータやExcelデータを流し込みたいという場面が考えられます。そのような時には、管理画面からデータ一括入出力機能はとても役に立ちます。
 django-import-exportというパッケージを用いれば、とても簡単に一括インポートエクスポート機能を追加できます。

目標

django-import-exportを用いて管理画面からcsv/xlsx形式のデータをデータベースへ反映出来る。

開発環境

・Python 3.9.4
・django 3.2.4
・Mac M1 Big Sur
・Pycharm(Visual Studio Codeでも可)
・ブラウザはSafari(もしくはChrome)
・miniforge(ARMアーキテクチャに最適化されているため)
↑は、Windows環境であればAnacondaでも構いません。windows環境でエクセルデータの入出力が出来ることは確認しています。

必要なパッケージ

・django-import-export

開発の手順

以下の手順で管理画面にCSV /Excelファイルの一括インポートエクスポート機能を実装していきます。

①パッケージのインストールとアプリの作成
②settings.pyの設定と管理ユーザー作成
③登録したいデータと対応するモデル作成
④admin.pyへdjango-import-exportの設定
⑤管理画面からcsvデータの一括登録

 ①アプリの作成とパッケージのインストール 

まずは、プロジェクトを作成します。ここではappというアプリを作成しました。

% python manage.py startapp app

次に、仮想環境にパッケージをインストールします(ここでは、condaを使っていますが、pipでも構いません。condaとpipは混ぜない方が良いようです)。

% conda install -c conda-forge django-import-export

まずは、プロジェクトを作成します。ここではappというアプリを作成しました。

% python manage.py startapp app

 ②settings.pyの設定と管理ユーザー作成

インストールしたパッケージを使えるようにするため、settings.pyを設定していきます。また、日本語化やタイムゾーン変更などのお決まりの設定も変更しています。最後の部分は、データの一括入力や削除を実施する際にエラーを回避するために追加しています。

・・・省略・・・
INSTALLED_APPS = [
   'django.contrib.admin',
   'django.contrib.auth',
   'django.contrib.contenttypes',
   'django.contrib.sessions',
   'django.contrib.messages',
   'django.contrib.staticfiles',
   'app',  # 追加
   'import_export',  # 追加
]
・・・省略・・・

LANGUAGE_CODE = 'ja'  # 変更
TIME_ZONE = 'Asia/Tokyo'  # 変更

・・・省略・・・
DATA_UPLOAD_MAX_NUMBER_FIELDS = 10240 # 追加

ここで一旦マイグレーションして、データベースを作成します。

% python manage.py makemigrations
% python manage.py migrate

管理者(スーパーユーザー)を作成し、管理画面を使えるようにします。

% python manage.py createsuperuser

画像2

 ③登録したいデータと対応するモデル作成

今回入力するサンプルデータをkaggleからダウンロードします。アメリカのピザレストランの販売データで形式はcsv、レコード数3510、フィールド数は17です。データを確認したところ、id列があります。ハマって分かったのですが、django-import-exportではid列があるとインポート出来ない事がありますので、ここを事前にcust_idに書き換えます。

画像1

画像3

次にモデルを作成していきます。今回は、全てのデータだけではなく住所、市、郵便番号、レストラン名、カテゴリー、IDをモデルに取り込むこととしますので、それぞれのモデルフィールドを定義します。class Metaでは、データベースのテーブル名、管理画面での表示の設定をしています。

app/models.py

from django.db import models

class PizzaRestaurant(models.Model):
   address = models.CharField(verbose_name='住所', max_length=100, blank=True, null=True)
   city = models.CharField(verbose_name='市', max_length=100, blank=True, null=True)
   postalcode = models.CharField(verbose_name='郵便番号', max_length=20, blank=True, null=True)
   name = models.CharField(verbose_name='レストラン名', max_length=100, blank=True, null=True)
   category = models.CharField(verbose_name='カテゴリー', max_length=100, blank=True, null=True)
   cust_id = models.CharField(verbose_name='ID', max_length=100, blank=True, null=True)
   class Meta:
       db_table = 'pizza_restaurants'
       verbose_name = 'ピザ屋さん'
       verbose_name_plural = 'ピザ屋さんリスト'

 ④admin.pyへdjango-import-exportの設定

モデルが作成出来たので、django-import-exportの設定をadmin.pyにコードしていきます。まずは、django-import-exportから必要なライブラリをインポートします。また、先ほど定義したPizzaRestaurantモデルもインポートします。
 class PizzaRestaurantResource(resources.ModelResource):では、フィールドの設定と対象となるモデルを設定しています。入力対象となるcsvの列名とモデルのフィールド名が異なる場合は、ここで設定していきます。
 class Meta:では、使用するモデルの指定とデータを追加した場合に同じデータがすでにdbに存在した場合にはデータを重複して登録しない設定を指定しています。また、最後のuse_bulk = Trueですがデータをdbへ登録する際に1件ずつではなくbulkで登録する設定です。
 コード後半では、ImportExportModelAdminを継承したAdminクラスを作成しています。list_displayでは、管理サイト上に表示させるフィールドを設定します。

app/admin.py

from django.contrib import admin
from .models import PizzaRestaurant  # 追加
from import_export import resources  # 追加
from import_export.admin import ImportExportModelAdmin  # 追加
from import_export.fields import Field # 追加

class PizzaRestaurantResource(resources.ModelResource):
   # field名とcsvの列名が異なる場合はここで指定する。
   # ここでは、postalcode / postalCode、category / categoriesと微妙に異なる。   
   postalcode = Field(attribute='postalcode', column_name='postalCode')
   category = Field(attribute='category', column_name='categories')
   address = Field(attribute='address', column_name='address')
   city = Field(attribute='city', column_name='city')
   name = Field(attribute='name', column_name='name')
   csut_id = Field(attribute='cust_id', column_name='cust_id')
   # django-import-exportのModel設定
   class Meta:
       model = PizzaRestaurant
       # Controls if the import should skip unchanged records. Default value is False
       skip_unchanged = True
       use_bulk = True

@admin.register(PizzaRestaurant)
# ImportExportModelAdminを継承したAdminクラスを作成する
class PizzaRestaurantAdmin(ImportExportModelAdmin):
   ordering = ['id']
   list_display = ('id', 'postalcode', 'name', 'category', 'city', 'address','cust_id')
   # resource_classにModelResourceを継承したクラス設定
   resource_class = PizzaRestaurantResource

モデル作成も完了していますのでマイグレーションを行い、pizza_restaurantsテーブルを作成します。

 ⑤管理画面からcsvデータの一括登録

管理画面にログインし、実際にcsvデータをアップロードしてみます。画像のピザ屋さんリストを押すと右上にインポート、エクスポートボタンが追加されていますのでインポートボタンを押してcsvファイルを選択します。

ピザ屋反映管理サイト

インポートボタン

インポート

問題なければ、少し時間をおいてプレビュー画面に読み込んだデータが表示されます。ダークモードだと少しデータが見えにくいですが、データが問題なく取り込めていそうであれば、右上のインポート実行ボタンを押してデータベースに反映させます。データにエラーがある場合には、エラーメッセージが返されます。ここで、csvデータの方にid列が含まれていたのでハマりました。id列の列名を変更して問題は解決されました。
 今回はデータ数が3510ですので、10秒もかからずに反映できました(M1 Macの場合)。お使いのマシンによって、若干時間に差は出てくると思います。

反映後

実際にデータベースに反映されたかも確認します。無事に反映されている事がdbでも確認できました(下図参照) 

db反映後

django-import-exportを使ってみた感想ですが、データ読み込み時に少し手間取るところがありました。読み込み前に、ある程度データクレンジングはしておいた方が良さそうです。また、データ反映にそれなりに時間がかかります。10万行程度であれば、数分程度ですので我慢できます。しかし、データ数が数百万行以上になるともう少し別の方法が良いかもしれません。
 その辺りを差し引いても管理画面から簡単にデータを一括登録出来るというのは非常に便利です。外部キーなども設定できるようですので、興味ありましたら公式のマニュアルをご一読下さい。













この記事が気に入ったらサポートをしてみませんか?