20
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

DjangoでCSVアップロード、データ処理、ダウンロード機能を作成してみた

Last updated at Posted at 2020-01-29

##やりたいこと
CSVを複数取り込み、処理をしてから結果を同じくCSVで返す。
今回は以下のケースを使用してこれを実装する。

##ケース
ある大学で、以下のCSVが入手できている。
available.csvでは、各クラスのキャパシティがどのくらいかの情報を持つ。
reserved.csvでは、各クラスで何人の生徒が授業を履修予定かの情報を持つ。
今回、これらのファイルをブラウザ上でアップロードすると、現在どのくらい各クラスに空きがあるかを示すCSVをダウンロードする様なアプリを作成する。

##設計
フロント側では、主に以下の2つの動きがある。
①:ファイルアップロード
②:アップロードされたファイルを元に各クラスの空きを示すCSVを吐き出す

image.png

##前提
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.pyindexメゾットに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_fileswrite_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

以上で設計通りの機能を実装できた。

##Demo
File_Upload_Demo.gif

##最後に
参考になったらぜひ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/

20
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?