DjangoでcsvファイルをアップロードしてDBにレコードを追加する方法
DjangoでcsvファイルをアップロードしてDBにレコードを追加する方法をご紹介します。
以下の記事の続きです。
目次
条件
- Django 2.1.4
- Python 3.7.0
前提
モデル
場所:気象データ = 1:N になるようモデルを作成します。
# models.py from django.db import models from django.urls import reverse from datetime import datetime as dt class Location(models.Model): """場所モデル""" class Meta: db_table = 'location' name = models.CharField(verbose_name='ロケーション名', max_length=255) memo = models.CharField(verbose_name='メモ', max_length=255, default='', blank=True) author = models.ForeignKey( 'auth.User', on_delete=models.CASCADE, ) created_at = models.DateTimeField(verbose_name='登録日時', auto_now_add=True) updated_at = models.DateTimeField(verbose_name='更新日時', auto_now=True) def __str__(self): return self.name @staticmethod def get_absolute_url(self): return reverse('monitor:index') class WeatherData(models.Model): """気象データモデル""" class Meta: db_table = 'weather_data' unique_together = (('location', 'data_datetime'),) location = models.ForeignKey(Location, verbose_name='ロケーション', on_delete=models.PROTECT) data_datetime = models.DateTimeField(verbose_name='データ日時', default=dt.strptime('2001-01-01 00:00:00', '%Y-%m-%d %H:%M:%S')) temperature = models.FloatField(verbose_name='気温') humidity = models.FloatField(verbose_name='湿度') created_at = models.DateTimeField(verbose_name='登録日時', auto_now_add=True) updated_at = models.DateTimeField(verbose_name='更新日時', auto_now=True) def __str__(self): return self.location.name + ":" + str(self.data_datetime)
ファイルのアップロード処理
ファイルのアップロード処理はすでに実装済みという前提で話を進めます。
csvデータ登録処理
DB関連処理
DB処理用のソース(addCsv.py)を追加します。
「where not exists」句を使用して、存在しないレコードのみをDBに追加するようにします。
# addCsv.py from django.db import connection import logging import csv from datetime import datetime as dt logger = logging.getLogger('development') # DBへの追加用SQL sql_insert = ("insert into weather_data (data_datetime, temperature, humidity, location_id, created_at, updated_at) " "select * from (select %s as data_datetime, %s as temperature, %s as humidity, %s as location_id, " "%s as created_at, %s as updated_at) as tmp " "where not exists (select * from weather_data where location_id = %s and data_datetime = %s)") def regist_data(cursor, file_path): # ファイル読み込み(CSV形式) try: file = open(file_path, newline='') except IOError: logger.warning('対象ファイルが存在しません:' + file_path) logger.warning('DB登録は行いません:' + file_path) else: logger.info('=== > Start DB登録 ==') with file: reader = csv.reader(file) header = next(reader) # ヘッダーをスキップ for row in reader: str_time = [dt.now().strftime('%Y-%m-%d %H:%M:%S')] add_data = [] add_data.extend(row) # csvから読み取った情報 add_data.extend(str_time) # created_at add_data.extend(str_time) # updated_at add_data.append(row[3]) # ロケーションID(対象レコードがDBに存在するかの確認に使用する) add_data.append(row[0]) # 対象日時(対象レコードがDBに存在するかの確認に使用する) logger.debug('add_data = ' + str(add_data)) # レコード追加 cursor.execute(sql_insert, add_data) logger.info("=== > End DB登録 ==") # csvファイルのデータをDBに追加する。 def insert_csv_data(file_path): logger.info('== csvデータ登録処理開始 ==') with connection.cursor() as cursor: regist_data(cursor, file_path) logger.info('== csvデータ登録処理終了 ==')
views.py
# views.py抜粋 import os from monitor import addCsv UPLOAD_DIR = os.path.dirname(os.path.abspath(__file__)) + '/uploads/' # アップロードしたファイルを保存するディレクトリ # アップロードされたファイルのハンドル def handle_uploaded_file(f): path = os.path.join(UPLOAD_DIR, f.name) with open(path, 'wb+') as destination: for chunk in f.chunks(): destination.write(chunk) try: addCsv.insert_csv_data(path) # csvデータをDBに登録する except Exception as exc: logger.error(exc) os.remove(path) # アップロードしたファイルを削除 # ファイルアップロード def upload(request): if request.method == 'POST': form = UploadFileForm(request.POST, request.FILES) if form.is_valid(): handle_uploaded_file(request.FILES['file']) return redirect('monitor:upload_complete') # アップロード完了画面にリダイレクト else: form = UploadFileForm() return render(request, 'monitor/upload.html', {'form': form}) # ファイルアップロード完了 def upload_complete(request): return render(request, 'monitor/upload_complete.html')
実行結果
前提
以下のようなレコード状態であるものとします。
レコード状態が適切でないと外部キー制約で追加時にエラーになる場合があります。
例えば今回のモデルでは、存在しないlocation_idのレコードをweather_dataテーブルに追加しようとすると外部キー制約エラーになります。
auth_user
管理者(admin)id=1が存在する。
location
管理者(admin)に紐づくレコードが2つ(id=1,2)が存在する。
アップロードするcsvファイル
data_datetime,temperature,humidity,location_id 2018-12-16 00:00:00,5,10,1 2018-12-16 01:00:00,1,50,1 2018-12-16 02:00:00,5,20,1 2018-12-16 03:00:00,10,70,1 2018-12-16 04:00:00,15,20,1 2018-12-16 00:00:00,25,80,2 2018-12-16 01:00:00,12,20,2 2018-12-16 02:00:00,5,50,2 2018-12-16 03:00:00,30,30,2 2018-12-16 04:00:00,15,70,2
アップロード前
weather_dataテーブルにレコードは存在しません。
アップロード後
weather_dataテーブルにレコードが追加されています。
また、views.pyの「os.remove(path)」でアップロードしたファイルの削除を行っているため、サーバーにファイルは残りません。
参考
サンプルソース
サンプルソースをGitHubに公開しています。
https://github.com/kzmrt/graph
知的好奇心さま
いつも拙い私のお願いにお答えいただき、大変ありがとうございます。
公開していただいたサンプルソースすばらしいです。
こんなことがいとも簡単に作れてしまう知的好奇心さまを尊敬いたします。
私では、書かれている中のコードを読み解くのがまだまだできない状況ですが、
少しずつ学習していき、自分の作りたいアプリを自作していきたいと考えています。
先日「現場で使えるdjangoの教科書」を買ったばかりなのですが、
もし、知的好奇心さまがご存じの学習する上で参考となります書籍やサイトなどがありましたら
ご教示いただけませんでしょうか。
書籍については現在お持ちのものをご使用いただくのが良いかと思います。
Djangoを学ぶ上でのおすすめWebサイトは以下の2つです。
Djangoチュートリアル
https://docs.djangoproject.com/ja/2.1/intro/tutorial01/
Django Grilsのチュートリアル
https://tutorial.djangogirls.org/ja/
私のおすすめ学習手順は以下の通りです。
1.Djangoチュートリアル等に沿って1通りの作成方法を学ぶ。
2.チュートリアルで作成したプロジェクトを改造してみる。
3.自分で考えたプロジェクトを1から作成する。
⇒分からないことはその都度、書籍を参照したりネットで検索したりして試行錯誤する。
「まずは基本を押さえて、その後に試行錯誤する」というのが上達への近道と考えております。
まずは基本を押さえるですね。
チュートリアルの学習から始めて行きたいと思います。
ありがとうございます。