DjangoでCommandを使ってみる

スポンサーリンク
Uncategorized

はじめに

自作アプリで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.csv

import用の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だとこういう使い方ができるのがいいですね!

タイトルとURLをコピーしました