はじめての Flask #3 ~htmlをrenderしてみよう~
今回は、実際にサイト内での処理を基にデータベースを作ってみたいと思います!
サイトの構築になくてはならないデータベースをいじれるようになりましょう!
PythonからDBを作ってみよう!
では初めに、DBについて簡単に説明しますね (そんなの知ってるよ!という方は飛ばしていただいて結構です。)
DBとは
検索や蓄積が容易にできるよう整理された情報の集まり。 通常はコンピュータによって実現されたものを指すが、紙の住所録などをデータベースと呼ぶ場合もある。コンピュータを使用したデータベース・システムでは、データベース管理用のソフトウェアであるデータベース管理システムを使用する場合も多い。
ありがとうwikipediaさん!ということで、データのまとまりを使いやすくまとめたものです。
データベースというファイルの中にいくつかテーブルという枠を作ります。そのテーブルには、決まった形式のデータしか入れられません。
例えば、< users >というテーブルを作って、そこにはユーザに関する情報のみを入れる。といった感じですね。テーブルの中にはいくつもデータを入れることができます。
百聞は一見に如かずということで、実際に触り始めてみましょう!
まずコンソール画面で
$ pip install sqlalchemy
と入力してください。
Python用のSQLAlchemyというライブラリをインストールしました。
データベースを実際に操作するにはSQLという言語を用いるのですが、それをPython内からやってくれる、というライブラリになっています。
では、ユーザ情報の入るデータベースを作って、実際に一つ入れてみましょう。
from sqlalchemy import create_engine, Column, String, Integer
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
engine = create_engine('sqlite:///user.db') # user.db というデータベースを使うという宣言です
Base = declarative_base() # データベースのテーブルの親です
class User(Base): # PythonではUserというクラスのインスタンスとしてデータを扱います
__tablename__ = 'users' # テーブル名は users です
id = Column(Integer, primary_key=True, unique=True) # 整数型のid をprimary_key として、被らないようにします
email = Column(String) # 文字列の emailというデータを作ります
name = Column(String) # 文字列の nameというデータを使います
def __repr__(self):
return "User<{}, {}, {}>".format(self.id, self.email, self.name)
Base.metadata.create_all(engine) # 実際にデータベースを構築します
SessionMaker = sessionmaker(bind=engine) # Pythonとデータベースの経路です
session = SessionMaker() # 経路を実際に作成しました
user1 = User(email="thisisme@test.com", name="Python") # emailと nameを決めたUserのインスタンスを作りましょう(idは自動で1から順に振られます)
session.add(user1) # user1 をデータベースに入力するための準備をします
session.commit() # 実際にデータベースにデータを入れます。
これで、user.db というファイル内の users というテーブルの中に [id=1, email="thisisme@test,com", name="Python"] という値を持つデータが入りました
では、今度はデータを取得してみましょう!
id | name | |
---|---|---|
1 | thisisme@test.com | Python |
2 | iloveruby@test.com | Ruby |
3 | cobolisnotold@test.com | Cobol |
4 | cplusminus@test.com | C |
5 | kotlinsocute@test.com | Kotlin |
6 | ilikeguido@test.com | Python |
というデータがusersの中にあったとします。
いろんなユーザのデータを取り出してみましょう!!
データの操作
primary_keyで検索
例えば、idが2番目の人を取り出したいなら、
user = session.query(User).get(2)
としましょう。
session.query(table名)
で、テーブルを指定して検索がかけられます。
primary_keyがidになっているので、**get()**の引数がidに合致するデータが取り出されます。
ただ、1番目の人を取り出すなら、
session.query(User).get(1) == session.query(User).first() # True
user = session.query(User).first()
のほうが良いでしょう。こっちの方が早いですしね。
値の大小で検索
idが3以上の人を探したいときには
user = session.query(User).filter(User.id >= 3).all()
print(user)
# [User<3, cobolisnotold@test.com, Cobol>, User<4, cplusminus@test.com, C>, User<5, kotlinissocute@test.com, Kotlin>, User<6, ilikeguido@test.com, Python>]
session.query(User).filter(cond)
とすることで、condがTrueになるものが抽出されます。
つまり
一致で検索
user = session.query(User).filter(User.name == "Python").all()
print(user)
# [User<1, thisisme@test.com, Python>, User<6, ilikeguido@test.com, Python>]
こういうことです。
複数条件
user = session.query(User).filter(User.name == "Python", User.email == "thisisme@test.com").all()
print(user)
# [User<1, thisisme@test.com, Python>]
複数の条件を持たけることもでき、そのすべてが当てはまるもののみ返します。
いったんはこの程度で十分だと思います。
変更を加える
user = session.query(User).filter(User.name == "Python", User.email == "thisisme@test.com").all()
user.name = "Pythonista"
print(user)
# [User<1, thisisme@test.com, Pythonista>]
session.commit()
こうすることで、ユーザのnameがPythonistaに書き換わりました。インスタンスの様に扱えるのですね。
session.commit()
を忘れないでくださいね!
データを削除する
user = session.query(User).filter(User.name == "Python", User.email == "thisisme@test.com").all()
session.delete(user)
session.commit()
とすることでuserをデータベースから削除できます。
では、いままでの知識+αで簡単な掲示板を作ってみましょう!
実際にアプリを作ってみよう!
とにかくシンプルな実装で、簡単に作ってみましょう!
では、
root/
|__main.py
|__templates
|__mainpage.html
という風にファイルを構築して、アプリを作り始めましょう。
今回は掲示板ということなので、まずはユーザーと、投稿のデータベースを作りましょう!
from sqlalchemy import create_engine, Column, String, Integer, DATETIME
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, scoped_session
engine = create_engine('sqlite:///app.db')
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
name = Column(String, primary_key=True, unique=True)
passw = Column(String)
def __repr__(self):
return "User<{}, {}, {}>".format(self.name)
class Content(Base):
__tablename__ = 'contents'
id = Column(Integer, primary_key=True)
name = Column(String)
content = Column(String)
timestamp = Column(DATETIME)
def __repr__(self):
return "Content<{}, {}, {}, {}>".format(self.id, self.name, self.content, self.timestamp)
Base.metadata.create_all(engine)
SessionMaker = sessionmaker(bind=engine)
session = scoped_session(SessionMaker)
nameとpasswという値を持つUserと、id, name, content, timestampという値を持つContentというクラスを作成しました。
投稿の際に自動でアカウントを作り、名前がかぶっていればそのUserとpasswでの照合を行います。両方同じであればそのアカウントでContent(投稿)を作成し、合致しなければ何もしない。
という一連の流れを作ってみましょう。
import hashlib
import datetime
from flask import *
from sqlalchemy import create_engine, Column, String, Integer, DATETIME
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, scoped_session
app = Flask(__name__)
engine = create_engine('sqlite:///app.db')
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
name = Column(String, primary_key=True, unique=True)
passw = Column(String)
def __repr__(self):
return "User<{}, {}, {}>".format(self.name)
class Content(Base):
__tablename__ = 'contents'
id = Column(Integer, primary_key=True)
name = Column(String)
content = Column(String)
timestamp = Column(DATETIME)
def __repr__(self):
return "Content<{}, {}, {}, {}>".format(self.id, self.name, self.content, self.timestamp)
Base.metadata.create_all(engine)
SessionMaker = sessionmaker(bind=engine)
session = scoped_session(SessionMaker)
@app.route("/", methods=["GET", "POST"])
def main_page():
cont = session.query(Content).all()
if request.method == "GET":
return render_template("mainpage.html", cont=cont)
user = session.query(User).get(request.form["name"].strip())
if user:
if user.passw != str(hashlib.sha256(request.form["pass"].strip().encode("utf-8")).digest()):
return render_template("mainpage.html", cont=cont)
else:
user = User(name=request.form["name"], passw=str(hashlib.sha256(request.form["pass"].strip().encode("utf-8")).digest()))
session.add(user)
mess = Content(name=request.form["name"], content=request.form["content"], timestamp=datetime.datetime.now())
session.add(mess)
session.commit()
cont = session.query(Content).all()
return render_template("mainpage.html", cont=cont)
if __name__ == "__main__":
app.run(debug=True, host='0.0.0.0', port=8888, threaded=True)
データの受け取りに関しては はじめての Flask #2 ~POSTを受け取ろう~,
パスワードの暗号化に関しては 国会でも話に上がった "HASH" をPythonで -- hash()じゃないよ! --
のとおりです。
contというデータで投稿のリストをjinja2に渡しています。
そして、htmlの方ですが
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<table border="1">
<tr>
<th>ID</th>
<th>時間</th>
<th>名前</th>
<th>内容</th>
</tr>
{% for co in cont %}
<tr>
<th>{{ co.id }}</th>
<th>{{ co.timestamp.strftime('%Y-%m-%d %H:%M:%S') }}</th>
<th>{{ co.name }}</th>
<th>{{ co.content }}</th>
</tr>
{% endfor %}
</table>
<form action="/" method="POST">
<table>
<tr>
<th>名前</th>
<th>パスワード</th>
<th>内容</th>
</tr>
<tr>
<th><input type="text" value="" placeholder="名前を入力してください" name="name"></th>
<th><input type="text" value="" placeholder="パスワードを入力してください" name="pass"></th>
<th><input type="text" value="" placeholder="内容を入力してください。" name="content"></th>
</tr>
</table>
<input type="submit" value="sub">
</form>
</body>
</html>
このような形でよいと思います。
さて、実際に動かしてみてください。
こんな感じになりましたかね?(最後の投稿と、その前の投稿の間の空白の40分間でOVERLORD三期観てました)
おわり
どうですか?きちんと動いたと思います。
今回も楽しんでいただけたら幸いです。
次回: はじめての Flask #5 ~JSONを返すWebAPIを書こう~
ではみなさん、たのしいPython Lifeを!!