はじめに
自作アプリでmysqlからPostgreSQLに鞍替えを行いました。
その修正の中で、当初はDDLでSQL文の中でcsvファイルをDBへインポートしていたのですが、DjangoでCommandを使う事で、csvファイルへのインポートができるということを知ったので実装した記録です。
DB周りはDjangoで実装するとメンテナンスもしやすそうです。
実装
ディレクトリについて
<projectフォルダ>/<appフォルダ>/management/commandsフォルダ配下に以下のようにimport用のpythonファイルを設けます。
<projectフォルダ>/<appフォルダ>/management/commands$ ls
import_firework.py import_sakura.py ...<projectフォルダ>/dataフォルダにimportするcsvファイル配置します。
<projectフォルダ>/data$ ls
firework.csv sakura.csvimport用のpythonファイルについて
- DjangoのBaseCommandを継承
- コマンド引数でCSVファイルパスを受け取り、データをパースしてDBに保存
します。
以下のようなファイルになります。
import csv
import re
from pathlib import Path
from django.core.management.base import BaseCommand
from django.db import transaction
from maps.models import SakuraEvent, SakuraSeason
class Command(BaseCommand):
help = 'Import sakura data from CSV'
def add_arguments(self, parser):
parser.add_argument('csv_file', type=str)
@transaction.atomic
def handle(self, *args, **options):
csv_path = Path(options["csv_path"])
if not csv_path.exists():
self.stderr.write(f"File not found: {csv_path}")
return
with csv_path.open(encoding="utf-8") as f:
reader = csv.DictReader(f)
for row in reader:
event, _ = SakuraEvent.objects.get_or_create(
spot_name=row["spot_name"],
defaults={
"common_season": row["common_season"],
"open_close": row["open_close"],
# ...他のフィールド
}
)
for month in months:
SakuraSeason.objects.get_or_create(
event=event,
season=month
)
self.stdout.write(self.style.SUCCESS("Sakura import completed"))Django側のModels
from django.db import models
class SakuraEvent(models.Model):
spot_name = models.CharField(max_length=200, primary_key=True)
common_season = models.CharField(max_length=200)
open_close = models.CharField(max_length=200)
# ...他のフィールド
class SakuraSeason(models.Model):
event = models.ForeignKey(SakuraEvent, on_delete=models.CASCADE)
season = models.IntegerField()
class Meta:
unique_together = ('event', 'season')シェルスクリプトによる一括実行をしておく
Djangoは起動時にentrypoint.shを実行するようにしていますが、以下のようにこのimportを一括実行しておくようにしておきます。
#!/bin/sh
set -e
python manage.py makemigrations --noinput
python manage.py migrate --noinput
python manage.py collectstatic --noinput
python manage.py import_sakura /code/data/sakura.csv
python manage.py import_firework /code/data/firework.csv
gunicorn --bind 0.0.0.0:8000 change_view.wsgiこれで、起動時にcsvファイルがDBにimportされる形になります。
DDLで実装する場合、仮にcsvファイルの元データを加工してimportしたい場合などコードが複雑になります。
なぜならSQLで書くことになるからです。
しかし、Djangoであれば、pythonで加工はかけるので可読性がより上がります。
最後に
バックエンドがDjangoだとこういう使い方ができるのがいいですね!
