本文目的是为了完成一个项目用到的flask基本知识,例子会逐渐加深。最好对着源码,一步一步走。
下载源码,运行
pip install -r requirements.txt 建立环境
python db_create.py 创建自己数据库
python db_migrate 迁移数据库
————————————————————————————–
flask 不仅简介小巧,同时运用的时候十分灵活。下面简单介绍一下如何编写一个 flask项目。
涉及调用开发服务器,数据库连接以及 ORM 映射,还有数据库的迁移,模板使用。后期再完善会话,管理后台,缓存等。
一 、安装环境
我们使用 flask web框架,并用 sqlalchemy来做数据库映射,并使用 migrate做数据迁移。
$ pip install flask
$ pip install SQLAlchemy==0.7.9
$ pip install flask-sqlalchemy
$ pip install flask-migrate
$ pip install sqlalchemy-migrate 二、建立项目
flask 没有 django 那样原生的 manage管理工具(flask-admin可以实现,日后再说)。因此我们需要手动建立目录。新建一个 myproject目录,在里面建 app tmp两个文件夹,然后在 app文件夹里面建立 static, templates 两个文件夹,用来存储静态文件和模板。最后目录结构如下:
├── app
│ ├── static
│ ├── templates
└── tmp 三、 初始化文件
建立一些文件,在 app文件里建立 __init__.py models.py views.py 然后在与app 同目录下建立 config.py 和 run.py 此时的目录结构如下: (env)admin@admindeMacBook-Air:~/project/python/flask/project$ tree ├── app
│ ├── static
│ ├── templates
│ ├── __init__.py
│ ├── models.py
│ ├── views.py
├── config.py
├── run.py
└── tmp 四 、开始项目
1 hello wrod
打开 (/app/__init.py) 文件,写入
# -*- coding: utf-8 -*-
from flask import Flask # 引入 flask
app = Flask(__name__) # 实例化一个flask 对象
import views # 导入 views 模块
# from app import views
注意,我们的 app 文件夹其实是一个python包,from app import views 这句话也就是从 包app里面导入 views模块,所以写成注释的那句话也没错,其他代码实践喜欢写成后面那种
现在我们来开始写我们的视图,打开 (/app/views.py) # -*- coding: utf-8 -*-
from app import app
from models import User, Post, ROLE_USER, ROLE_ADMIN
@app.route(‘/’)
def index():
return ‘hello world, hello flask’ 下一步我们将要启动我们的开发服务器,打开 (/run.py)
# -*- coding: utf-8 -*-
from app import app
if __name__ == ‘__main__’:
app.debug = True # 设置调试模式,生产模式的时候要关掉debug
app.run() # 启动服务器
这段代码需要注意第一句 from app import app。也许会有疑问,我们的 app 包里,貌似没有 app.py 这样的模块。其实这是 flask 方式和python的导入方式。from app 指导入 app 包里的 __iniy__.py 所以这句话的含义是导入 __.init__.py 里面的 app实例。
打开shell 运行
$ python run.py
(env)admin@admindeMacBook-Air:~/project/python/flask/project$ python run.py * Running on http://127.0.0.1:5000/
* Restarting with reloader
用浏览器打开 http://127.0.0.1:5000/ 将会看到一个输入 hello world 的页面
2 连接数据库
下面开始连接数据库引擎 先要做一个简单的配置 打开 (/config.py) # -*- coding: utf-8 -*- import os
basedir = os.path.abspath(os.path.dirname(__file__))
SQLALCHEMY_DATABASE_URI = ‘sqlite:///%s’ % os.path.join(basedir, ‘app.db’)
SQLALCHEMY_MIGRATE_REPO = os.path.join(basedir, ‘db_repository’)
CSRF_ENABLED = True
SECRET_KEY = ‘you-will-never-guess’ SQLALCHEMY_DATABASE_URI是the Flask-SQLAlchemy必需的扩展。这是我们的数据库文件的路径。
SQLALCHEMY_MIGRATE_REPO 是用来存储SQLAlchemy-migrate数据库文件的文件夹。
然后打开 (/app/__init__.py) # -*- coding: utf-8 -*-
import os
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
from config import basedir app = Flask(__name__)
app.config.from_object(‘config’) # 载入配置文件
db = SQLAlchemy(app) # 初始化 db 对象
# from app import views, models # 引用视图和模型
import views, models 打开 (/app/models.py) # -*- coding: utf-8 -*- from app import db
ROLE_USER = 0
ROLE_ADMIN = 1 class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
nickname=db.Column(db.String(60), index=True, unique=True)
email = db.Column(db.String(120), index=True, unique=True)
role = db.Column(db.SmallInteger, default=ROLE_USER) def __repr__(self):
return ‘<User %r>’ % self.nickname 我们的模型建立了一个 user类,正好是 数据库里面的 user 表,表有四个字段
现在我们的数据库配置已经好了,可是还没有建立数据库。新建一个文件 (/db_create.py) # -*- coding: utf-8 -*- from migrate.versioning import api
from config import SQLALCHEMY_DATABASE_URI
from config import SQLALCHEMY_MIGRATE_REPO
from app import db
import os.path db.create_all() if not os.path.exists(SQLALCHEMY_MIGRATE_REPO):
api.create(SQLALCHEMY_MIGRATE_REPO, 'database repository')
api.version_control(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
else:
api.version_control(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO, api.version(SQLALCHEMY_MIGRATE_REPO)) 这个脚本是完全通用的,所有的应用路径名都是从配置文件读取的。当你用在自己的项目时,你可以把脚本拷贝到你的项目目录下就能正常使用了。
打开shell 运行
$ python db_create.py 运行这条命令之后,你就创建了一个新的app.db文件。这是个支持迁移的空sqlite数据库,同时也会生成一个带有几个文件的db_repository目录,这是SQLAlchemy-migrate存储数据库文件的地方,注意如果数据库已存在它就不会再重新生成了。这将帮助我们在丢失了现有的数据库后,再次自动创建出来。
运行
slqite3 app.db
>>> .tables
>>> user
或者使用 sqlite 图形化客户端,没有需要安装 (windows 下直接下载安装包即可)
$ sudo apt-get install sqlitebrowser
3 数据库迁移
每当我们更改了 models 。等价于更改了数据库的表结构,这个时候,需要数据库同步。新建 (/db_migrate.py) # -*- coding: utf-8 -*- import imp
from migrate.versioning import api
from app import db
from config import SQLALCHEMY_DATABASE_URI
from config import SQLALCHEMY_MIGRATE_REPO migration = SQLALCHEMY_MIGRATE_REPO + '/versions/%03d_migration.py' % (api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO) + 1)
tmp_module = imp.new_module('old_model')
old_model = api.create_model(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO) exec old_model in tmp_module.__dict__ script = api.make_update_script_for_model(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO, tmp_module.meta, db.metadata) open(migration, 'wt').write(script) api.upgrade(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO) print 'New migration saved as' + migration
print 'Current database version:' + str(api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)) 这个脚本看起来很复杂,SQLAlchemy-migrate通过对比数据库的结构(从app.db文件读取)和models结构(从app/models.py文件读取)的方式来创建迁移任务,两者之间的差异将作为一个迁移脚本记录在迁移库中,迁移脚本知道如何应用或者撤销一次迁移,所以它可以方便的升级或者降级一个数据库的格式。
开始数据库迁移
$ python db_mirgate.py
New migration saved as db_repository/versions/001_migration.py Current database version: 1
相应的,我们继续创建数据库 升级和回退的脚本
(/db_upgrade.py) # -*- coding: utf-8 -*- from migrate.versioning import api
from config import SQLALCHEMY_DATABASE_URI
from config import SQLALCHEMY_MIGRATE_REPO
api.upgrade(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
print 'Current database version:' + str(api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)) (/db_downgrade.py) # -*- coding: utf-8 -*- from migrate.versioning import api
from config import SQLALCHEMY_DATABASE_URI
from config import SQLALCHEMY_MIGRATE_REPO v = api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)
api.downgrade(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO, v – 1) print 'Current database version:' + str(api.db_version(SQLALCHEMY_DATABASE_URI, SQLALCHEMY_MIGRATE_REPO)) 4 操作数据库
接下来,我们定义一个新的models class 并做第二次迁移归并
打开(/app/models.py) # -*- coding: utf-8 -*- from app import db ROLE_USER = 0
ROLE_ADMIN = 1 class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
nickname=db.Column(db.String(60), index=True, unique=True)
email = db.Column(db.String(120), index=True, unique=True)
role = db.Column(db.SmallInteger, default=ROLE_USER)
posts = db.relationship(‘Post’, db.backref = ‘author’, db.lazyload = ‘dynamic’) def __repr__(self):
return ‘<User %r>’ % self.nickname class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
body = db.Column(db.String(140))
timestamp = db.Column(db.DateTime)
user_id = db.Column(db.Integer, db.ForeignKey(‘user.id’)) def __repr__(self):
return ‘<Post %r>’ % (self.body) 这里我们进行了两个class 的关联。
$ python db_mirgrate.py
New migration saved as db_repository/versions/002_migration.py Current database version: 2
打开 (/app/views.py) # -*- coding: utf-8 -*- from flask import render_template, flash, redirect, session, url_for, request, g
from app import app, db
from models import User, Post, ROLE_USER, ROLE_ADMIN @app.route(‘/’)
def index():
return render_template(‘index.html’) @app.route(‘/adduser/<nickname>/<email>’)
def adduser(nickname, email):
u = User(nickname=nickname, email=email)
try:
db.session.add(u)
db.session.commit()
return ‘add successful’
except Exception, e:
return ‘something go wrong’ @app.route(‘/getuser/<nickname>’)
def getuser(nickname):
user = User.query.filter_by(nickname=nickname).first()
return render_template(‘user.html’, user=user) @app.errorhandler(404)
def internal_error(error):
return render_template(‘404.html’), 404 @app.errorhandler(500)
def internal_error(error):
db.session.rollback()
return render_template(‘500.html’), 500 这次我们使用了模板, 新建(/app/templates/user.html) <html>
<head>
<title>user</title>
</head>
<body>
<h1>user</h1>
<ul>
<li>user: {{ user.nickname }}</li>
<li>email: {{ user.email }}</li>
</ul>
</body>
</html> 最后运行
$ python run.py
用浏览器访问 http://127.0.0.1:5000/adduser/username/useremail
最后还可以用 sqlite 客户端打开查看我们的数据库变换。关于更多的数据库操作方法,请阅读 sqlalchemy文档。
最后我们的代码目录结构如下: (env)admin@admindeMacBook-Air:~/project/python/flask/project$ tree
.
├── app
│ ├── __init__.py
│ ├── models.py
│ ├── static
│ ├── templates
│ │ ├── 404.html
│ │ ├── 500.html
│ │ ├── index.html
│ │ └── user.html
│ ├── views.py
├── app.db
├── config.py
├── db_create.py
├── db_downgrade.py
├── db_migrate.py
├── db_repository
│ ├── __init__.py
│ ├── manage.py
│ ├── migrate.cfg
│ ├── README
│ └── versions
│ ├── 001_migration.py
│ ├── 002_migration.py
│ ├── __init__.py
├── db_upgrade.py
├── requirements.txt
├── run.py
├── tmp 5 表单
接下来,我们将要接触表单方面的内容。flask原生提供的表单处理,也比较简单。当然,有了轮子,我们就直接用好了。需要安装一个
Flask-WTF==0.9.4。
安装完之后,打开 (/app/forms.py) # -*- coding: utf-8 -*- import re
from flask_wtf import Form
from flask_wtf.html5 import EmailField
from wtforms import TextField, PasswordField, BooleanField
from wtforms.validators import DataRequired, ValidationError
from models import User
from hashlib import md5 class RegisterFrom(Form):
nickname = TextField(‘nickname’, validators=[DataRequired()])
email = EmailField(’email’, validators=[DataRequired()])
password = PasswordField(‘password’, validators=[DataRequired()])
conform = PasswordField(‘conform’, validators=[DataRequired()]) def validate_nickname(self, field):
nickname = field.data.strip()
if len(nickname) < 3 or len(nickname) > 20:
raise ValidationError(‘nickname must be 3 letter at least’)
elif not re.search(r’^\w+$’, nickname):
raise ValidationError(‘User names can contain only alphanumeric characters and underscores.’)
else:
# 验证是否已经注册
u = User.query.filter_by(nickname=nickname).first()
if u:
raise ValidationError(‘The nickname already exists’) def validate_email(self, field):
email = field.data.strip()
email = User.query.filter_by(email=email).first()
if email:
raise ValidationError(‘The email already exists’) def validate_password(self, field):
password = field.data.strip()
if len(password) < 6:
raise ValidationError(‘password must be 3 letter at least’) def validate_conform(self, field):
conform = field.data.strip()
if self.data[‘password’] != conform:
raise ValidationError(‘the password and conform are different’) 上述代码定义了一个 RegisterFrom class,正好是一个表单,class的字段映射为表单的域。其中 wtforms 中有 TextField, PasswordField, BooleanField这些表单类型,flask_wtf.html5 中 EmailField 则是 html5 规范的。
表单有 form.validate_on_submit() 方法,这个方法执行之前会验证表单域。验证方法和django类似,都是 validate_field 的方法。其中一个参数 field正好是表单的value值 需要注意验证两次密码是否相同的时候,需要用到 self.data 。这是一个字典,包含了表单域的name和value
6 用户注册
打开(/app/views.py)添加注册方法。 @app.route(‘/account/signup’, methods=[‘GET’, ‘POST’])
def signup():
form = RegisterFrom()
if request.method == ‘POST’:
if form.validate_on_submit():
psdmd5 = md5(form.data[‘password’])
password = psdmd5.hexdigest()
u = User(nickname=form.data[‘nickname’], email=form.data[’email’], password=password)
try:
db.session.add(u)
db.session.commit()
flash(‘signup successful’) except Exception, e:
return ‘something goes wrong’
return redirect(url_for(‘signin’)) return render_template(‘signup.html’, form=form) 模版 新建 (/app/tempaltes/signup.html) <!– extend base layout –>
{% extends “base.html” %} {% block content %}
<form method=”POST” action=”>
{{ form.hidden_tag() }}
<p>{{ form.nickname.label }} {{ form.nickname(size=20) }}</p>
<p>{{ form.email.label }} {{ form.email(size=20) }}</p>
<p>{{ form.password.label }} {{ form.password(size=20) }}</p>
<p>{{ form.conform.label }} {{ form.conform(size=20) }}</p>
<input type=”submit” value=”Sign Up”>
<input type=”reset” value=”Reset”>
</form> {{ form.errors }}
{% endblock %} 其中,主要是用到 form 对象的一些属性。form.nickname.label 是指表单类定义的名字,form.nickname 会转变成 表单域的 html 代码 即 <input type=”text” id=”nickname” name=”nickaname” value=””/> 7 用户登录
先添加一个用户登录的表单,打开 (/app/forms.py) class LoginForm(Form):
nickname = TextField(‘nickname’, validators=[DataRequired()])
password = PasswordField(‘password’, validators=[DataRequired()])
remember_me = BooleanField(‘remember_me’, default=False) def validate_nickname(self, field):
nickname = field.data.strip()
if len(nickname) < 3 or len(nickname) > 20:
raise ValidationError(‘nickname must be 3 letter at least’)
elif not re.search(r’^\w+$’, nickname):
raise ValidationError(‘User names can contain only alphanumeric characters and underscores.’)
else:
return nickname 用户登录,最简单的方法就是 验证用户名,如果通过,则添加 session,登出的时候,清除session即可,模版里面可以直接使用 request.session用来判断用户登录状态
打开(/app/views.py) 添加登录登出方法 @app.route(‘/account/signin’, methods=[‘GET’, ‘POST’])
def signin():
form = LoginForm()
if request.method == ‘POST’:
if form.validate_on_submit():
nickname = form.data[‘nickname’]
psdmd5 = md5(form.data[‘password’])
password = psdmd5.hexdigest()
u = User.query.filter_by(nickname=nickname, password=password).first()
if u:
session[‘signin’] = True
flash(‘signin successful’)
return redirect(url_for(‘index’))
else:
flash(u’用户名或者密码错误’)
return render_template(‘signin.html’, form=form) @app.route(‘/account/signout’)
def signout():
session.pop(‘signin’, None)
flash(‘signout successful’)
return redirect(‘index’) 注意,我们使用 flask 的时候,用了中文,就必须是 unicode
除了原生的登录方式,我们还可以用flask-login。安装flask-login
此时需要初始化一个 login_manager 对象,打开 (/app/__init__.py) # -*- coding: utf-8 -*- import os
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
from flask.ext.login import LoginManager
from config import basedir app = Flask(__name__) app.config.from_object(‘config’) db = SQLAlchemy(app) # 初始化数据库管理对象
login_manager = LoginManager() # 初始化登录管理对象
login_manager.init_app(app)
login_manager.login_view = “signin” # 登录跳转视图
login_manager.login_message = u”Bonvolu ensaluti por uzi tio paĝo.” # 登录跳转视图前的输出消息 # from app import views, models import views, models 然后打开 (/app/views.py)
添加下面方法, 注释掉之前的 登入登出方法 from flask.ext.login import login_user, logout_user, current_user, login_required
from app import app, db, login_manager @login_manager.user_loader
def load_user(userid):
return User.query.get(userid) @app.route(‘/account/signin’, methods=[‘GET’, ‘POST’])
def signin():
form = LoginForm()
if request.method == ‘POST’:
if form.validate_on_submit():
nickname = form.data[‘nickname’]
psdmd5 = md5(form.data[‘password’])
password = psdmd5.hexdigest()
remember_me = form.data[‘remember_me’]
user = User.query.filter_by(nickname=nickname, password=password).first()
if user:
login_user(user, remember = remember_me)
flash(‘signin successful’)
return redirect(request.args.get(“next”) or url_for(“index”))
else:
flash(u’用户名或者密码错误’)
return render_template(‘signin.html’, form=form) @app.route(‘/account/signout’) @login_required
def signout():
logout_user()
flash(‘signout successful’)
return redirect(‘index’) 模版也会响应的改,具体看源码吧。
base.html <html>
<head>
<meta http-equiv=”Content-Type” content=”text/html; charset=utf-8″ />
{% if title %}
<title>{{title}} – microblog</title>
{% else %}
<title>microblog</title>
{% endif %}
</head>
<body>
<div class="metanav"> <div>
<a href=”{{ url_for(‘index’) }}”>Home</a>
{% if current_user.is_authenticated() %}
<a href=”{{ url_for(‘signout’) }}”>signout</a>
{% else %}
<a href=”{{ url_for(‘signin’) }}”>signin</a>
{% endif %}
</div> {% for message in get_flashed_messages() %}
<div class="flash">{{ message }}</div>
{% endfor %}
{% block content %}{% endblock %}
</body>
</html>

python flask实现小项目方法的更多相关文章

  1. 【Azure 应用服务】Azure App Service For Linux 上实现 Python Flask Web Socket 项目 Http/Https

    问题描述 在上篇博文"[Azure 应用服务]App Service for Linux 中实现 WebSocket 功能 (Python SocketIO)"中,实现了通过 HT ...

  2. python网页爬虫小项目开发

    这是我最近接的一个小项目,花了是整整四天多时间. 任务是将http://www.examcoo.com/index/detail/mid/7网站下所有的试卷里的试题全部提取出来,首先按照题型进行分类, ...

  3. [Python]Python入坑小项目推荐- Flask example minitwit

    知乎上看到的Python练手项目推荐,链接见:https://www.zhihu.com/question/29372574,不知道是我自己懒得看还是理解力不行,这些项目真的是...太大了呀~~~~ ...

  4. python flask构建小程序订餐系统--centos下项目开发环境的搭建

    1.项目开发环境的搭建(Linux环境) 1)软件的安装 我们搭建整个项目的过程中,我们需要用到下面的一些软件,但是这些软件的安装过程我们在这里不用说明.(因为windows软件的安装比较的简单,类似 ...

  5. Python基础--01小项目体现的基础知识

    part1:猜拳游戏 #coding=utf-8 #当有汉语时可能编译器不认识,需要定义代码 ''' 多行注释 写这个程序是为了熟悉python的基本语法 这是第一个小例子包含简单的if判断,循环和输 ...

  6. Python 综合应用小项目一

    数据库报错重连机制 利用异常捕获来获取mysql断开的报错,然后再重连 import MySQLdb as mysql class DB: def __init__(self,host,user,pa ...

  7. python 的一些小项目

    1.在线教育平台(视频播放) 2.仿微信网页版(语音.视频.文字聊天) 3.高德API + Python 解决租房问题 4.仿知乎 5.Django打造文件分享系统.文件管理.搜索引擎(仿云盘) 6. ...

  8. Python爬虫实战小项目

    爬取国家药品监督管理总局中基于中华人民共和国化妆品生产许可证相关数据 import requests from fake_useragent import UserAgent ua = UserAge ...

  9. Flask框架的学习与实战(二):实战小项目

    昨天写了一篇flask开发环境搭建,今天继续,进行一个实战小项目-blog系统. blog系统很简单,只有一个页面,然而麻雀虽小五脏俱全.这里目的不是为了做项目而做项目,这篇文章本意是通过这次练习传达 ...

随机推荐

  1. 树莓派 CSI摄像头 No data received from sensor. Check all connections, including the Sunny one on the camera board

    不知道为什么摄像头在包里放了两天旧坏了,中间完全没用过摄像头的功能,查了资料,原因大概有两种 1)sunny烧了 2)试摄像头传感器坏了 这两天没有插拔过摄像头,可能是树莓派漏电了,也可能是它被压坏了 ...

  2. 堆排序 GPLT L2-012 关于堆的判断

    题目链接:https://pintia.cn/problem-sets/994805046380707840/problems/994805064676261888 分析:这题看起来非常唬人,其实不难 ...

  3. GPLT L2-004 这是二叉搜索树吗?

    题目链接:https://pintia.cn/problem-sets/994805046380707840/problems/994805070971912192 类似题目有FBI树 这两个题有个小 ...

  4. 关于StringUtils的常用方法

    StringUtils.split(String, char) * <pre> * StringUtils.split(null, *) = null * StringUtils.spli ...

  5. 使用LVM方式安装Ubuntu 16.04

    --- By 小甘丶 注: 这里只讲解如何配置LVM,其他不再陈述! 这个方法,通用的!只要操作系统支持LVM即可!(个人推测,尚未证实) 配置好虚拟机后,开始安装,先进入Ubuntu使用界面,对磁盘 ...

  6. Cassandra V2.1.20单机安装

    1. 系统调优 [root@sht-sgmhadoopcm- ~]# echo "vm.max_map_count=131072" >> /etc/sysctl.con ...

  7. 时间序列(六): 炙手可热的RNN: LSTM

    目录 炙手可热的LSTM 引言 RNN的问题 恐怖的指数函数 梯度消失* 解决方案 LSTM 设计初衷 LSTM原理 门限控制* LSTM 的 BPTT 参考文献: 炙手可热的LSTM 引言 上一讲说 ...

  8. python爬虫---urllib库的基本用法

    urllib是python自带的请求库,各种功能相比较之下也是比较完备的,urllib库包含了一下四个模块: urllib.request   请求模块 urllib.error   异常处理模块 u ...

  9. IntelliJ IDEA下载及安装,破解

    IntelliJ IDEA下载及安装,破解 百度百科:IDEA 全称IntelliJ IDEA,是java语言开发的集成环境,IntelliJ在业界被公认为最好的java开发工具之一,尤其在智能代码助 ...

  10. from…import 语句