Python
Flask

Flaskでcsvダウンロード機能を作成する

More than 1 year has passed since last update.

はじめに

PythonのフレームワークであるFlask上で、csvでファイルをダウンロードする必要がありました。今回はSQLAlchemyを使ってDB上のデータをcsvに変換して、FlaskのAPIを使ってダウンロードできるようしました。

Flaskでcsvダウンロード

ダウンロードするデータは以下のような形式になってるとします。シンプルなFlaskアプリケーションで、今回はusersテーブルを作成しておきましょう。このテーブルのデータをDBから取得して、csv形式でダウンロードできるようにします。

from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
from io import StringIO

app = Flask(__name__)
DB_URL = <DBURI>
app.config['SQLALCHEMY_DATABASE_URI'] = DB_URI
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)

#サンプルデータのスキーマ
class User(db.Model):

    __tablename__ = "users"
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(200))
    gender = db.Column(db.Integer)
    age = db.Column(db.Integer)
    created_at = db.Column(db.DateTime)

    def __init__(self, username, gender, age):
        self.username = username
        self.gender = gender
        self.age = age
        now = datetime.now()
        self.created_at = now

    def __repr__(self):
        return '<User %r>' % self.username

  
今回はダウンロードを/download/~から実行できるようにします。

@app.route('/download/<obj>/')
def download(obj):

    f = StringIO()
    writer = csv.writer(f, quotechar='"', quoting=csv.QUOTE_ALL, lineterminator="\n")

    if obj == 'users':
        writer.writerow(['id','username','gender','age','created_at'])
        for u in User.query.all():
            writer.writerow([u.id, u.username,u.gender,u.age,u.created_at])

    res = make_response()
    res.data = f.getvalue()
    res.headers['Content-Type'] = 'text/csv'
    res.headers['Content-Disposition'] = 'attachment; filename='+ obj +'.csv'
    return res

肝はContent-Dispositionヘッダーであり、このヘッダーの値に応じてブラウザ上でインラインで表示されるか、アタッチメントとして処理されるか決まります。
今回はattachmentと指定して、添付ファイル化します。ダウンロードされる際のファイル名はfilename=で指定することができます。

Flaskでレスポンスの形式を弄るときはmake_response()を弄ればOKです
またpythonでcsvに変換するときはStringIOを使用しましょう。

終わりに

方法は単純で、データをcsv形式に変換して、レスポンスの際にHTTPヘッダーを書き換えるだけです。ダウンロードという簡単な機能ですが、HTTPについてあまり知らないと少し引っかかってしまうかもしれません。

参照

MDN web docs: Content-Disposition