はじめに
(MySQLとか使ってて)初期データをインポートしたい時なんかはHeidiSQLみたいなクライアントソフトを使えばいいんですが、勉強がてら作ってみました。
モデルの例
こんなモデルを用意してみました。
WeaponテーブルのデータをCSVでインポートしたいと思います。
外部キーも使いたかったので、サブウェポンとスペシャルを無理やりテーブルにして外部キーとして参照させています。
class SubWeapon(models.Model):
# TODO: Define fields here
name = models.CharField('名前', blank=True, max_length=100)
overview = models.TextField('概要', blank=True)
class Meta:
verbose_name = 'サブウェポン'
verbose_name_plural = 'サブウェポン一覧'
def __str__(self):
return(self.name)
class Special(models.Model):
# TODO: Define fields here
name = models.CharField('名前', blank=True, max_length=100)
overview = models.TextField('概要', blank=True)
class Meta:
verbose_name = 'スペシャル'
verbose_name_plural = 'スペシャル一覧'
def __str__(self):
return(self.name)
class Weapon(models.Model):
# TODO: Define fields here
no = models.IntegerField('番号', blank=True, null=True)
name = models.CharField('ブキ名', blank=True, max_length=100)
CATEGORY = (
('0', 'シューター'),
('1', 'マニューバー'),
('2', 'ブラスター'),
('3', 'スピナー'),
('4', 'ローラー'),
('5', 'フデ'),
('6', 'チャージャー'),
('7', 'スロッシャー'),
('8', 'シェルター'),
('9', 'ガイザー'),
)
category = models.CharField(blank=True, max_length=2, choices=CATEGORY)
sub = models.ForeignKey(SubWeapon, on_delete=models.PROTECT, null=True, blank=True)
special = models.ForeignKey(Special, on_delete=models.PROTECT, null=True, blank=True)
gauge = models.IntegerField('スペシャル必要ポイント', blank=True, null=True)
release_condition = models.CharField('解放条件', blank=True, max_length=100)
range = models.IntegerField('射程', blank=True, null=True)
continuous_power = models.IntegerField('連射力', blank=True, null=True)
fixed_num = models.IntegerField('確定数', blank=True, null=True)
class Meta:
verbose_name = 'ブキ'
verbose_name_plural = 'ブキ一覧'
def __unicode__(self):
return(self.name)
ちなみにスプラトゥーン2のブキは2018/4/17時点で80種類近く登場しているので、これらの情報をDjangoの管理画面から1つ1つ登録するなんてのは愚の骨頂です。
CSVファイルの用意
作ったモデルを元に、以下のようなCSVファイルを用意します。
※実際にインポートするときはヘッダー行は消してください。
また、models.py
でcategory
をchoicesで指定し、その選択肢を0~9の数字で設定したため、カテゴリの列には数字を入力する必要があります。
選択肢を数字ではなく、文字列にしておけばそのまま文字列で大丈夫なはずです。(未検証)
#番号,名前,カテゴリ,サブウェポン,スペシャル,スペシャル必要ポイント,解放条件,射程,連射力,確定数
101,わかばシューター,0,スプラッシュボム,インクアーマー,180,最初から,35,75,4
102,もみじシューター,0,ロボットボム,アメフラシ,180,ランク4,35,75,4
103,スプラシューター,0,クイックボム,スーパーチャクチ,200,ランク2,50,60,3
104,スプラシューターコラボ,0,スプラッシュボム,ジェットパック,210,ランク4,50,60,3
:
:
views.py
続いて views.py
を書いていきます。
from django.shortcuts import render
from .models import SubWeapon, Special, Weapon
import csv
from io import TextIOWrapper, StringIO
def upload(request):
if 'csv' in request.FILES:
form_data = TextIOWrapper(request.FILES['csv'].file, encoding='utf-8')
csv_file = csv.reader(form_data)
for line in csv_file:
weapon, created = Weapon.objects.get_or_create(name=line[1])
weapon.no = line[0]
weapon.name = line[1]
weapon.category = line[2]
weapon.sub = SubWeapon.objects.get(name=line[3])
weapon.special = Special.objects.get(name=line[4])
weapon.gauge = line[5]
weapon.release_condition = line[6]
weapon.range = line[7]
weapon.continuous_power = line[8]
weapon.fixed_num = line[9]
weapon.save()
return render(request, 'YpurApp/upload.html')
else:
return render(request, 'YourApp/upload.html')
最初にモデルやら必要なモジュールやらを読み込んでおきます。
ポイントはhtmlの<input>
タグで指定したname
を拾ってくるのでここの指定を間違えないことです。(上の例ではcsv
と指定しています。)
request.FILES
で受け取ったCSVファイルをcsv.reader
で読み込み、読み込んだファイルをfor文で回し、1行ずつ取り出します。
line
はリスト形式でCSVの1行のデータが1つずつ入っています。
line[0]
は番号、line[1]
は名前、といった感じ。
weapon, created = Weapon.objects.get_or_create(name=line[1])
データの重複登録を避けるため、get_or_create
を使ってWeaponテーブルに既に同じ名前のものが存在していれば新たにレコードは登録せずにgetするだけにします。
外部キーであるsub
とspecial
はそれぞれのテーブルからCSVに書かれたデータと一致するものをgetすればセット可能です。
weapon.sub = SubWeapon.objects.get(name=line[3])
weapon.special = Special.objects.get(name=line[4])
for文の処理が全て終わればテンプレートを描画します。
今回はサクセスメッセージなどは表示させず、単純にアップロード画面を表示するだけにしています。
urls.py
ブラウザからアクセスできるようにマッピングします。
from django.urls import path
from . import views
app_name = 'YourAppName'
urlpatterns = [
# :
# :
path('upload/', views.upload, name='upload'),
# :
# :
]
upload.html
標準のファイル選択ボタンは味気ないのでDropifyを利用しています。
↓このサイトを参考にさせて頂きました。
あのダサいデザインとはおさらば! input[type=file] をかっこ良く表示できる jQuery プラグイン『Dropify』の使い方
あとCSSフレームワークにMaterializeも利用しています。
{% extends "base.html" %}
{% load static %}
{% block title %}ブキデータアップロード{% endblock %}
{% block extrahead %}
<script>
$(function(){
$('.dropify').dropify();
})
</script>
{% endblock %}
{% block content %}
<h3>アップロードするよ</h3>
<form action="{% url 'YourAppName:upload' %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<input type="file" class="dropify" data-default-file="ファイルを選択してください" name="csv">
<button type="submit" class="btn" name="button">アップロード</button>
</form>
{% endblock content %}
完成
あとはこの画面からCSVファイルをインポートしてエラーが出なければOKです。
Djangoの管理画面にログインし、データがと登録されているか確認しましょう。
無事登録できました。
※Django IDが3桁になってるのは色々試行錯誤した結果です。
終わりに
本当はアップロードされたファイルの拡張子がちゃんとCSVかどうかとか、入力データが正しいかとか、ちゃんとバリデーションチェックしないといけませんが、今回は一切考慮していません。
本番で提供するアプリケーションではもっとちゃんと実装しましょう(自戒)
あとCSVデータ作るのが一番しんどかった。