【Django】データベースAPIを使ってデータをCSVファイルに書き込む


投稿日 2020年8月16日 >> 更新日 2023年3月1日

今回は、Djangoの対話型shellからデータベースAPIを使用してデータをCSVファイルに書き込んでいきたいと思います。

CSVファイルに書き込むことで、データのバックアップだったり、他のデータベースに保存できたり、データ分析に活用できたりします。

そして、DjangoのデータベースAPIに使い慣れることで、面倒な処理も一括で行うことが可能だったり様々なオブジェクトがどのように出力されるかなどを容易に試すことができるので非常に便利です。

実行環境&使用ライブラリ

実行環境
Windows Subsystem for Linux
Python 3.6.9
pip 9.0.1
使用ライブラリ ライセンス
Django==3.1 BSD

モデル一覧と準備

「test_app」というアプリ名の中のモデルは以下のようなCategoryモデルとTodoモデルがあり、CategoryモデルはTodoモデルによって関連付けられています。

# test_app/models.py

from django.db import models


class Category(models.Model):
    name = models.CharField(max_length=50)

    def __str__(self):
        return self.name


class Todo(models.Model):
    title = models.CharField(max_length=255)
    category = models.ForeignKey(Category, on_delete=models.PROTECT)
    created_at = models.DateField(auto_now_add=True)

    def __str__(self):
        return self.title

各モデルにはデータがまだ存在していないので、データベースAPIを使用してデータを追加していきます。

※マイグレーションは実行済み

# 実行

$ python3 manage.py shell
>>> # 各モデルのインポート
>>> from test_app.models import Category, Todo
>>>
>>> # Categoryに保存するための名前を準備
>>> category_list = ['A', 'B', 'C']
>>>
>>> # 準備した要素を順番に保存
>>> for row in category_list:
...     Category.objects.create(
...         name=row
...     )
...
<Category: A>
<Category: B>
<Category: C>
>>>
>>> # Todoに保存するためのタイトルを準備
>>> todo_list = ['test1', 'test2', 'test3', 'test4', 'test5']
>>>
>>> # カテゴリーを指定するためのidを準備
>>> category_number = [1, 2, 3, 1, 2]
>>>
>>> # zipメソッドを使ってまとめてループ保存
>>> # created_atは自動保存されるので引数はtitleとcategoryのみ
>>> for title, number in zip(todo_list, category_number):
...     Todo.objects.create(
...         title=title,
...         category=Category.objects.get(id=number)
...     )
...
<Todo: test1>
<Todo: test2>
<Todo: test3>
<Todo: test4>
<Todo: test5>

データの準備が整ったので、CSVファイルに保管していきます。

CSVファイルに保管

では引き続きDjangoの対話型shellを実行してCSVファイルに保管していきます。

CSVファイルのヘッダー(列名の一番上)用にフィールド名を取得します。

>>>
>>> category_name = Category._meta.get_fields()
>>>
>>> category_name_values = [row.name for row in category_name]
>>>
>>> category_name_values
['todo', 'id', 'name']

関連付け先が「todo」、そして「id」と「name」というフィールド取得しました。

「todo」と「id」というフィールドはDjangoが自動で作成しているフィールドです。

次に保存されているデータを取得します。

>>>
>>> category_list = Category.objects.values_list()
>>>
>>> category_data = [row for row in category_list]
>>>
>>> category_data
[(1, 'A'), (2, 'B'), (3, 'C')]

idナンバーと要素のデータを取得しました。

「Category.objects.values_list()」の引数にフィールド名を与えると、そのフィールド内の要素だけを取得することもできます。

>>> Category.objects.values_list('name')
<QuerySet [('A',), ('B',), ('C',)]>

それではヘッダーを設けるために、「category_data」リストの0番目にフィールド名を挿入します。

フィールド名が格納された「category_name_values」の1番目と2番目だけをスライスで取り出して、insertメソッドで追加します。

>>> # category_name_values[0]の'todo'以外を挿入
>>> # リスト内はタプルなので、「()」で囲む
>>> category_data.insert(0, (category_name_values[1], category_name_values[2]))
>>>
>>> category_data
[('id', 'name'), (1, 'A'), (2, 'B'), (3, 'C')]

これで「id」と「name」の列が揃えられたので、CSVファイルに保管します。

ここではファイル保管先をDjangoのプロジェクトディレクトリとします。

>>> import csv
>>>
>>> with open('category.csv', 'wt') as f:
...     csvout = csv.writer(f)
...     csvout.writerows(category_data)
...
>>>

Category内のデータがCSVファイルとして保管することが出来ました。

Python標準ライブラリの「subprocess」モジュールを使ってファイルの確認をしてみます。

>>> import subprocess
>>>
>>> # カレントディレクトリの確認
>>> subprocess.call('ls')
category.csv  config  db.sqlite3  manage.py  test_app
0
>>>
>>> # ファイルの中身を表示
>>> subprocess.call(['cat', 'category.csv'])
id,name
1,A
2,B
3,C
0
>>>

ではTodoモデルのデータも一気に実行していきます。

>>> # Todo、csv、subprocessモジュールはインポート済み
>>>
>>> # フィールド名の取得
>>> todo_name = Todo._meta.get_fields()
>>> todo_name_values = [row.name for row in todo_name]
>>>
>>> todo_name_values
['id', 'title', 'category', 'created_at']
>>>
>>> # 要素の取得
>>> todo_list = Todo.objects.values_list()
>>> todo_data = [row for row in todo_list]
>>>
>>> todo_data
[(1, 'test1', 1, datetime.date(2020, 8, 16)), (2, 'test2', 2, datetime.date(2020, 8, 16)), (3, 'test3', 3, datetime.date(2020, 8, 16)), (4, 'test4', 1, datetime.date(2020, 8, 16)), (5, 'test5', 2, datetime.date(2020, 8, 16))]
>>>
>>> # リストの0番目にフィールド名を挿入
>>> # リスト内はタプルなので、tupleで変換
>>> todo_data.insert(0, tuple(todo_name_values))
>>>
>>> todo_data
[('id', 'title', 'category', 'created_at'), (1, 'test1', 1, datetime.date(2020, 8, 16)), (2, 'test2', 2, datetime.date(2020, 8, 16)), (3, 'test3', 3, datetime.date(2020, 8, 16)), (4, 'test4', 1, datetime.date(2020, 8, 16)), (5, 'test5', 2, datetime.date(2020, 8, 16))]
>>>
>>> # csvファイルに保管
>>> with open('todo.csv', 'wt') as f:
...     csvout = csv.writer(f)
...     csvout.writerows(todo_data)
...
>>> 
>>> # カレントディレクトリの確認
>>> subprocess.call('ls')
category.csv  config  db.sqlite3  manage.py  test_app  todo.csv
0
>>>
>>> # ファイルの中身を表示
>>> subprocess.call(['cat', 'todo.csv'])
id,title,category,created_at
1,test1,1,2020-08-16
2,test2,2,2020-08-16
3,test3,3,2020-08-16
4,test4,1,2020-08-16
5,test5,2,2020-08-16
0
>>>
>>> # 対話終了
>>> quit()

今回は単純にデータを取得してCSVファイルに保管しただけですけど、別の機会にCSVファイルから特定のモデルへの保存を行ったり、WebページからCSVファイルをダウンロードできるような機能を紹介していきます。

それでは以上となります。

最後までご覧いただきありがとうございました。