作为一个编程入门新手,Flask是我接触到的第一个Web框架。想要深入学习,就从《FlaskWeb开发:基于Python的Web应用开发实战》这本书入手,本书由于是翻译过来的中文版,理解起来不是很顺畅。但是对着代码理解也是能应对的,学到  第七章:大型程序结构  这章节的时候,发现难度有所提升,网上能参考的完整实例没有,于是根据自己的理解记下来。

程序结构图:

README

(1)本程序是基于Flask微型Web框架开发,使用Jinja2模版引擎
(2)页面展示了一个文本框和一个按钮,输入文本框点击按钮提交,文本框为空无法提交(输入文本框的数据为一个模拟用户);
(3)当在文本框中输入新用户提交,欢迎词和文本框中输入老用户提交不一致;
(4)文本框输入新用户提交后,将新用户保存至SQLite数据库,并使用异步发送邮件至管理员邮箱;
(5)页面刷新,浏览器不会再次提示:是否提交 项目结构 flasky # 程序根目录
├── app # 核心模块目录
│   ├── email.py # 邮件发送模版
│   ├── __init__.py
│   ├── main # 蓝图模块目录
│   │   ├── errors.py # 错误处理模块
│   │   ├── forms.py # 页面表单模块
│   │   ├── __init__.py
│   │   └── views.py # 正常处理模块
│   ├── models.py # 对象关系映射模块
│   ├── static # 页面静态资源目录
│   │   └── favicon.ico # 页面收藏夹图标
│   └── templates # 默认存放页面模版目录
│   ├── 404.html
│   ├── base.html
│   ├── index.html
│   ├── mail # 邮件模块目录
│   │   ├── new_user.html
│   │   └── new_user.txt
│   └── user.html
├── config.py # 程序配置文件
├── data-dev.sqlite # 程序数据库文件
├── manage.py # 程序管理启动文件
├── migrations # 数据库迁移目录
│   ├── alembic.ini
│   ├── env.py
│   ├── README
│   ├── script.py.mako
│   └── versions
├── requirements.txt # 所有依赖包文件
└── tests # 测试文件目录
├── __init__.py
└── test_basics.py

README

程序代码总汇

"/"

# -*- coding: utf-8 -*-
# Author: hkey
import os basedir = os.path.abspath(os.path.dirname(__file__)) class Config(object): # 所有配置类的父类,通用的配置写在这里
SECRET_KEY = os.environ.get('SECRET_KEY') or 'hard to guess string'
SQLALCHEMY_COMMIT_ON_TEARDOWN = True
SQLALCHEMY_TRACK_MODIFICATIONS = True
FLASKY_MAIL_SUBJECT_PREFIX = '[Flasky]'
FLASKY_MAIL_SENDER = 'Flasky Admin <xxx@126.com>'
FLASKY_ADMIN = 'xxx@qq.com' @staticmethod
def init_app(app): # 静态方法作为配置的统一接口,暂时为空
pass class DevelopmentConfig(Config): # 开发环境配置类
DEBUG = True
MAIL_SERVER = 'smtp.126.com'
MAIL_PORT = 465
MAIL_USE_SSL = True
MAIL_USERNAME = 'xxx@126.com'
MAIL_PASSWORD = 'xxxxxx'
SQLALCHEMY_DATABASE_URI = \
'sqlite:///' + os.path.join(basedir, 'data-dev.sqlite') class TestingConfig(Config): # 测试环境配置类
TESTING = True
SQLALCHEMY_DATABASE_URI = \
'sqlite:///' + os.path.join(basedir, 'data-test.sqlite') class ProductionConfig(Config): # 生产环境配置类
SQLALCHEMY_DATABASE_URI = \
'sqlite:///' + os.path.join(basedir, 'data.sqlite') config = { # config字典注册了不同的配置,默认配置为开发环境,本例使用开发环境
'development': DevelopmentConfig,
'testing': TestingConfig,
'production': ProductionConfig,
'default': DevelopmentConfig
}

config.py

# -*- coding: utf-8 -*-
# Author: hkey
import os
from app import create_app, db
from app.models import User, Role
from flask_script import Manager, Shell
from flask_migrate import Migrate, MigrateCommand app = create_app(os.getenv('FLASK_CONFIG') or 'default')
manager = Manager(app)
migrate = Migrate(app, db) def make_shell_context():
return dict(app=app, db=db, User=User, Role=Role) manager.add_command('shell', Shell(make_context=make_shell_context))
manager.add_command('db', MigrateCommand) @manager.command
def test():
import unittest
tests = unittest.TestLoader().discover('tests')
unittest.TextTestRunner(verbosity=2).run(tests) if __name__ == '__main__':
manager.run()

manage.py

"/app"

# -*- coding: utf-8 -*-
# Author: hkey
from flask import Flask, render_template
from flask_bootstrap import Bootstrap
from flask_sqlalchemy import SQLAlchemy
from flask_mail import Mail
from config import config # 由于尚未初始化所需的程序实例,所以没有初始化扩展,创建扩展类时没有向构造函数传入参数。
bootstrap = Bootstrap()
mail = Mail()
db = SQLAlchemy() def create_app(config_name):
'''工厂函数'''
app = Flask(__name__)
app.config.from_object(config[config_name])
config[config_name].init_app(app) # 通过config.py统一接口
bootstrap.init_app(app) # 该init_app是bootstrap实例的方法调用,与上面毫无关系
mail.init_app(app) # 同上
db.init_app(app) # 同上 # 附加路由和自定义错误页面,将蓝本注册到工厂函数
from .main import main as main_blueprint
app.register_blueprint(main_blueprint) return app

app/__init__.py

# -*- coding: utf-8 -*-
# Author: hkey
from threading import Thread
from flask import render_template, current_app
from flask_mail import Message
from . import mail def send_async_mail(app, msg):
'''创建邮件发送函数'''
with app.app_context():
mail.send(msg) def send_mail(to, subject, template, **kwargs):
app = current_app._get_current_object()
if app.config['FLASKY_ADMIN']:
msg = Message(app.config['FLASKY_MAIL_SUBJECT_PREFIX'] + subject,
sender=app.config['FLASKY_MAIL_SENDER'], recipients=[to])
msg.body = render_template(template + '.txt', **kwargs)
msg.html = render_template(template + '.html', **kwargs)
thr = Thread(target=send_async_mail, args=(app, msg))
thr.start() # 通过创建子线程实现异步发送邮件
return thr

app/email.py

# -*- coding: utf-8 -*-
# Author: hkey # 对象关系映射类 from . import db class Role(db.Model):
__tablename__ = 'roles'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True, index=True)
users = db.relationship('User', backref='role', lazy='dynamic')
def __repr__(self):
return '<Role %r>' % self.name class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), unique=True, index=True)
role_id = db.Column(db.Integer, db.ForeignKey('roles.id')) def __repr__(self):
return '<User %r>' % self.username

app/models.py

''/app/main"

# -*- coding: utf-8 -*-
# Author: hkey
from flask import Blueprint
# 定义蓝本
main = Blueprint('main', __name__) from . import views, errors

app/main/__init__.py

# -*- coding: utf-8 -*-
# Author: hkey
from flask import render_template
from . import main @main.app_errorhandler(404) # 路由装饰器由蓝本提供,这里要调用 app_errorhandler 而不是 errorhandler
def page_not_found(e):
return render_template('404.html'), 404 @main.app_errorhandler(500)
def internal_server_error(e):
return render_template('500.html'), 500

app/main/errors.py

# -*- coding: utf-8 -*-
# Author: hkey
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import Required class NameForm(FlaskForm):
'''通过 flask-wtf 定义表单类'''
name = StringField('What is your name ?', validators=[Required()]) # 文本框
submit = SubmitField('Submit') # 按钮

app/main/forms.py

# -*- coding: utf-8 -*-
# Author: hkey
from flask import render_template, session, redirect, url_for, current_app
from . import main
from .forms import NameForm
from .. import db
from ..models import User
from ..email import send_mail @main.route('/', methods=['GET', 'POST'])
def index():
form = NameForm()
if form.validate_on_submit():
user = User.query.filter_by(username=form.name.data).first() # 查询数据库是否有该用户
if user is None: # 如果没有该用户,就保存到数据库中
user = User(username=form.name.data)
db.session.add(user)
session['known'] = False # 通过session保存 known为False,通过web渲染需要
if current_app.config['FLASKY_ADMIN']: # 如果配置变量有flasky管理员就发送邮件
# 异步发送邮件
send_mail(current_app.config['FLASKY_ADMIN'], 'New User', 'mail/new_user', user=user)
else:
session['known'] = True
session['name'] = form.name.data
form.name.data = ''
return redirect(url_for('.index')) # 通过redirect避免用户刷新重复提交
return render_template('index.html', form=form, name=session.get('name'),
known=session.get('known', False))

app/main/views.py

"/app/main/templates" 页面

<!DOCTYPE html>
{% extends "bootstrap/base.html" %}
{% block title %}Flasky{% endblock %}
{% block head %}
{{ super() }}
<link rel="shortcut icon" href="{{ url_for('static', filename = 'favicon.ico')}}"
type="image/x-icon">
<link rel="icon" href="{{ url_for('static', filename = 'favicon.ico')}}"
type="image/x-icon">
{% endblock %}
{% block navbar %}
<div class="navbar navbar-inverse" role="navigation">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle"
data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/">Flasky</a>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li><a href="/">Home</a></li>
</ul>
</div>
</div>
</div>
{% endblock %}
{% block content %}
<div class="container">
{% for message in get_flashed_messages() %}
<div class="alert alert-warning">
<button type="button" class="close" data-dismiss="alert">&times;</button>
{{ message }}
</div>
{% endfor %}
{% block page_content %}{% endblock %}
</div>
{% endblock %}

app/templates/base.html

<!DOCTYPE html>
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}Flasky{% endblock %}
{% block page_content %}
<div class="page-header">
<h1>Hello, {% if name %}{{ name }}{% else %}Stranger{% endif %}!</h1>
{% if not known %}
<p>Pleased to meet you!</p>
{% else %}
<p>Happy to see you again!</p>
{% endif %}
</div>
{{ wtf.quick_form(form) }}
{% endblock %}

app/templates/index.html

<!DOCTYPE html>
{% extends "base.html" %}
{% block title %}Flasky - Page Not Found{% endblock %}
{% block page_content %}
<div class="page-header">
<h1>Not Found!</h1>
</div>
{% endblock %}

app/templates/404.html

"/app/main/templates/mail" 邮件模版

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
User <b>{{ user.username }}</b> has joined.
</head>
<body> </body>
</html>

app/templates/mail/new_user.html

User {{ user.username }} has joined.

app/templates/mail/new_user.txt

"/app/main/static/favicon.ico" 静态 icon 图片文件

创建需求文件

程序中必须包含一个 requirements.txt 文件,用于记录所有依赖包及其精确的版本号。如果要在另一台电脑上重新生成虚拟环境,这个文件的重要性就体现出来了,例如部署程序时使用的电脑。

(venv) E:\flasky>pip3 freeze > requirements.txt

创建数据库

(venv) E:\flasky>python manage.py shell
>>> db.create_all()
>>> exit()

生成数据库迁移文件

(venv) E:\flasky>python manage.py db init
Creating directory E:\flasky\migrations ... done
Creating directory E:\flasky\migrations\versions ... done
Generating E:\flasky\migrations\alembic.ini ... done
Generating E:\flasky\migrations\env.py ... done
Generating E:\flasky\migrations\README ... done
Generating E:\flasky\migrations\script.py.mako ... done
Please edit configuration/connection/logging settings in 'E:\\flasky\\migrations\\alembic.ini' before proceeding. (venv) E:\flasky>python manage.py db migrate -m "initial migration"
INFO [alembic.runtime.migration] Context impl SQLiteImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.env] No changes in schema detected. (venv) E:\flasky>python manage.py db upgrade
INFO [alembic.runtime.migration] Context impl SQLiteImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.

运行测试

(venv) E:\flasky>python manage.py test
test_app_exists (test_basics.BasicsTestCase)
确保程序实例存在 ... ok
test_app_is_testing (test_basics.BasicsTestCase)
确保程序在测试中运行 ... ok ----------------------------------------------------------------------
Ran 2 tests in 2.232s OK

启动程序

(venv) E:\flasky>python manage.py runserver
* Serving Flask app "app" (lazy loading)
* Environment: production
WARNING: Do not use the development server in a production environment.
Use a production WSGI server instead.
* Debug mode: on
* Restarting with stat
* Debugger is active!
* Debugger PIN: 138-639-525
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

浏览器输入 http://127.0.0.1:5000

输入用户名并提交:

程序会异步发送邮件,程序控制台会打印发送日志。已收到邮件:

[ Python ] Flask 基于 Web开发 大型程序的结构实例解析的更多相关文章

  1. pycharm+python+Django之web开发环境的搭建(windows)

    转载:https://blog.csdn.net/yjx2323999451/article/details/53200243/ pycharm+python+Django之web开发环境的搭建(wi ...

  2. BEGINNING SHAREPOINT&#174; 2013 DEVELOPMENT 第3章节--SharePoint 2013 开发者工具 SharePoint中基于Web开发

    BEGINNING SHAREPOINT® 2013 DEVELOPMENT 第3章节--SharePoint 2013 开发者工具 SharePoint中基于Web开发         之前提到过, ...

  3. 使用eclipse搭建第一个python+Django的web开发实例

    python+Django的web开发实例   一.创建一个项目如果这是你第一次使用Django,那么你必须进行一些初始设置.也就是通过自动生成代码来建立一个Django项目--一个Django项目的 ...

  4. python——flask常见接口开发(简单案例)

    python——flask常见接口开发(简单案例)原创 大蛇王 发布于2019-01-24 11:34:06 阅读数 5208 收藏展开 版本:python3.5+ 模块:flask 目标:开发一个只 ...

  5. Flask从入门到精通之大型程序的结构一

    尽管在单一脚本中编写小型Web 程序很方便,但这种方法并不能广泛使用.程序变复杂后,使用单个大型源码文件会导致很多问题.不同于大多数其他的Web 框架,Flask 并不强制要求大型项目使用特定的组织方 ...

  6. 【Python】【Web开发】

    # [[Web开发]] ''' 最早的软件都是运行在大型机上的,软件使用者通过“哑终端”登陆到大型机上去运行软件.后来随着PC机的兴起,软件开始主要运行在桌面上,而数据库这样的软件运行在服务器端,这种 ...

  7. Python flask模块接口开发学习总结

    引言 Flask 是一个简单且十分强大的Python web 框架.它被称为微框架,“微”并不是意味着把整个Web应用放入到一个Python文件,微框架中的“微”是指Flask旨在保持代码简洁且易于扩 ...

  8. python +Django 搭建web开发环境初步,显示当前时间

    1.python 的安装 网上很多关于django跟python 开发的资料,这块我正在实习准备用这个两个合起来搞一个基于web 的东西出来现在开始学习,写点东西记录一下心得. 开发环境是window ...

  9. Flask从入门到精通之大型程序的结构二

    一.程序包 程序包用来保存程序的所有代码.模板和静态文件.我们可以把这个包直接称为app(应用),如果有需求,也可使用一个程序专用名字.templates 和static 文件夹是程序包的一部分,因此 ...

随机推荐

  1. angular2.0学习日记1

    使用NG2之前需要安装node以及Npm环境,并到node下下载ng2所需要得文件,具体配置请到https://angular.cn/docs/ts/latest/quickstart.html按照提 ...

  2. Elasticsearch安装配置和测试

    官方教程:https://www.elastic.co/guide/en/elasticsearch/reference/master/_installation.html 中文教程:https:// ...

  3. sift特征简述

    参考文献:http://blog.csdn.net/abcjennifer/article/details/7639681 分为几个步骤: 1. 构建尺度空间 2. LoG近似DoG找到关键点< ...

  4. stm32寄存器版学习笔记05 PWM

    STM32除TIM6和TIM7外都可以产生PWM输出.高级定时器TIM1和TIM8可以同时产生7路PWM,通用定时器可以产生4路PWM输出. 1.TIM1 CH1输出PWM配置步骤 ①开启TIM1时钟 ...

  5. BZOJ2038 2009国家集训队 小Z的袜子(hose) 【莫队】

    BZOJ2038 2009国家集训队 小Z的袜子(hose) Description 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼 ...

  6. 携程框架Apollo实现.NET Core微服务统一配置(测试环境-单机)

    Apollo实现.NET Core微服务统一配置(测试环境-单机) https://www.cnblogs.com/guolianyu/p/10065999.html 一.前言 注:此篇只是为测试环境 ...

  7. Vue生命周期函数详解

    vue实例的生命周期 1 什么是生命周期(每个实例的一辈子) 概念:每一个Vue实例创建.运行.销毁的过程,就是生命周期:在实例的生命周期中,总是伴随着各种事件,这些事件就是生命周期函数: 生命周期: ...

  8. Mysql 5.7初始化密码

    一.MAC Mysql安装 1.下载dmg安装 从http://dev.mysql.com/downloads/mysql/下载dmg安装 二.Mysql密码修改 1.通过mysql -uroot - ...

  9. flannel vxlan 实现原理【转】

    flannel是coreos为kubernets提供的网络解决方案,主要为打通跨节点的容器通信,其中vxlan模式为flannel实现的一种后端模式,其他模式还包括udp, host-gw等,可以通过 ...

  10. angularJS自定义一个过滤器

    ng允许我们自定义指令 下面来我们自己来定义一个过滤指令:filter,返回一个函数的形式 filter(name,callback(){//name:过滤器的名字,callback:匿名函数 ret ...