Flaskチュートリアル-Flaskでブログを作ってみる②

image Flaskチュートリアル

前回の記事

Flaskチュートリアル-Flaskでブログを作ってみる①
はじめにこのチュートリアルではPythonの軽量WebフレームワークであるFlaskを使ってブログを制作していきます。このチュートリアルで使用している環境OS:macOS Catalinaエディタ:AtomPython3.7SQlit...

データベースを構築する

はじめに投稿やユーザー情報を保存しておくためのデータベースを構築していきます。

今回制作するブログは簡易的なものなので次のような構成でテーブルを作っていきます。

「BlogContent」テーブル
・id
・title
・body
・date

「Users」テーブル
・id
・name
・password

面倒なので今回はパスワードを平文で保存します。(実際のプロジェクトではやらないでください)

ファイル構成

前回の記事までに作ったフォルダの中に下のように新しくフォルダ,ファイルを設置してください。

myblog(前回までのフォルダ)/
 |-models/
    |-models.py
    |-database.py
    |- __init__.py

設置したファイルについて

models.py

データベースのモデルを書いておくためのファイルです。

database.py

データベース(SQLite)と接続するためのファイルです。

__init__.py

おまじないみたいなものです。
無くても動くらしいですが、一応置いときます。
“_”は2つつけてください。

プログラム

database.py

import sqlalchemy
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
import os

engine = sqlalchemy.create_engine("sqlite:///"+os.path.join(os.path.abspath(os.path.dirname(__file__)),"myblog.db"))

Base = declarative_base()

from models.models import BlogContent,Users

Base.metadata.create_all(bind=engine)

db_session = sessionmaker(bind=engine)()

engineに指定してある”myblog.db”がsqliteのファイル名です。

sqlite:///からmyblog.dbまでのプログラムではファイルパスを取得しています。

models.py

from sqlalchemy import Column, Integer, String, Text, DateTime
from datetime import datetime
from models.database import Base

class BlogContent(Base):
    __tablename__ = "blogcontent"
    id = Column(Integer,primary_key=True)
    title = Column(String(128),unique=True)
    body = Column(Text)
    date = Column(DateTime,default=datetime.now())
    def __init__(self, title=None,body=None,date=None):
        self.title = title
        self.body = body
        self.date = date
    def __repr__(self):
        return "<BlogContent(title='%s',body='%s',date='%s')>" % (self.title, self.body,self.date)

class Users(Base):
    __tablename__ = "users"
    id = Column(Integer,primary_key=True)
    name = Column(Text,unique=True)
    password = Column(Text)
    def __init__(self,name=None,password=None):
        self.name = name
        self.password = password
    def __repr__(self):
        return "<Users(name='%s', password='%s')>" % (self.name, self.password)

primary_keyは必ず設定する必要がありデータを追加するとき勝手に割り当てられます。

unique=Trueとなっているところへは重複したデータを入れることができなくなります。

データベースを生成

モデルの設定・データベースの設定ができたらデータベースのファイルを生成します。

ターミナルで「myblog」フォルダに移動してpython3を起動して次のように入力してください。

import models.database

database.pyをインポートすると「myblog.db」というデータベースファイルが生成されます。

生成されたデータベースファイルをDB Browser for SQLite等のツールで読み込むとモデルで設定したようにテーブルが作られていることが分かります。

image

テンプレートファイルを作成

DBの構築ができたので次にテンプレートファイルを作成します。

app内にあるtemplatesフォルダに次のファイルを作成してください。

main_layout.html
notfound.html
post.html
login.html
add_post.html
edit_post.html

メインテンプレートの作成

Flaskのテンプレートエンジンには複数のページで共通する部分がある場合(ヘッダー,フッダー等)共通する部分だけを別のファイルに書いて使うとき呼び出せる機能があります。

今回はその共通する部分をmain_layout.htmlに書いて使います。

main_layout.html

<!DOCTYPE html>
<html lang="ja" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title>{{blog_name}}</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
    <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
    <link rel="stylesheet" href="/static/style.css">
  </head>
  <body>
    <header>
      <nav class="navbar navbar-light" style="background-color:#b3e5fc;">
        <a href="{{ url_for("index") }}" class="navbar-brand">{{blog_name}}</a>
        {% if status == "n" %}
        <a href="{{ url_for("login") }}" class="btn btn-primary">ログイン</a>
     {% else %}
        <div class="btn-group">
        <a href="{{ url_for("add") }}" class="btn btn-danger">記事を投稿</a>
        <a href="{{ url_for("logout") }}" class="btn btn-primary">ログアウト</a>
        </div>
        {% endif %}

      </nav>
    </header>
    <main>
      {% block content %}

      {% endblock %}
    </main>
  </body>
</html>

他のテンプレートファイルからメインテンプレートを読み込むと

{% block content %}

{% endblock %}

の中に入ります。

一からデザインするのは面倒なのでBootStrapを使用します。

ブログ名は後からapp.pyにblog_nameを指定することで編集できるようにしています。

ログインしているユーザーとそうでないユーザーで表示を分けるためテンプレート内でif文を使用しています。

このif文では後でapp.pyで設定するstatusの値が”n”だったらログインボタンをそれ以外だったら記事投稿ボタンとログアウトボタンを表示するようにしています。

その他のテンプレートファイルの作成

post.html

{% extends "main_layout.html" %}
{% block content%}

{% if status == "login" %}
  <a href="{{url_for("delete",title=post.title)}}">削除</a>
  <br>
  <a href="{{url_for("edit",title=post.title)}}">編集</a>
  <br>
{% endif %}
<p>投稿:{{post.date}}</p>
  <div>
    <h1>{{post.title}}</h1>
    <p>{{ post.body | safe}}
</p>
  </div>
{% endblock %}

投稿を表示するためのテンプレートです。

ログインしているユーザーに対しては編集・削除ボタンを表示します。

先程制作したメインテンプレートは下のように読み込めます。

{% extends "main_layout.html" %}
{% block content%}
{% endblock %}

login.html

{% extends "main_layout.html" %}
{% block content%}
      {{message}}
      <div>
        <form method="post" action="/login">
          <div class="form-group">
            <label for="id">Name</label>
            <input type="text" class="form-control" id="id" name="name">
          </div>
          <div class="form-group">
            <label for="pass">Password</label>
            <input type="password" class="form-control" id="pass" name="password">
          </div>
          <button type="submit" class="btn btn-primary">Submit</button>
        </form>
      </div>

{% endblock %}

ログインページです。

詳細はプログラムを作成する際に説明します。

index.html

{% extends "main_layout.html" %}
{% block content%}

  {% for posts in posts %}
    <a href="/post/{{posts.title}}">
      <div class="card">
        <div class="card-body">
          <h5 class="card-title">{{posts.title}}</h5>
        </div>
      </div>
    </a>
    <br>
  {% endfor %}

{% endblock %}

トップページなので記事を取得するコードを書いています。

普段のPythonに近い感じでfor文やif文をテンプレート上に書くことができます。

add_post.html

{% extends "main_layout.html" %}
{% block content%}
    <main>
      <h2 style="color:red;">{{message}}</h2>
      <div>
        <form method="post" action="/add">
          <div class="form-group">
            <label for="title">title</label>
            <input type="title" class="form-control" value="{{title}}" id="id" name="title">
          </div>
          <div class="form-group">
            <label for="body">body</label>
            <textarea id="body" class="form-control" name="body">{{body}}</textarea>
          </div>
          <button type="submit" class="btn btn-primary">Submit</button>
        </form>
      </div>
{% endblock %}

投稿ページのテンプレートです。

edit_post.html

{% extends "main_layout.html" %}
{% block content%}
    <main>
      <h2 style="color:red;">{{message}}</h2>
      <div>
        <form method="post" action="/edit/{{post.title}}">
          <div class="form-group">
            <label for="title">title</label>
            <input type="title" class="form-control" value="{{post.title}}{{title}}" id="id" name="title">
          </div>
          <div class="form-group">
            <label for="body">body</label>
            <textarea id="body" class="form-control" name="body">{{post.body}}{{body}}</textarea>
          </div>
          <button type="submit" class="btn btn-primary">Submit</button>
        </form>
      </div>
{% endblock %}

記事の編集ページのテンプレートです。

notfound.html

{% extends "main_layout.html" %}
{% block content%}
<h2>ページが見つかりません</h2>
{% endblock %}

404エラー用のテンプレートです。

次回の記事からはapp.pyにブログのプログラムを書いています。

次回の記事

Flaskチュートリアル-Flaskでブログを作ってみる③
前回の記事トップページのプログラムを作成トップページに記事一覧を表示するためのプログラムを書きます。でapp.pyにFlask,render_templateだけ読み込ませているので新しく次のように書き足してください。fro...
ビスケットを送る

コメント

タイトルとURLをコピーしました