##やりたいこと
CSVを複数取り込み、処理をしてから結果を同じくCSVで返す。
今回は以下のケースを使用してこれを実装する。
##ケース
ある大学で、以下のCSVが入手できている。
available.csv
では、各クラスのキャパシティがどのくらいかの情報を持つ。
reserved.csv
では、各クラスで何人の生徒が授業を履修予定かの情報を持つ。
今回、これらのファイルをブラウザ上でアップロードすると、現在どのくらい各クラスに空きがあるかを示すCSVをダウンロードする様なアプリを作成する。
##設計
フロント側では、主に以下の2つの動きがある。
①:ファイルアップロード
②:アップロードされたファイルを元に各クラスの空きを示すCSVを吐き出す
##前提
Djangoのプロジェクトフォルダ(myproject
)とアプリフォルダ(app
)が既にある前提とする。ない場合はこちらを参照。
##フォルダ構成
今回、以下のようなフォルダ構成を取る。
├─app
│ │ admin.py
│ │ apps.py
│ │ forms.py
│ │ functions.py
│ │ models.py
│ │ tests.py
│ │ views.py
│ │ __init__.py
│ │
│ ├─migrations
│ │ __init__.py
│ │
│ ├─static
│ │ └─upload
│ │ test.txt
│ │
│ └─templates
│ index.html
│
├─myproject
│ │ settings.py
│ │ urls.py
│ │ wsgi.py
│ │ __init__.py
│ │
│ └─static
│ humans.txt
│
└─staticfiles
##フォーム作成
まず、ファイルをアップロードできるよう、以下のようにforms.py
でフォームを定義する。
from django import forms
#ClassroomFormを定義
class ClassroomForm(forms.Form):
availablity = forms.FileField()
reservation = forms.FileField()
##フォームをHTMLへ渡す
次に、作成したフォームをフロント側で表示するため、以下のようにviews.py
でフォームをHTMLに渡す。
from django.shortcuts import render
from app.forms import ClassroomForm
def index(request):
###ClassroomFormをindex.htmlに渡す
classroom = ClassroomForm()
return render(request,"index.html",{'form':classroom})
これでindex.html
で{{ form.as_p }}
を使用し、以下のようにフォームを引っ張ってこれる。
<body>
<form method="POST" class="post-form" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="save btn btn-default">Save</button>
</form>
</body>
##POST処理
このままフォームを提出すると、views.py
のindex
メゾットにPOSTされた情報が渡される。なので、POST処理をするようindex
を以下のように書き換える。
from django.shortcuts import render
from django.http import HttpResponse
from app.functions import process_files
from app.functions import write_into_csv
from app.forms import ClassroomForm
from django.template import loader
import csv
def index(request):
if request.method == 'POST':
classroom = ClassroomForm(request.POST, request.FILES)
#classroomにデータがある場合
if classroom.is_valid():
availability = request.FILES['availablity']
reservation = request.FILES['reservation']
#implement process_files
csv_data = process_files(availability, reservation)
#download result as a csv format
response = write_into_csv(csv_data)
return response
else:
###ClassroomFormをindex.htmlに渡す
classroom = ClassroomForm()
return render(request,"index.html",{'form':classroom})
ここでは、functions.py
で定義されたprocess_files
とwrite_into_csv
という2つのメゾットを呼び出している。次でこのメゾットを定義する。
##ロジックを定義
functions.py
にてviews.py
で使用するロジックを定義する。
import csv
from django.http import HttpResponse
### Process csv files
def process_files(availability, reservation):
""" Description
:type availability:
:param availability:
:type reservation:
:param reservation:
:raises:
:rtype:
"""
availability_dict = convert_to_dict(availability)
reservation_dict = convert_to_dict(reservation)
csv_data = []
courses = list(availability_dict)
for course in courses:
remaining = availability_dict[course] - reservation_dict[course]
row = [course, remaining]
csv_data.append(row)
return csv_data
def convert_to_dict(file):
""" Description
:type file:
:param file:
:raises:
:rtype:
"""
data = file.read().decode("utf-8-sig")
lines = data.split("\r\n")
dict = {}
for line in lines:
fields = line.split(",")
course = fields[0]
capacity = int(fields[1])
dict[course] = capacity
return dict
def write_into_csv(csv_data):
""" Description
:type csv_data:
:param csv_data:
:raises:
:rtype:
"""
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="download.csv"'
writer = csv.writer(response)
for row in csv_data:
writer.writerow(row)
return response
以上で設計通りの機能を実装できた。
##最後に
参考になったらぜひLikeをしてください。
##Github
https://github.com/norifumi92/csv_uploader/tree/develop
##参考
https://www.javatpoint.com/django-file-upload
https://codepen.io/adamlaki/pen/VYpewx
https://www.pythoncircle.com/post/30/how-to-upload-and-process-the-csv-file-in-django/
https://into-the-program.com/customize-input-type-file/
https://docs.djangoproject.com/en/3.0/topics/forms/