《Flask web开发》笔记5:大型程序的结构&用户认证Flask-Login
一.学习的内容:
1.工厂函数的定义;
2.蓝本的注册和蓝本文件结构;
3.Werkzeug实现密码散列如何实现;
4.Flask_login的用法;
以上内容通过代码的形式展示;
二、工程的文件结构:
|-falsky/
|-app/
|-__init__.py //初始化、工厂函数、注册蓝本等
|-models.py //定义数据库
|-templates/
|-base.html //基本的模板
|-auth/ //蓝本的模板,这样不会跟其他蓝本弄混
|-login.html
|-welcome.html
|-auth/
|-__init__.py
|-views.py //蓝本的路由和视图函数的定义
|-forms.py //针对这个蓝本的表单
|-config.py //设置,可以给指定人不同的设置,例如数据库的不同
|-manage.py //运行
三、程序代码:
1.app/__init__.py
解释:这个py文件最为重要,database、flask_bootstrap、flask_login的实例化都在里面;另外,还有工厂函数(重点)
# coding: utf-8 from flask import Flask, render_template from flask_bootstrap import Bootstrap from flask_mail import Mail from flask_sqlalchemy import SQLAlchemy from flask_login import LoginManager from config import config bootstrap = Bootstrap() mail = Mail() db = SQLAlchemy() login_manager = LoginManager() login_manager.session_protection = None login_manager.login_view = 'auth.login' #端点 def create_app(config_name): #工厂函数 app=Flask(__name__) app.config.from_object(config[config_name]) #调用config.py里定义配置名的配置 config[config_name].init_app(app) bootstrap.init_app(app) mail.init_app(app) db.init_app(app) login_manager.init_app(app) #注册蓝本,路由写在里面,注册才能使用 from .auth import auth as auth_blueprint app.register_blueprint(auth_blueprint, url_prefix='/auth') #url_prefix里的值代表路由的路径为/auth/**; return app
2.app/auth/__init__.py
解释:既然前面的工厂函数里注册了,那么这里就要创建蓝本对象了
from flask import Blueprint auth = Blueprint('auth',__name__) #一般创建的蓝本对象和蓝本文件件重名 from . import views #这里引入路由
3.app/auth/views.py
解释:这里定义路由;
# coding: utf-8 #import os #import sys #sys.path.append(os.path.abspath(os.path.dirname(__file__) + '/' + '..')) from flask import render_template from flask import redirect, request, url_for, flash from flask_login import login_user from . import auth from ..models import User from .forms import LoginForm from flask_login import logout_user, login_required @auth.route('/login/',methods=['GET', 'POST']) def login(): form = LoginForm() print 'views test' if form.validate_on_submit(): user = User.query.filter_by(email = form.email.data).first() if user is not None and user.verify_password(form.password.data): login_user(user,form.remember_me.data) return redirect(request.args.get('next') or url_for('auth.welcome')) pass flash('Invalid username or password') pass return render_template('auth/login.html',form=form) pass @auth.route('/logout') @login_required def logout(): logout_user() flash('you have been logged out') pass @auth.route('/test') def test(): form = LoginForm() return render_template('welcome.html') pass
4.app/auth/forms.py
解释:在views视图函数要用到渲染模板的表单数据在这里定义;
# coding: utf-8 from flask_wtf import Form from wtforms import StringField, PasswordField, BooleanField, SubmitField # 输入字段 密码字段 复选字段 提交 from wtforms.validators import Required, Length, Email class LoginForm(Form): email = StringField('Email', validators=[Required(), Length(1, 64),Email()]) password = PasswordField('Password', validators=[Required()]) remember_me = BooleanField('Keep me logged in') submit = SubmitField('Log In')
5.app/models.py
解释:这里定义页面的数据库类:
# coding: utf-8 from werkzeug.security import generate_password_hash, check_password_hash from flask_login import UserMixin from . import login_manager,db from flask_sqlalchemy import SQLAlchemy class User(UserMixin, db.Model): __tablename__ = 'users_three' id = db.Column(db.Integer, primary_key = True) email = db.Column(db.String(64), unique=True, index=True) username = db.Column(db.String(64), unique=True, index=True) password_hash = db.Column(db.String(128)) #利用了Werkzeug实现密码哈希形式存入数据库; @property def password(self): raise AttributeError('password is not atterbute') pass @password.setter def password(self,password): self.password_hash=generate_password_hash(password) pass def verify_password(self,password): return check_password_hash(self.password_hash,password) pass @login_manager.user_loader def load_user(user_id): return User.query.get(int(user_id))#加载用户的回调函数接收以 Unicode 字符串形式表示的用户标识符。如果能找到用户,这个函数必须返回用户对象;否则应该返回 None pass
6.config.py
解释:这里你可以针对不同用户设置不同数据库路径、secret_key等、、、
import os basedir = os.path.abspath(os.path.dirname(__file__)) class config: SECRET_KEY = os.environ.get('SECRET_KEY') or 'hard to guess string' SQLALCHEMY_DATABASE_URI = 'mysql://root@localhost/yxctest' SQLALCHEMY_COMMIT_ON_TEARDOWN = True SQLALCHEMY_TRACK_MODIFICATIONS = True @staticmethod def init_app(app): pass config = { #'development': DevelopmentConfig, #'testing': TestingConfig, #'production': ProductionConfig, 'default': config }
7.manage.py
解释:这里app.run运行
# coding: utf-8 import os from app import create_app,db from app.models import User app = create_app(os.getenv('FLASK_CONFIG') or 'default') if __name__ == '__main__': app.run(debug=True)
到这里,一个完整的文件框架已经完成,下面添加一些模板
base.html
{% extends "bootstrap/base.html" %} {% block content %} <div class="container"> <div class="page-header"> <ul class="nav navbar-nav navbar-right"> {% if current_user.is_authenticated%} <li><a href="{{ url_for('auth.logout') }}">Sign Out</a></li> {% else %} <li><a href="{{ url_for('auth.login') }}">Sign In</a></li> {% endif %} </ul> </div> </div> {%endblock %}
login.html
{% extends "base.html" %} {% import "bootstrap/wtf.html" as wtf %} {% block title %}Flasky - Login{% endblock %} {% block content %} <div class="container"> <div class="page-header"> <h1>Login</h1> </div> </div> {{ wtf.quick_form(form) }} {% endblock %}
wlecome.html
{% extends "bootstrap/base.html" %} {% block title %}TTTTT{% endblock %} {% block content %} <div class="container"> <div class="page-header"> <h1>Hello!</h1> </div> </div> {% endblock %}
效果图: