FlaskでWebアプリを作成 続き2(編集と削除の機能をアップデートまで)
はじめに
前回の続きで、今回は編集画面を作って更新と削除の機能を追加していきます。
おまけ:CSSが反映されないときの対処法「リロードをshift+F5で行う」
目標:
- インデックス画面の「サンプル1」をユーザー名に変更
- 編集画面のレイアウトを作成
- 編集画面へ移動
- 編集画面で更新処理
- 編集画面に削除ボタン
- ゲストは更新・削除をできないように
1.「サンプル1」をユーザー名に変更
「index.html」の「<h3>サンプル1</h3>」を消してタイトルの部分に「タイトル @ユーザー名」にすることにしました。
server.py(変更部分)
#「/diarysave」へアクセスがあった場合「/blog/index」ページへ移動
@app.route('/diarysave', methods=['post'])
def diarysave(): #保存
image_url = ""
# 画像ファイルを保存
if request.files['image']:
f = request.files['image']
filepath = 'static/images/' + secure_filename(f.filename)
f.save(filepath)
filepath = '/' + filepath
image_url = filepath
# データベースに保存
name = name = session["user_name"]
if name == "":
title = request.form["title"]
else:
title = request.form["title"] + " @" + name
comment = request.form["comment"]
# JSTタイムゾーンを作成
jst = timezone(timedelta(hours=9), 'JST')
date = datetime.now(jst)
content = WikiContent(title,image_url,comment,date)
db_session.add(content)
db_session.commit()
return redirect(url_for("blog.index"))
title = request.form["title"] + " @" + name
「入力画面」で保存する時に「タイトル @ユーザー名」でタイトルを保存します。
2.編集画面を作成
「diary.html」を作成します。画面のレイアウトは入力画面とほとんど同じです。
※「diary.html」のコードは最後に載せます。
3.インデックス画面から編集画面へ移動
index.html
{% extends "blog/base.html" %}
{% block body %}
<link rel="stylesheet" type="text/css" href="/static/index.css">
<title>インデックス画面</title>
<div id="main">
{% for diary in all_diary[::-1] %}
<form action="/blog/diary/{{diary.id}}">
<label id="menu" for="{{diary.id}}">
<div class="table">
<h3>{{diary.title}}</h3>
<p><img src={{diary.image_url}}></p>
<p>{{diary.comment}}</p>
<div class="time">{{diary.date.strftime("%Y/%m/%d %H:%M")}}</div>
</div>
</label>
<input type="submit" id="{{diary.id}}" class="update">
</form>
{% endfor %}
</div>
{% endblock %}
<form>を追加します。
<input type="submit" id="{{diary.id}}" class="update">
このボタンは非表示にして、テーブル全体をラベルにしてボタン化します。
server.py(追加部分)
#「/diary/<ID>」へアクセスがあった場合「blog/diary.html」を返す
@app.route('/diary/<ID>')
def diary(ID): #参照
name = session["user_name"]
diary = WikiContent.query.get(ID)
return render_template('blog/diary.html',name=name,diary=diary)
4.編集画面で更新処理
「diary.html」を編集します。更新ボタンを作成します。
※「diary.html」のコードは最後に載せます。
server.py(追加部分)
#「/diaryupdate」へアクセスがあった場合「/blog/index」ページへ移動
@app.route('/diaryupdate', methods=['post'])
def diaryupdate(): #更新
content = WikiContent.query.get(request.form.get("ID"))
# 画像ファイルを更新
if request.files['image']:
f = request.files['image']
filepath = 'static/images/' + secure_filename(f.filename)
f.save(filepath)
filepath = '/' + filepath
content.image_url = filepath
# データベースを更新
content.title = request.form["title"]
content.comment = request.form["comment"]
db_session.commit()
return redirect(url_for("blog.index"))
5.画面に削除ボタン、ゲストは更新・削除できないように
「diary.html」を編集します。削除ボタンを作成します。
diary.html
{% extends "blog/base.html" %}
{% block body %}
<link rel="stylesheet" type="text/css" href="/static/diary.css">
<script src="/static/diary.js"></script>
<title>更新画面</title>
<form action="/blog/diaryupdate" method="post" enctype="multipart/form-data">
<div id="main">
<input id="ID" type="text" name="ID" value="{{diary.id}}">
<label class="label">タイトル</label>
<input id="text1" type="text" name="title" value="{{diary.title}}">
<br><br>
<div id=image_box>
{% if name != "" %}
<label class="btn1" for="file-sample">ファイルを選択</label>
{% else %}
<label class="btn0">ファイルを選択</label>
{% endif %}
<input id="file-sample" type="file" name="image" value="{{diary.image_url}}">
<img id="file-preview" src="{{diary.image_url}}">
</div>
<label class="label">コメント</label>
<textarea rows="12" cols="100" name="comment">{{diary.comment}}</textarea>
<div class="time">{{diary.date.strftime("%Y/%m/%d %H:%M")}}</div>
{% if name != "" %}
<input class="btn1" type="submit" value=" 更 新 ">
<button class="btn1" id="btn2" type="button" onclick="location.href='/blog/delete/{{diary.id}}'"> 削 除 </button>
{% endif %}
</div>
</form>
{% endblock %}
<button type="button" onclick="location.href='/blog/delete/{{diary.id}}'"> 削 除 </button>
(<form>を使わずに)ボタンでページ移動する方法です。※更新ボタンで<form>を利用しているため、削除ボタンを<input>で作ってしまうと「1つの<form>に2つの<input>があるので」削除ボタンを押しても更新処理をしてしまいました。
{% if name != "" %}
ゲストでログイン時(ユーザー名がない時)の処理です。「更新」「削除」「ファイルを選択」のボタンを非表示にしています。※「ファイルを選択」ボタンは消してしまうとレイアウトがおかしくなったので不細工ですが透明に配置しています。
diary.css
header{
position: fixed; /*絶対位置で固定*/
top: 0px; /*上に配置*/
left: 0px; /*左の隙間をなくす*/
padding:0px 30px; /*範囲内の余白(上下、左右)*/
width: 100%; /*幅*/
height: 60px; /*高さ*/
background-image: url("back_image.jpg");
/*画像を背景に指定*/
}
#main{
margin: auto; /*中央寄せ*/
padding:60px 20px; /*範囲内の余白(上下、左右)*/
width: 90%; /*幅*/
max-width: 800px; /*最大の幅*/
}
#ID{
display: none; /*非表示にする*/
}
.label{
font-size: 24px; /*文字のサイズ*/
}
#text1{
padding: 8px; /*範囲内の余白*/
width: 100%; /*幅*/
font-size: 18px; /*文字の大きさ*/
box-sizing: border-box; /*横幅の解釈をpadding, borderまでとする*/
}
textarea{
padding: 10px; /*範囲内の余白*/
width: calc(100% - 300px); /*幅*/
font-size: 17px; /*文字の大きさ*/
box-sizing: border-box; /*横幅の解釈をpadding, borderまでとする*/
resize: vertical;
}
#image_box{
float: right; /*右に寄せる*/
width: 300px;
}
#file-sample {
display: none;
}
.btn1{
display: inline-block;
padding: 5px 30px; /*余白*/
color:#fff; /*文字の色*/
font-size: 16px; /*文字のサイズ*/
text-align: center; /*文字の位置*/
background:#1B73BA; /*背景色*/
border-radius: 10px; /*角の半径*/
cursor: pointer; /*カーソル(指のかたち)*/
}
.btn1:hover{
opacity: 0.8; /*透明度*/
}
.btn0{
display: inline-block;
padding: 5px 30px; /*余白*/
font-size: 16px; /*文字のサイズ*/
opacity: 0; /*透明*/
}
#btn2{
background:red; /*背景色*/
float: right; /*右に寄せる*/
}
img{
margin: 2px 0;
width: 100%;
}
@media screen and (max-width: 600px) {
#image_box{
width: 100%; /*スマホなどの時の画像サイズ*/
}
textarea{
width: 100%;
}
}
.time{
clear: both; /*「float」を解除*/
}
footer{
position: fixed; /*絶対位置で固定*/
bottom: 0px; /*下に配置*/
left: 0px; /*左の隙間をなくす*/
width: 100%; /*幅*/
height: 60px; /*高さ*/
font-weight: bold; /*文字を太字にする*/
text-align: center; /*中央に表示する*/
background-image: url("back_image.jpg");
/*画像を背景に指定*/
}
ul.footer-menu li {
display: inline; /*一行で表示する*/
}
#copy{
position: absolute; /*絶対位置(rightのために必要)*/
right: 30px; /*右に配置*/
}
diary.js(edit.jsと同じ。なので「edit.js」を読み込めば、これは必要ないかも)
window.onload = function () {
document.getElementById('file-sample').addEventListener('change', function (e) {
var file = e.target.files[0];
var blobUrl = window.URL.createObjectURL(file);
var img = document.getElementById('file-preview');
img.src = blobUrl;
});
}
server.py(追加部分)
#「/delete/<ID>」へアクセスがあった場合「/blog/index」ページへ移動
@app.route('/delete/<ID>')
def delete(ID): #削除
content = WikiContent.query.get(ID)
db_session.delete(content)
db_session.commit()
return redirect(url_for("blog.index"))
まとめ
http://startpython.pythonanywhere.com/
こちらから実際に操作できますのでお試しください。(ユーザー名・パスワードは普段お使いのもの以外にしてください。セキュリティーは保証できません)
ユーザー名とパスワードを何も入れずにログインするとゲストでログインできます。
これで完成にしたかったのですが、(ゲストログインの時は編集・削除はできないのですが)別のユーザーが登録したコメントや画像も編集・削除ができてしまいます。
次回はそのあたりをなんとかしたいのと、検索機能を付けたいと思います。
↓よかったらポチッとしていってください。