python3.doc_63
最終投稿日:2022年6月18日
[要件]
DB の内容をダウンロードする、又はアップロードして DB に反映させる
ダウンロード、アップロード共に【タイトル行】【ダブルクォーテーション】の有無を選択できる
[DB 内容]
テーブル名:books
項目内容:
ID 連番
title タイトル
price 金額
memo 備考
アプリ名は【booktitle】とします
● アプリの作成
python manage.py startapp booktitle
● settings.py
INSTALLED_APPS = [
~ 省略 ~
'booktitle.apps.BooktitleConfig'
]
● models.py
from django.db import models
class Books(models.Model):
class Meta:
#本当の DB 名
db_table = 'books'
verbose_name = '本管理'
title = models.CharField(max_length=50)
price = models.IntegerField()
memo = models.TextField(null=True)
※定義の後はお約束をコマンドプロンプトから叩く
python manage.py makemigrations
python manage.py migrate
● プロジェクト/urls.py
from django.contrib import admin
from django.conf.urls import include, url
from django.views.generic import RedirectView
from config import backends
urlpatterns = [
url(r'^$', RedirectView.as_view(url='booktitle/')),
url(r'^booktitle/', include('booktitle.urls', namespace='booktitle')),
]
● アプリ/urls.py
from django.conf.urls import url
from django.views.generic import RedirectView
from .views import *
app_name = 'booktitle'
urlpatterns = [
url(r'^$', RedirectView.as_view(url='index/')),
url(r'^index/$', IndexView.as_view(), name='index'),
]
● my_lib/my_common/file_common.py
"""
モデルのフィールド名をリストで返却する
"""
def csvTitleCreate(ct):
return [v.name for v in ct._meta.fields]
● forms.py
from django import forms
class BookForm(forms.Form):
dq_chk = forms.BooleanField(
label = '「"」で囲う',
required = False,
)
tit_chk = forms.BooleanField(
label = 'タイトル行を含む',
required = False,
)
csv = forms.FileField(
label = 'CSVインポート',
required = False,
)
● views.py
from django.shortcuts import render
from django.http.response import HttpResponse
from django.http.response import HttpResponseRedirect
from django.urls import reverse
from django.views.generic import View
from .models import Books
from .forms import BookForm
from io import TextIOWrapper, StringIO
import csv
import codecs
from my_common import file_common as fcmmon
class IndexView(View):
def get(self, request):
form = BookForm()
row = Books.objects.all()
d = {'form':form,'row':row}
return render(request, 'booktitle/index.html', d)
def post(self, request):
# ダウンロード
#ダウンロードボタンが押された場合
if 'down' in request.POST:
#StringIO クラスを使いバッファに CSV の内容をため込む準備をする
memory_file = StringIO()
#ダブルクォーテーションの有無の確認
if 'dq_chk' in request.POST:
#StringIO クラスのインスタンスを引数として
#CSV クラスの writer メソッドを実行する
writer = csv.writer(memory_file,quoting=csv.QUOTE_ALL)
else:
writer = csv.writer(memory_file)
#タイトル行の有無の確認
if 'tit_chk' in request.POST:
# タイトル作成
#タイトル行が必要な場合は独自メソッドよりタイトルのカラム名をリスト型で取得
title = fcmmon.csvTitleCreate(Books)
#インスタンス writer の writerow メソッドにタイトル行を追加する
writer.writerow(title)
#DB の内容を全て取得する
for row in Books.objects.all():
#レコード単位でリスト型にする
line = [row.id, row.title, row.price, row.memo]
#インスタンス writer の writerow メソッドに
#リストを追加する(DB の全内容ループ)
writer.writerow(line)
#HTTP ヘッダーの設定
response = HttpResponse(memory_file.getvalue(), content_type='text/csv')
#HTTP ヘッダーの設定
response['Content-Disposition'] = 'attachment; filename=books.csv'
#設定した HTTP ヘッダーを出力する
return response
# アップロード
else:
#ファイルが選択されているか確認
if 'csv' not in request.FILES:
form = BookForm(request.POST)
form.add_error('csv', 'CSVファイルを選択してください。')
d = {'form':form}
#ファイルが選択されていない場合は、index.html に戻す
return render(request, 'booktitle/index.html', d)
#TextIOWrapper クラスを使いメモリ上のファイルを引数に初期化する
form_data = TextIOWrapper(request.FILES['csv'], encoding='utf-8')
#CSV クラス reader メソッドで初期化したインスタンスを読み込む
csv_file = csv.reader(form_data)
#タイトル行の有無を確認
if 'tit_chk' in request.POST:
#タイトル行がある場合はイテレータを1行スキップする
next(csv_file)
try:
#CSV ファイルを1行単位で処理
for line in csv_file:
#get_or_create メソッドで DB を主キーで検索しなければ新規登録する
#(books, _ の _ には、新規作成した場合は True が入る(使わない))
books, _ = Books.objects.get_or_create(
id=line[0],
#新規登録の際は、初期値を設定しないと
#NOT NULL 制約のカラムがあるとエラーとなる
defaults = {'title':' ','price':0,'memo':' '}
)
#それぞれのカラムに CSV ファイルの内容を登録する
books.title = line[1]
books.price = line[2]
books.memo = line[3]
#レコードを登録する
books.save()
except:
print('失敗!タイトル行とか含んでないかな?')
return HttpResponseRedirect(reverse('booktitle:index'))
● layout/base.html
<html>
<head>
<title>{{my_title}}</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
</head>
<body>
{% block my_body %}{% endblock %}
</body>
</html>
● booktitle/index.html
{% extends 'layout/base.html' %}
{% block my_body %}
<div class='hoge'>いちらん画面</div>
<table border='1'>
<tr>
<th>ID</th><th>タイトル</th><th>価格</th><th>備考</th>
</tr>
{% for col in row %}
<tr>
<td>{{col.id}}</td>
<td>{{col.title}}</td>
<td>{{col.price}}</td>
<td>{{col.memo}}</td>
</tr>
{% endfor %}
</table>
<form method="POST" action="{% url 'booktitle:index' %}" enctype="multipart/form-data">
{{form.as_p}}
{% csrf_token %}
<BR>
<input type='submit' name='down' value='ダウンロード'>
<input type='submit' name='up' value='アップロード'>
</form>
<br>
{% endblock %}