一、项目结构:
|--mytest
|--app/
|--init.py--初始化
|--models.py--模型文件
|--auth/
|--init.py--初始化
|--forms.py--auth表单文件
|--views.py--auth视图文件
|--main/
|--init.py--main初始文件
|--views.py--视图文件
|--static/--样式文件
|--templates/--模板文件
|--venv--虚拟环境
|--config.py--配置文件
|--emailTest.py--发送邮件文件
|--etc.py--默认文件
|--manage.py--启动文件
|--requirements.txt--三方库集合
1、配置文件:
#-- coding:utf-8 --
class Config(object):
'''
1 加密秘钥
2 自动提交数据库
'''
SECRET_KEY = "Ay98Cct2oNSlnHDdTl8"
SQLALCHEMY_COMMIT_ON_TEARDOWN = True
# SQLALCHEMY_TRACK_MODIFICATIONS = True
@staticmethod
def init_app(app):
pass
class LastConfig(Config):
'''
调试
连接数据库
'''
DEBUG = True
#SQLALCHEMY_ECHO = True
#连接数据库、其中username为你的登录的用户名,password则为登录密码
SQLALCHEMY_DATABASE_URI = "mysql+pymysql://username:password@localhost:3306/test_three"
config = {'default':LastConfig, 'test':TestConfig}
2、启动项
# -- coding:utf-8 --
from app import create_app, db
from flask_script import Manager, Shell
from etc import default
#创建app
app = create_app(default)
#实例化Manager对象
manager = Manager(app)
def make_shell_context():
return dict(app=app, db=db)
#调试模式
manager.add_command("shell", Shell(make_context=make_shell_context))
#启动程序
if name == "main":
manager.run()
3、三方库集合requirements.txt
alembic==0.8.8
blinker==1.4
click==6.6
dominate==2.2.1
Flask==0.11.1
Flask-Bootstrap==3.3.7.0
Flask-Mail==0.9.1
Flask-Migrate==2.0.0
Flask-Moment==0.5.1
Flask-Script==2.0.5
Flask-SQLAlchemy==2.1
Flask-WTF==0.12
itsdangerous==0.24
Jinja2==2.8
Mako==1.0.4
MarkupSafe==0.23
python-editor==1.0.1
SQLAlchemy==1.0.15
visitor==0.1.3
Werkzeug==0.11.11
WTForms==2.1
Flask-Login==0.3.1
一键安装插件库:
pip install -r requirements.txt
4、发送邮箱配置文件
# -- coding:utf-8 --
from flask import Flask,render_template
from flask_mail import Mail, Message
app = Flask(name)
'''
1 邮箱服务
2 邮箱端口
3 发送邮箱
4 邮箱授权码
'''
app.config.update(
MAIL_SERVER='smtp.qq.com',
MAIL_PORT='465',
MAIL_USE_SSL=True,
MAIL_USERNAME='QQ号',
MAIL_PASSWORD='授权码'#(可在邮箱设置中获取)
)
mail = Mail(app)
def send_email(to,subject,template,user,token):
'''
1 实例化Message对象
2 设置发送邮件的内容
3 发送邮件
'''
msg = Message(subject, sender='发送文件的QQ邮箱地址', recipients=[to])
msg.html = render_template(template + '.txt', user=user,token=token)
mail.send(msg)
5、虚拟环境:前面已将介绍了,在cmd中打开创建虚拟环境的根目录,通过执行命令virtualenv venv(虚拟环境名,自定义)来创建:
创建虚拟环境
virtualenv venv
或
python -m venv venv
6、app:即整个应用的核心,我是手动创建的文件夹,并且命名为app.
6.1、初始文件:其中包括工厂函数(create_app(config)),其中的参数就是配置文件里的配置名、数据库实例化数据库、邮箱、时间格式、bootstrap等。
from flask import Flask
from flask_bootstrap import Bootstrap
from flask_mail import Mail
from flask_moment import Moment
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
from config import config
#实例化各应用类
bootstrap = Bootstrap()
mail = Mail()
moment = Moment()
db = SQLAlchemy()
login_manager = LoginManager()
login_manager.session_protection = 'strong'
login_manager.login_view = 'auth.login'
def create_app(config_name):
app = Flask(__name__)
app.config.from_object(config[config_name])
config[config_name].init_app(app)
bootstrap.init_app(app)
mail.init_app(app)
moment.init_app(app)
db.init_app(app)
login_manager.init_app(app)
#分别导入蓝图,并注册
from .main import main as main_blueprint
app.register_blueprint(main_blueprint)
from .auth import auth as auth_blueprint
app.register_blueprint(auth_blueprint, url_prefix='/auth')
return app
6.2、数据库模型(models):
from werkzeug.security import generate_password_hash, check_password_hash
from flask_login import UserMixin
###从flask_login导入UserMixin类
###USerMixin类包含的以上四种方法的默认实现。
from . import db, login_manager
###从程序的工厂函数引入login_manager实例
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
##导入生成令牌函数
from flask import current_app
class Role(db.Model):
'''
1 添加角色表、角色id、角色名
'''
__tablename__ = 'roles'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
users = db.relationship('User',backref='role',lazy='dynamic')
def __repr__(self):
return '' % self.name
class User(UserMixin, db.Model):
'''
1 User继承UserMixin和db.Model类的功能属性
2 建表users、id、email、username、role_id、password_hash(密码加密字段),confirmed(账户确认)
3 密码属性、加密密码
4 加载用户的回调函数接收以Unicode字符串形式表示的用户标示符
5 如果能找到用户,这个函数必须返回用户对象,否则返回None。
'''
__tablename__ = 'users'
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)
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
password_hash = db.Column(db.String(128))
confirmed = db.Column(db.Boolean,default=False)
@property
def password(self):
raise AttributeError('password is not a readable attribute')
@password.setter
def password(self,password):
self.password_hash = generate_password_hash(password)
def verify_password(self, password):
return check_password_hash(self.password_hash, password)
def __repr__(self):
return '' % self.username
#expiration为令牌失效时长,定义生成令牌函数
def generate_confirmation_token(self,expiration=3600):
'''
加密确认码
'''
s = Serializer(current_app.config['SECRET_KEY'],expiration)
return s.dumps({'confirm':self.id})
def confirm(self,token):
'''
解密确认码
'''
s = Serializer(current_app.config['SECRET_KEY'])
try:
data=s.loads(token)
except:
return False
if data.get('confirm') != self.id:
return False
self.confirmed = True
db.session.add(self)
return True
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
####### 6.3、用户文件(auth):(包含初始文件(创建蓝图)、forms文件(创建表单)、views文件(视图函数)).
6.3.1、用户初始化(创建蓝图):
from flask import Blueprint
auth = Blueprint('auth',__name__)
from . import views
6.3.2、forms文件(创建表单):
from flask_wtf import Form
from wtforms import StringField, PasswordField, BooleanField, SubmitField
from wtforms.validators import Required, Length, Email,Regexp,EqualTo
from wtforms import ValidationError
from ..models import User
表单login
class LoginForm(Form):
'''
1 登录表单。StringField构造函数中的可选参数validators指定一个有验证函数组成的列表,在接受用户提交的数据之前验证数据。
2 电子邮件字段用到了WTForms提供的Length()和Email()验证函数。
3 PasswordField类表示属性为type="password"的< input>元素。
4 BooleanField类表示复选框。
'''
email = StringField('Email', validators=[Required(), Length(1, 64), Email()])
password = PasswordField('密码', validators=[Required()])
remember_me = BooleanField('记住密码')
submit = SubmitField('登录')
#表单注册Registration
class RegistrationForm(Form):
'''
1 注册表单
2 填写表单时的格式限制(输入邮箱、输入用户名、输入密码、确认密码、注册按钮)
3 验证账号是否被注册过
4 验证用户名是否被注册过
'''
email = StringField('Email',validators=[Required(),Length(1,64),Email()])
username = StringField('用户名',validators=[Required(),Length(1,64),Regexp('^[A-Za-z][A-Za-z0-9_.]*$',\
0,'Usernames must have only letter,numbers,dots or underscores')])
password = PasswordField('密码',validators=[Required(),EqualTo('password2',message='两次密码必须一致.')])
password2 = PasswordField('确认密码',validators=[Required()])
submit = SubmitField('注册')
def validate_email(self,field):
if User.query.filter_by(email=field.data).first():
raise ValidationError('Email 已注册过.')
def validata_username(self,field):
if User.query.filter_by(username=field.data).first():
raise ValidationError('用户名已存在.')
6.3.3、views视图函数:
from flask import render_template, redirect, request, url_for, flash
from flask_login import login_user, logout_user, login_required
from . import auth
from ..models import User
from .forms import LoginForm
from .forms import RegistrationForm
from .. import db
from emailTest import send_email
from flask.ext.login import current_user
@auth.route('/login', methods=['GET', 'POST'])
###当请求为GET时,直接渲染模板,当请求是POST提交时,验证表格数据,然后尝试登入用户。
def login():
flash('账户或者密码不正确.')
'''
1 实例化表单
2 表格中填入了数据,执行下面操作
3 视图函数使用表单中填写的email加载用户
4 如果user不是空的,而且验证表格中的密码正确,执行下面的语句,调用Flask_Login中的login_user()函数,在用户会话中把用户标记为登录
5 否则直接执行flash消息和跳转到新表格中。
6 login_user函数的参数是要登录的用户,以及可选的‘记住我’布尔值。
7 用户访问未授权的url时会显示登录表单,Flask-Login会把原地址保存在查询字符串的next参数中,这个参数可从request.args字典中读取。如果查询字符串中没有next参数,则重定向到首页。
'''
form = LoginForm()
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('main.index'))
flash('账户或者密码不正确.')
return render_template('auth/login.html', form=form)
@auth.route('/logout')
###退出路由
@login_required
###用户要求已经登录
def logout():
'''
1 登出用户,这个视图函数调用logout_user()函数,删除并重设用户会话。
'''
logout_user()
flash('你已经退出登录!')
return redirect(url_for('main.index'))
###用户注册
@auth.route('/register',methods=['GET','POST'])
def register():
'''
1 实例化注册表单
2 判断是否提交表单,并对数据库进行操作,如果是则发送账户确认邮件给注册邮箱,且重定向到首页
'''
form = RegistrationForm()
if form.validate_on_submit():
user = User(email=form.email.data,username=form.username.data,password=form.password.data)
db.session.add(user)
db.session.commit()
token = user.generate_confirmation_token()
send_email(user.email,'确认你的账户','auth/email/confirm',user,token)
flash('账号验证已发送邮箱!')
return redirect(url_for('main.index'))
# return redirect(url_for('auth.login'))
##重定向到登录页面
return render_template('auth/register.html',form=form)
#确认账户
@auth.route('/confirm/')
@login_required
def confirm(token):
'''
1 判断数据库的confirmed字段是否为True,并以flash消息提示账户是否已确认,并重定向到首页
'''
if current_user.confirmed:
return redirect(url_for('main.index'))
if current_user.confirm(token):
flash('你已经确认了你的账户,谢谢')
else:
flash('这个确认链接不可用,或已超时')
return redirect(url_for('main.index'))
@auth.before_app_request
def before_request():
'''
如果返回响应或重定向,会直接发送至客户端,不会调用请求视图函数
'''
if current_user.is_authenticated \
and not current_user.confirmed \
and request.endpoint[:5] != 'auth.'\
and request.endpoint != 'static':
return redirect(url_for('auth.unconfirmed'))
#尚未确认账户
@auth.route('/unconfirmed')
def unconfirmed():
'''
判断数据库的confirmed字段是否为True,如果为False(0),就跳转到提示确认账户界面
'''
if current_user.is_anonymous or current_user.confirmed:
return redirect(url_for('main.index'))
return render_template('auth/unconfirmed.html')
#重新发送账户确认邮件
@auth.route('/confirm')
@login_required
def resend_confirmation():
token = current_user.generate_confirmation_token()
send_email(current_user.email, '确认你的账户',
'auth/email/confirm', current_user, token)
flash('新确认账户邮件已发送到邮箱,注意查收.')
return redirect(url_for('main.index'))
6.4、main文件:(初始文件也是蓝图创建)、views视图函数.
6.4.1、初始函数:
from flask import Blueprint
main = Blueprint('main', __name__)
from .views import *
6.4.2、视图函数:
from . import main
from .. import auth
from flask import render_template, request
from app import db
from sqlalchemy import or_
from flask import redirect, url_for
@main.route('/')
def index():
# return render_template('index.html')
return redirect(url_for('auth.login'))
@main.route('/index')
def index1():
return render_template('index.html')
7.静态样式文件:(主要包括样式(css、js等))
8.静态模板文件:(主要是HTML模板)
最后,根据蓝图,视图函数,把各个模块以及模板串在一起,这样吃能实现点击注册是,将表单添加的资料写入数据库,并同时发送邮箱验证(初始是否验证字段在数据库的默认值为0(Flase),当用户点击发送到自己的邮箱的验证链接,并更改数据的字段为1(True),还有就是秘密加密,flask有一个模块对于加密数据很方便,就是itsdangerous包里的TimedJSONWebSignatureSerializer可以对字段进行加密解密。对于账户、用户名是否已被用过,就是把表单输入的内容跟数据库中字段最匹配,看是否已存在。