pip install flask
pip install flask-login
pip install flask-sqlalchemy
pip install flask-bootstrap
1 创建虚拟环境
pipenv install
2 安装需要的模块包
pip install ……
3 根据目录结构创建目录
4 编写setting文件
import os
class BaseConfig(object):
SECRET_KEY = os.getenv('SECRET_KEY', "key string")
SQLALCHEMY_TRACK_MODIFICATIONS = False
class DevelopmentConfig(BaseConfig):
SQLALCHEMY_DATABASE_URI = "mysql://root:123.com@localhost/bluelog"
class TestingConfig(BaseConfig):
TESTING = True
WTF_CSRF_ENABLED = False
SQLALCHEMY_DATABASE_URI = "mysql://root:123.com@localhost/test01"
class ProductionConfig(BaseConfig):
SQLALCHEMY_DATABASE_URI = "mysql://root:123.com@localhost/test01"
config = {
'development': DevelopmentConfig,
'testing': TestingConfig,
'production': ProductionConfig
}
5 编写包init文件
log_test/__init__.py
import os
import pymysql
import click
from flask import Flask
from log_test.settings import config
from log_test.extensions import db, bootstrap, login_manager
from log_test.blueprints.admin import ad
from log_test.blueprints.auth import auth
from log_test.blueprints.log import log
def create_app(config_name=None):
if config_name is None:
config_name = os.getenv('FLASK_CONFIG', 'development')
app = Flask("log_test")
app.config.from_object(config[config_name])
register_extensions(app)
register_blueprints(app)
register_commands(app)
return app
def register_extensions(app):
pymysql.install_as_MySQLdb()
db.init_app(app)
bootstrap.init_app(app)
login_manager.init_app(app)
def register_blueprints(app):
app.register_blueprint(ad, url_prefix='/admin')
app.register_blueprint(auth, url_prefix='/auth')
app.register_blueprint(log)
def register_commands(app):
from log_test.models import User, Admin
@app.cli.command()
@click.option('--drop', is_flag=True, help="Create drop delete")
def initdb(drop):
if drop:
db.drop_all()
click.echo("drop tables")
db.create_all()
click.echo("create tables")
6 引入扩展包 extensions
from flask_bootstrap import Bootstrap
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
bootstrap = Bootstrap()
db = SQLAlchemy()
login_manager = LoginManager()
# 设置加载用户的回调函数
@login_manager.user_loader
def load_user(user_id):
from log_test.models import Admin
user = Admin.query.get(user_id)
return user
# 设置登录页端点
login_manager.login_view = "auth.login"
7 编写表单 forms
from flask_wtf import FlaskForm
from wtforms import SubmitField, StringField, PasswordField, BooleanField
from wtforms.validators import DataRequired, Length, EqualTo
class LoginForm(FlaskForm):
username = StringField("UserName", validators=[DataRequired(), Length(1, 20)])
password = PasswordField("PassWord", validators=[DataRequired(), Length(1, 128)])
remember = BooleanField("Remember Me")
submit = SubmitField("Log In")
class RegistrationForm(FlaskForm):
email = StringField("Email")
username = StringField("UserName", validators=[DataRequired(), Length(1, 20)])
password = PasswordField("PassWord", validators=[DataRequired(), Length(1, 128), EqualTo('password2')])
password2 = PasswordField("Confirm PassWord", validators=[DataRequired(), Length(1, 128)])
submit = SubmitField("Registr")
8 编写模型类 models
from werkzeug.security import generate_password_hash, check_password_hash
from log_test.extensions import db
from flask_login import UserMixin
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(128), unique=True)
passwd_hash = db.Column(db.String(128))
@property
def passwd(self):
raise AttributeError('not readable')
@passwd.setter
def passwd(self, passwd):
self.passwd_hash = generate_password_hash(passwd)
def verify_passwd(self, passwd):
return check_password_hash(self.passwd_hash, passwd)
class Admin(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(20))
password_hash = db.Column(db.String(128))
blog_title = db.Column(db.String(60))
blog_sub_title = db.Column(db.String(100))
name = db.Column(db.String(30))
about = db.Column(db.Text)
def set_password(self, password):
self.password_hash = generate_password_hash(password)
def validate_password(self, password):
return check_password_hash(self.password_hash, password)
9 编写蓝图
log_test/blueprints/admin.py
--------------------------
from flask import Blueprint, render_template
ad = Blueprint("admin", __name__)
@ad.route("/")
def index():
return render_template("admin.html")
log_test/blueprints/auth.py
------------------------
@auth.route("/", methods=('GET', 'POST'))
def login():
form = LoginForm()
emsg = ""
if form.validate_on_submit():
user_name = form.username.data
password = form.password.data
remember = form.remember.data
admin = Admin.query.filter_by(username=user_name).first()
if admin:
if admin.validate_password(password):
login_user(admin)
return redirect(request.args.get('next') or url_for('log.index'))
else:
emsg = "密码错误"
else:
emsg = "账户不存在"
return render_template("login.html", form=form, emsg=emsg)
@auth.route("/logout")
@login_required
def logout():
logout_user()
return redirect(url_for('auth.login'))
@auth.route("/registr", methods=('GET', 'POST'))
def registr():
form = RegistrationForm()
if form.validate_on_submit():
user = Admin(username=form.username.data)
user.set_password(form.password.data)
db.session.add(user)
db.session.commit()
return redirect(url_for("auth.login"))
return render_template("registr.html", form=form)
log_test/blueprints/log.py
-----------------------
from flask import Blueprint, redirect, render_template
from flask_login import login_required, current_user
log = Blueprint("log", __name__)
@log.route('/')
@login_required
def index():
return render_template("index.html", username=current_user.username)
10 编写templates
base.html
--------------
{% extends "bootstrap/base.html" %}
{% block title %}Flasky{% endblock %}
{% block navbar %}
{% block page_content %}{% endblock %}
{% endblock %}
macros.html
------------------
{% macro render_field(field) %}
{{ field.label }}:
{{ field(**kwargs)|safe }}
{% if field.errors %}
{% for error in field.errors %}
- {{ error }}
{% endfor %}
{% endif %}
{% endmacro %}
admin.html
------------
{% extends "base.html" %}
{% block title %} Flasky {% endblock %}
{% block page_content %}
Hello, admin
{% endblock %}
index.html
-----------
{% extends "base.html" %}
{% import 'macros.html' as macros %}
{% block title %} Flasky {% endblock %}
{% block page_content %}
欢迎 {{ username }}!
登出
{% endblock %}
login.html
---------------
{% extends "base.html" %}
{% block title %} Flasky {% endblock %}
{% import 'macros.html' as macros %}
{% block page_content %}
New user?
click here to registr
{% endblock %}
registr.html
----------------
{% extends "base.html" %}
{% block title %} Flasky {% endblock %}
{% import 'macros.html' as macros %}
{% block page_content %}
{% endblock %}
{% endblock %}
{% block content %}
{% block mycontent %}
{% endblock %}
1 增加角色模型
models.py
-------
# 在用户模型中增加外键关联角色模型
class Admin(db.Model, UserMixin):
……
# 定义角色外键
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
# 设置默认角色为User
def __init__(self, **kwargs):
super(Admin, self).__init__(**kwargs)
if self.role is None:
self.role = Role.query.filter_by(name='User').first()
# 角色验证
def can(self, permissions):
return self.role is not None and (self.role.permissions & permissions) == permissions
def is_administrator(self):
return self.can(Permission.ADMINISTER)
# 建立角色模型
class Role(db.Model):
__tablename__ = 'roles'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
default = db.Column(db.Boolean, default=False, index=True)
permissions = db.Column(db.Integer)
# 建立双向关系
admins = db.relationship('Admin', backref='role', lazy='dynamic')
# 权限常量表
class Permission:
FOLLOW = 0x01
COMMENT = 0x02
WRITE_ARTICLES = 0x04
MODERATE_COMMENTS = 0x08
ADMINISTER = 0x80
# 设置匿名用户权限验证
class AnonymousUser(AnonymousUserMixin):
def can(self, permissions):
return False
def is_administrator(self):
return False
2 自定义装饰器检查用户角色
decorators.py
------------
from functools import wraps
from flask import abort
from flask.ext.login import current_user
def permission_required(permission):
def decorator(f):
@wraps(f)
def decorated_function(*args, **kwargs):
if not current_user.can(permission):
abort(403)
return f(*args, **kwargs)
return decorated_function
return decorator
def admin_required(f):
return permission_required(Permission.ADMINISTER)(f)
3 在数据库中创建角色
__init__.py
----------------------
def register_commands(app):
from log_test.models import Admin, Permission, Role, Post
……
@app.cli.command()
def initroles():
roles = {
'User': (Permission.FOLLOW | Permission.COMMENT | Permission.WRITE_ARTICLES, True),
'Moderator': (Permission.FOLLOW | Permission.COMMENT | Permission.WRITE_ARTICLES | Permission.MODERATE_COMMENTS, False),
'Administor': (0xff, False)
}
for r in roles:
role = Role.query.filter_by(name=r).first()
if role is None:
role = Role(name=r)
role.permissions = roles[r][0]
role.default = roles[r][1]
db.session.add(role)
db.session.commit()
flask initdb --drop 重新创建数据库
flask initroles 初始化角色
4 测试
admin.py
-------
from flask import Blueprint, render_template, request, redirect, url_for
from flask_login import login_required, current_user
from log_test.utols import admin_required, permission_required
from log_test.models import Permission
ad = Blueprint("admin", __name__)
@ad.route('/')
@login_required
@admin_required
def for_admins_only():
return "For administrators!"
@ad.route('/moderator')
@login_required
@permission_required(Permission.MODERATE_COMMENTS)
def for_moderators_only():
return "For comment moderators!"
# 把 Permission 类加入模板上下文
@ad.app_context_processor
def inject_permissions():
return dict(Permission=Permission)
5 测试前记得先给用户配置好权限
1 查询出结果
@log.route('/', methods=('GET', 'POST'))
@login_required
def index():
form = PostForm()
if form.validate_on_submit():
post = Post(body=form.body.data,
admin=current_user._get_current_object())
db.session.add(post)
db.session.commit()
return redirect(url_for("log.index"))
page = request.args.get('page', 1, type=int)
# paginate() 分页函数: page 页码、 per_page 可选参数默认20,error_out 默认True 超出页面范围返回404错误,false 超出页面范围返回空列表
pagination = Post.query.order_by(Post.timestamp.desc()).paginate(page, per_page=current_app.config['FLASKY_POSTS_PER_PAGE'], error_out=False)
# 返回当前页中内容
posts = pagination.items
return render_template("index.html", form=form, posts=posts, pagination=pagination)
# return render_template("index.html", form=form, posts=posts)
2 编写分页宏模版
macros.html
-----------
{% macro pagination_widget(pagination, endpoint) %}
{% endmacro %}
3 引入宏
index.html
---------------
{% import 'macros.html' as macros %}
…………
{{ macros.pagination_widget(pagination, '.index') }}
{% endblock %}