“Flask学习笔记” 系列是博主学习《Flask Web开发实战》的学习记录。原书的作者为了读者方便,使用了 faker
生成了模拟数据,代码跑起来更加简单。我做了一些修改,连接了本地的数据库,在这里记录一下学习过程。
看一下目录结构,采用了 MVC 框架。
把相关配置信息放到一个单独的文件中。
import os
SECRET_KEY = os.getenv('SECRET_KEY', '123456789') #可以任意给一个字符串
SQLALCHEMY_TRACK_MODIFICATIONS = False
SQLALCHEMY_DATABASE_URI = os.getenv('DATABASE_URI', 'mysql+pymysql://root:密码@127.0.0.1:3306/数据库名')
创建示例程序、初始化拓展等操作在此处完成。
flask_bootstrap
、flask_moment
、flask_sqlalchemy
是 flask 的拓展组件,我们后面会用到相关功能。app.config.from_pyfile
导入配置文件。from flask import Flask
from flask_bootstrap import Bootstrap
from flask_moment import Moment
from flask_sqlalchemy import SQLAlchemy
app = Flask('sayhello')
app.config.from_pyfile('settings.py')
app.jinja_env.trim_blocks = True # 删除Jinjia2语句后的第一个空行
app.jinja_env.lstrip_blocks = True # 删除Jinjia2语句所在行之前的空格和制表符
db = SQLAlchemy(app)
bootstrap = Bootstrap(app)
moment = Moment(app)
from sayhello import views, errors
我们需要确定使用哪些表来存储数据。本次项目较为简单,一个表即可。
id
:主键name
:留言者姓名body
:留言内容timestamp
:时间戳from datetime import datetime
from sayhello import db
class Message(db.Model):
__tablename__ = 'sayhello' # 用到的表为sayhello
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(20))
body = db.Column(db.String(200))
timestamp = db.Column(db.DateTime, default=datetime.utcnow, index=True)
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, TextAreaField
from wtforms.validators import DataRequired, Length
class HelloForm(FlaskForm):
name = StringField('Name', validators=[DataRequired(), Length(1, 20)])
body = TextAreaField('Message', validators=[DataRequired(), Length(1, 200)])
submit = SubmitField()
此处的视图函数有两个作用:
index.html
。flash()
函数显示一条提示,然后重定向到 index
视图,渲染页面。from flask import flash, render_template, url_for, redirect
from sayhello import app, db
from sayhello.forms import HelloForm
from sayhello.models import Message
@app.route('/', methods=['GET', 'POST'])
def index():
form = HelloForm()
if form.validate_on_submit():
name = form.name.data
body = form.body.data
message = Message(body=body, name=name) # 实例化模型类,创建记录
db.session.add(message) # 添加记录到数据库回会话
db.session.commit() # 提交会话
flash('Your message have been sent to the world!')
return redirect(url_for('index')) # 重定向到index视图
messages = Message.query.order_by(Message.timestamp.desc()).all()
return render_template('index.html', form=form, messages=messages)
错误处理函数也属于视图函数,只是单独分离出来作为一个文件。
from flask import render_template
from sayhello import app
@app.errorhandler(404)
def page_not_found(e):
return render_template('errors/404.html'), 404
@app.errorhandler(500)
def internal_server_error(e):
return render_template('errors/500.html'), 500
DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>{% block title %}Say Hello!{% endblock %}title>
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap.min.css') }}" type="text/css">
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}" type="text/css">
head>
<body>
<main class="container">
<header>
<h1 class="text-center display-4">
<a href="{{ url_for('index') }}" class="text-success"><strong>Say Hellostrong>a>
<small class="text-muted sub-title">to the worldsmall>
h1>
header>
{% for message in get_flashed_messages() %}
<div class="alert alert-info">
<button type="button" class="close" data-dismiss="alert">×button>
{{ message }}
div>
{% endfor %}
{% block content %}{% endblock %}
<footer class="text-center">
{% block footer %}
<small> © 2018 <a href="http://greyli.com" title="Written by Grey Li">Grey Lia> /
<a href="https://github.com/greyli/sayhello" title="Fork me on GitHub">GitHuba> /
<a href="http://helloflask.com" title="A HelloFlask project">HelloFlaska>
small>
<p><a id="bottom" href="#" title="Go Top">↑a>p>
{% endblock %}
footer>
main>
<script type="text/javascript" src="{{ url_for('static', filename='js/jquery-3.2.1.slim.min.js') }}">script>
<script type="text/javascript" src="{{ url_for('static', filename='js/popper.min.js') }}">script>
<script type="text/javascript" src="{{ url_for('static', filename='js/bootstrap.min.js') }}">script>
<script type="text/javascript" src="{{ url_for('static', filename='js/script.js') }}">script>
{{ moment.include_moment(local_js=url_for('static', filename='js/moment-with-locales.min.js')) }}
body>
html>
在主页模板 index.html
中,使用 render_form
宏渲染表单,然后迭代传入的 message
变量,渲染消息列表。
{% extends 'base.html' %}
{% from 'bootstrap/form.html' import render_form %}
{% block content %}
<div class="hello-form">
{{ render_form(form, action=request.full_path) }}
div>
<h5>{{ messages|length }} messages
<small class="float-right">
<a href="#bottom" title="Go Bottom">↓a>
small>
h5>
<div class="list-group">
{% for message in messages %}
<a class="list-group-item list-group-item-action flex-column">
<div class="d-flex w-100 justify-content-between">
<h5 class="mb-1 text-success">{{ message.name }}
<small class="text-muted"> #{{ loop.revindex }}small>
h5>
<small data-toggle="tooltip" data-placement="top"
data-timestamp="{{ message.timestamp.strftime('%Y-%m-%dT%H:%M:%SZ') }}"
data-delay="500">
{{ moment(message.timestamp).fromNow(refresh=True) }}
small>
div>
<p class="mb-1">{{ message.body }}p>
a>
{% endfor %}
div>
{% endblock %}
{% extends "base.html" %}
{% block title %}404 Error{% endblock %}
{% block content %}
<p class="text-center">Page Not Foundp>
{% endblock %}
{% block footer %}
<a href="{{ url_for('index') }}">← Go Backa>
{% endblock %}
{% extends "base.html" %}
{% block title %}500 Error{% endblock %}
{% block content %}
<p class="text-center">Internal Server Errorp>
{% endblock %}
{% block footer %}
<a href="{{ url_for('index') }}">← Go Backa>
{% endblock %}
from sayhello import app
if __name__ == '__main__':
app.run(debug=True)