这个前提的话我们需要两个模块,即pymysql
和sqlalchemy
,前者用于连接mysql,后者是他提供了一种名为ORM
的技术,使得我们不用通过Mysql的原生指令来执行命令,而是通过Python操作普通对象似的执行mysql语句。
主入口文件app.py
内容如下
from flask import Flask #从Flask包中调用flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
#在app.config中设置连接数据库的信息
#使用SQLALchemy(app)创建一个数据库对象
#SQLALchemy会自动读取app.config中连接数据库的信息
#Mysql的主机名
HOSTNAME = "127.0.0.1"
#Mysql的端口号,默认3306
PORT = 3306
#连接Mysql的用户名
USERNAME = "root"
#连接Mysql的密码
PASSWORD = "root"
#Mysql上创建的数据库名称
DATABASE = "flask"
app.config['SQLALCHEMY_DATABASE_URI'] = f"mysql+pymysql://{USERNAME}:{PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}?charset=utf8mb4"
db = SQLAlchemy(app)
with app.app_context():
with db.engine.connect() as conn:
rs = conn.execute("select 1")#当连接成功时就执行select 1 语句
print(rs.fetchone())
@app.route('/')
def hello_world():
return "Hello"
if __name__ == '__main__': #如果当前文件为主入口,就会往下运行
app.run()
接下来在Navicat
中创建一个数据库,命名为flask
接下来启动项目
可以发现结果为1,与select 1
对应,说明连接成功
ORM,即对象关系映射
。
一个ORM模型对应数据库中的一个表,ORM模型
中的每个类属性分别对应表的每个字段,ORM模型
中的每个类属性分别对应表的每个字段,ORM模型
的每个实例对象对应表中每条记录。
接下来说一下创建数据表,然后设置字段即标明主键的实现方式,主入口文件app.py
内容如下
from flask import Flask #从Flask包中调用flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
#在app.config中设置连接数据库的信息
#使用SQLALchemy(app)创建一个数据库对象
#SQLALchemy会自动读取app.config中连接数据库的信息
#Mysql的主机名
HOSTNAME = "127.0.0.1"
#Mysql的端口号,默认3306
PORT = 3306
#连接Mysql的用户名
USERNAME = "root"
#连接Mysql的密码
PASSWORD = "root"
#Mysql上创建的数据库名称
DATABASE = "flask"
app.config['SQLALCHEMY_DATABASE_URI'] = f"mysql+pymysql://{USERNAME}:{PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}?charset=utf8mb4"
db = SQLAlchemy(app)
class User(db.Model):
__tablename__ = "user" #表名
id = db.Column(db.Integer,primary_key=True,autoincrement=True) #字段 ,Integer为整型,Primary_key=True为主键
username = db.Column(db.String(100),nullable=False)
password = db.Column(db.String(100),nullable=False)
#user = User(username="赵四",password='6666') #导入至数据库中
# sql: insert user(username,password) values("赵四",'6666')
with app.app_context():
db.create_all() #将表映射至数据库中
@app.route('/')
def hello_world():
return "Hello"
if __name__ == '__main__': #如果当前文件为主入口,就会往下运行
app.run()
接下来运行文件
在navicat
中可以发现成功创建了user
表,且其内含有三个字段,id
、username
和password
首先说一下增,大致就是三步
1、创建ORM对象 2、将对象添加到db.session中 3、将db.session中的改变同步于数据库中
具体代码如下所示
from flask import Flask #从Flask包中调用flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
#在app.config中设置连接数据库的信息
#使用SQLALchemy(app)创建一个数据库对象
#SQLALchemy会自动读取app.config中连接数据库的信息
#Mysql的主机名
HOSTNAME = "127.0.0.1"
#Mysql的端口号,默认3306
PORT = 3306
#连接Mysql的用户名
USERNAME = "root"
#连接Mysql的密码
PASSWORD = "root"
#Mysql上创建的数据库名称
DATABASE = "flask"
app.config['SQLALCHEMY_DATABASE_URI'] = f"mysql+pymysql://{USERNAME}:{PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}?charset=utf8mb4"
db = SQLAlchemy(app)
class User(db.Model):
__tablename__ = "user" #表名
id = db.Column(db.Integer,primary_key=True,autoincrement=True) #字段 ,Integer为整型,Primary_key=True为主键
username = db.Column(db.String(100),nullable=False)
password = db.Column(db.String(100),nullable=False)
with app.app_context():
db.create_all() #将表映射至数据库中
@app.route('/')
def hello_world():
return "Hello"
@app.route('/user/add')
def add_user():
# 1、创建ORM对象
# 2、将对象添加到db.session中
# 3、将db.session中的改变同步于数据库中
user = User(username="赵四",password='6666')
db.session.add(user)
db.session.commit()
return "用户创建成功"
if __name__ == '__main__': #如果当前文件为主入口,就会往下运行
app.run()
接下来去访问/user/add
路由
接下来去Navicat
中查看user
表
成功添加
具体查的方法有两种
1、get查找:根据主键查找
2、filter_by查找
第一种方法一次只能查一条数据,所以我们常用的是第二个,接下来使用具体代码来进行演示。
app.py
内容如下
from flask import Flask #从Flask包中调用flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
#在app.config中设置连接数据库的信息
#使用SQLALchemy(app)创建一个数据库对象
#SQLALchemy会自动读取app.config中连接数据库的信息
#Mysql的主机名
HOSTNAME = "127.0.0.1"
#Mysql的端口号,默认3306
PORT = 3306
#连接Mysql的用户名
USERNAME = "root"
#连接Mysql的密码
PASSWORD = "root"
#Mysql上创建的数据库名称
DATABASE = "flask"
app.config['SQLALCHEMY_DATABASE_URI'] = f"mysql+pymysql://{USERNAME}:{PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}?charset=utf8mb4"
db = SQLAlchemy(app)
class User(db.Model):
__tablename__ = "user" #表名
id = db.Column(db.Integer,primary_key=True,autoincrement=True) #字段 ,Integer为整型,Primary_key=True为主键
username = db.Column(db.String(100),nullable=False)
password = db.Column(db.String(100),nullable=False)
#user = User(id=1,username="quan9i",password='6666') #导入至数据库中
# sql: insert user(username,password) values("赵四",'6666')
with app.app_context():
db.create_all() #将表映射至数据库中
@app.route('/')
def hello_world():
return "Hello"
@app.route('/user/query')
def query_user():
#1、get查找:根据主键查找
#user =User.query.get(1)
#print(f"{user.id}:{user.username}/{user.password}")
#2、filter_by查找
users = User.query.filter_by(username="赵四")
#print(type(users))
for user in users:
print(user.username)
return "数据查找成功"
if __name__ == '__main__': #如果当前文件为主入口,就会往下运行
app.run()
接下来访问/user/query
路由
而后我们可以发现此时已经有回显了
成功查找
具体步骤就是三步
1、查到具体的数据
2、对数据进行修改
3、数据同步
具体app.py
内容如下
from flask import Flask #从Flask包中调用flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
#在app.config中设置连接数据库的信息
#使用SQLALchemy(app)创建一个数据库对象
#SQLALchemy会自动读取app.config中连接数据库的信息
#Mysql的主机名
HOSTNAME = "127.0.0.1"
#Mysql的端口号,默认3306
PORT = 3306
#连接Mysql的用户名
USERNAME = "root"
#连接Mysql的密码
PASSWORD = "root"
#Mysql上创建的数据库名称
DATABASE = "flask"
app.config['SQLALCHEMY_DATABASE_URI'] = f"mysql+pymysql://{USERNAME}:{PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}?charset=utf8mb4"
db = SQLAlchemy(app)
class User(db.Model):
__tablename__ = "user" #表名
id = db.Column(db.Integer,primary_key=True,autoincrement=True) #字段 ,Integer为整型,Primary_key=True为主键
username = db.Column(db.String(100),nullable=False)
password = db.Column(db.String(100),nullable=False)
#user = User(id=1,username="quan9i",password='6666') #导入至数据库中
# sql: insert user(username,password) values("赵四",'6666')
with app.app_context():
db.create_all() #将表映射至数据库中
@app.route('/')
def hello_world():
return "Hello"
@app.route('/user/update')
def update_user():
user = User.query.filter_by(username="赵四").first()#first()也可以替换为[0],get(1)也可以
user.password = "2222"
db.session.commit()
return "数据修改成功"
if __name__ == '__main__': #如果当前文件为主入口,就会往下运行
app.run()
接下来访问/user/update
路由
查看Navicat
可以发现成功修改
具体步骤也是三步
1、查找数据
2、通过db.session
删除数据
3、同步到数据库
具体代码如下
from flask import Flask #从Flask包中调用flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
#在app.config中设置连接数据库的信息
#使用SQLALchemy(app)创建一个数据库对象
#SQLALchemy会自动读取app.config中连接数据库的信息
#Mysql的主机名
HOSTNAME = "127.0.0.1"
#Mysql的端口号,默认3306
PORT = 3306
#连接Mysql的用户名
USERNAME = "root"
#连接Mysql的密码
PASSWORD = "root"
#Mysql上创建的数据库名称
DATABASE = "flask"
app.config['SQLALCHEMY_DATABASE_URI'] = f"mysql+pymysql://{USERNAME}:{PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}?charset=utf8mb4"
db = SQLAlchemy(app)
class User(db.Model):
__tablename__ = "user" #表名
id = db.Column(db.Integer,primary_key=True,autoincrement=True) #字段 ,Integer为整型,Primary_key=True为主键
username = db.Column(db.String(100),nullable=False)
password = db.Column(db.String(100),nullable=False)
#user = User(id=1,username="quan9i",password='6666') #导入至数据库中
# sql: insert user(username,password) values("赵四",'6666')
with app.app_context():
db.create_all() #将表映射至数据库中
@app.route('/')
def hello_world():
return "Hello"
@app.route('/user/delete')
def delete_user():
user = User.query.get(1)
db.session.delete(user)
db.session.commit()
return "数据删除成功"
if __name__ == '__main__': #如果当前文件为主入口,就会往下运行
app.run()
接下来查看Navicat
关系型数据库中有一个强大的功能,即多个表之间可以建立关系。比如文章表中,需要存储作者数据,我们这里不需要把作者数据放入文章表中,通过外键引用用户表即可。接下来说一下具体实现
首先我们在前的基础上创建一个article
表,具体代码如下
class Article(db.Model)
__tablename__ = "article"
id = db.Column(db.Integer,primary_key=True,autoincrement=True)
title = db.Column(db.String(200),nullable=False)
content = db.Column(db.Text,nullable=False)
#添加作者的外键
author_id = db.Column()
article = Article(title="Flask从0到0.1",content="Flask")
接下来介绍一下relationship
,它其实就是自动查找了一下
author = db.relationship("User") #此时它会自动寻找User表中有关此外键的信息
#操作类似于 article.author = user.query.get(article.author_id)
接下来我们是可以通过这个来访问到author
,但如果我们想获取某作者所有的文章,该如何进行操作呢,具体代码如下
author = db.relationship("User",backref="articles")
#backref会自动给User模型添加一个article属性,用来获取文章列表
综上,整体代码如下
from flask import Flask #从Flask包中调用flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
#在app.config中设置连接数据库的信息
#使用SQLALchemy(app)创建一个数据库对象
#SQLALchemy会自动读取app.config中连接数据库的信息
#Mysql的主机名
HOSTNAME = "127.0.0.1"
#Mysql的端口号,默认3306
PORT = 3306
#连接Mysql的用户名
USERNAME = "root"
#连接Mysql的密码
PASSWORD = "root"
#Mysql上创建的数据库名称
DATABASE = "flask"
app.config['SQLALCHEMY_DATABASE_URI'] = f"mysql+pymysql://{USERNAME}:{PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}?charset=utf8mb4"
db = SQLAlchemy(app)
class User(db.Model):
__tablename__ = "user" #表名
id = db.Column(db.Integer,primary_key=True,autoincrement=True) #字段 ,Integer为整型,Primary_key=True为主键
username = db.Column(db.String(100),nullable=False)
password = db.Column(db.String(100),nullable=False)
class Article(db.Model):
__tablename__ = "article"
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
title = db.Column(db.String(200), nullable=False)
content = db.Column(db.Text, nullable=False)
# 添加作者的外键
author_id = db.Column(db.Integer,db.ForeignKey("user.id"))
author = db.relationship("User",backref="articles")
article = Article(title="Flask学习大纲",content="Flaskxxxx")
#author = db.relationship("User") #此时它会自动寻找User表中有关此外键的信息
#操作类似于 article.author = user.query.get(article.author_id)
with app.app_context():
db.create_all() #将表映射至数据库中
@app.route('/')
def hello_world():
return "Hello"
if __name__ == '__main__': #如果当前文件为主入口,就会往下运行
app.run()
这里的话,用到了一个模块,migrate
,所以我们首先需要去下载一下这个模块,具体指令如下
pip install flask-migrate
ORM模型映射成表的三步
1、flask db init
只需执行一次
2、flask db migrate
识别ORM模型的改变,生成脚本
3、flask db upgrade
运行脚本,同步到数据库中
主入口文件app.py
内容如下
from flask import Flask #从Flask包中调用flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
# pip install flask-migrate
app = Flask(__name__)
#在app.config中设置连接数据库的信息
#使用SQLALchemy(app)创建一个数据库对象
#SQLALchemy会自动读取app.config中连接数据库的信息
#Mysql的主机名
HOSTNAME = "127.0.0.1"
#Mysql的端口号,默认3306
PORT = 3306
#连接Mysql的用户名
USERNAME = "root"
#连接Mysql的密码
PASSWORD = "root"
#Mysql上创建的数据库名称
DATABASE = "flask"
app.config['SQLALCHEMY_DATABASE_URI'] = f"mysql+pymysql://{USERNAME}:{PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}?charset=utf8mb4"
db = SQLAlchemy(app)
migrate = Migrate(app,db)
class User(db.Model):
__tablename__ = "user" #表名
id = db.Column(db.Integer,primary_key=True,autoincrement=True) #字段 ,Integer为整型,Primary_key=True为主键
username = db.Column(db.String(100),nullable=False)
password = db.Column(db.String(100),nullable=False)
email = db.Column(db.String(100))
class Article(db.Model):
__tablename__ = "article"
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
title = db.Column(db.String(200), nullable=False)
content = db.Column(db.Text, nullable=False)
# 添加作者的外键
author_id = db.Column(db.Integer,db.ForeignKey("user.id"))
author = db.relationship("User",backref="articles")
article = Article(title="Flask学习大纲",content="Flaskxxxx")
#author = db.relationship("User") #此时它会自动寻找User表中有关此外键的信息
#操作类似于 article.author = user.query.get(article.author_id)
with app.app_context():
db.create_all() #将表映射至数据库中
@app.route('/')
def hello_world():
return "Hello"
if __name__ == '__main__': #如果当前文件为主入口,就会往下运行
app.run()
首先执行flask db init
,而后会生成
接下来执行flask db migrate
和flask db upgrade
接下来查看数据库,这个alembic_version
用于查看版本
接下来点击设计表
这里可以发现我们的email
字段已经写入
前期准备,大致思路如下
我们的数据库连接这种,如果每次都写语句连接则过于繁琐,这个我们把它写进一个配置文件,然后我们进行引用,连接数据库即可,所以这就需要第一个文件,config.py
,第二个,则是我们需要一个模块文件models.py
,用来存放ORM模型
,第三个呢,是我们需要的一些拓展的东西,比如SQLAlchemy
,我们可以把它放入一个扩展文件exts.py
中,这样引用比较方便,不过它的主要作用其实是为了避免循环引用的问题,具体如下
当没有exts.py
,我们的SQLAlchemy
这种就要写入到主文件app.py
中,而models.py
需要引用这个,所以就会通过from app import db
指向app.py
文件,但主文件app.py
也需要这个ORM
模型,所以它会通过from models import UserModel
指向models.py
文件。
他们的关系如下图
而此时就会造成一个循环引用的问题,这样引用超过一定次数就会出现bug,而当引用exts.py
后,它的关系如下所示
此时就完美解决了它的问题
然后呢,我们还需要一个蓝图文件夹,用于进行一个简单分类。比如豆瓣网,存在电影、读书、生活等多个方面,那我们就把电影作为蓝图文件夹下的一个文件,读书作为蓝图文件夹下的另一个文件,生活亦是如此。
那我们这里的话,比如我们想实现一个问答界面,它肯定是需要注册和登录的,所以我们这里就可以去创建两个文件,一个命名为auth.py
,用于搞用户注册和登录,另一个命名为qa
,用于搞问答。所以最终的大致情况如下所示
各文件内容如下
#app.py
from flask import Flask
import config
from exts import db
from models import UserModel
from blueprints.qa import bp as qa_bp
from blueprints.auth import bp as auth_bp
app = Flask(__name__)
#绑定配置文件
app.config.from_object(config)
db.init_app(app)
app.register_blueprint(qa_bp)
app.register_blueprint(auth_bp)
@app.route('/')
def hello_world(): # put application's code here
return 'Hello World!'
if __name__ == '__main__':
app.run()
#exts.py
#该文件存在是为了解决循环引用的问题
# flask-sqlalchemy
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
#models.py
from exts import db
class UserModel(db.Model):
pass
#auth.py
from flask import Blueprint
bp= Blueprint("auth",__name__,url_prefix="/auth")
@bp.route("/login")
def login():
pass
#qa.py
from flask import Blueprint
bp = Blueprint("qa",__name__,url_prefix="/")
@bp.route("/")
def index():
pass
首先我们需要配置连接数据库,这里我们新建一个数据库,命名为demo
然后呢,我们需要去配置一下这个config.py
文件,使得它可以与数据库实现连接
#Mysql的主机名
HOSTNAME = "127.0.0.1"
#Mysql的端口号,默认3306
PORT = '3306'
#连接Mysql的用户名
USERNAME = "root"
#连接Mysql的密码
PASSWORD = "root"
#Mysql上创建的数据库名称
DATABASE = "demo"
DB_URI = f"mysql+pymysql://{USERNAME}:{PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}?charset=utf8mb4"
SQLALCHEMY_DATABASE_URI = DB_URI
接下来的话,配置一下模型文件,即models.py
,对于这个,需要新添一些字段,比如email
用于注册,join_time
用于显示注册时间。
from exts import db
from datetime import datetime
class UserModel(db.Model):
__tablename__ = "user"
id = db.Column(db.Integer,primary_key=True,autoincrement=True)
username = db.Column(db.String(100),nullable=True)
password = db.Column(db.String(100), nullable=True)
email = db.Column(db.String(100), nullable=True,unique=True)
join_time = db.Column(db.DateTime, default=datetime.now)
然后我们需要在app.py
中进行Migrate
的简单配置,具体文件如下
from flask import Flask
import config
from exts import db
from models import UserModel
from blueprints.qa import bp as qa_bp
from blueprints.auth import bp as auth_bp
from flask_migrate import Migrate
app = Flask(__name__)
#绑定配置文件
app.config.from_object(config)
db.init_app(app)
migrate = Migrate(app,db)
app.register_blueprint(qa_bp)
app.register_blueprint(auth_bp)
@app.route('/')
def hello_world(): # put application's code here
return 'Hello World!'
if __name__ == '__main__':
app.run()
接下来去执行db
三部曲即可
1、flask db init
2、flask db migrate
3、flask db upgrate
此时再查看数据库
user
表已建立成功。
接下来首先我们需要使用一个HTML+CSS+JS
模板,这里我使用的是这个https://blog.csdn.net/weixin_46048542/article/details/127968514
接下来将index.html
界面上传到templates
文件夹下,css
和js
文件则上传到static
文件夹下
接下来修改auth.py
文件,对内容进行一个渲染
from flask import Blueprint,render_template
bp= Blueprint("auth",__name__,url_prefix="/auth")
@bp.route("/login")
def login():
pass
@bp.route("/register")
def register():
return render_template("index.html")
此时运行程序,访问/auth/register
路由
此时发现这个没有美化,这是为什么呢,这是因为html
中加载css
的方式是绝对路径的方式
而在Flask
这里,我们应该使用的是{{url_for}}
这种方式,所以需要对它进行简单更改,修改为如下方式
<link rel="stylesheet" href="{{ url_for('static',filename='css/style.css') }}">
此时再次进行访问
可以发现界面一样,但这个太简单了,只有一个index.html
,没有办法去写继承什么的,所以我又找了一个https://github.com/Yourdaylight/javaWeb-Study
下载后我们做同上操作即可,然后我们就可以开始继承了
可以发现上层和底部,都是一样的,所以我们可以写入一个base.html
,然后让其他html界面引用,实现一个继承,具体代码如下所示
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}{% endblock %}</title>
{%block head%}{% endblock %}
<link rel="stylesheet" href="{{ url_for('static',filename='css/regist.css')}}">
<link rel="shortcut icon" href="{{ url_for('static',filename='images/camera.ico')}}">
<script type="text/javascript" src="{{ url_for('static',filename='js/regist.js')}}"></script>
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
</head>
<body>
{% block body %}{% endblock %}
<div id="footer">
<div class="link">
<a href="">关于我们</a>
|
<a href="">联系我们</a>
|
<a href="">商家入驻</a>
|
<a href="">友情链接</a>
|
</div>
<div class="copyright">
Copyright © 1999 - 2020 LZH. All Rights Reserved.
</div>
<script type="text/javascript" src="{{ url_for('static',filename='js/regist.js')}}"></script>
</body>
</html>
而接下来去写register.html
界面
{% extends "base.html" %}
{% block tile %}注册{% endblock %}
{% block head %}{% endblock %}
{% block body %}
<!--头部-->
<div id="reg_header">
<div class="reg_h_center">
<div class="reg_h_left">
<img src="{{ url_for('static',filename='images/logo.png')}}" alt="">
<h3>欢迎注册</h3>
</div>
</div>
</div>
<!--表单内容-->
<div class="reg_back">
<div class="reg_left">
<p>新用户注册</p>
<p>USER REGISTER</p>
</div>
<!-- 表单列表-->
<div class="reg_center">
<div class="reg_form">
<form action="#" method="post" id="form">
<table>
<tr>
<td class="td_left"><label for="username">用户名</label></td>
<td class="td_right"><input type="text" name="username" placeholder="请输入用户名"
id="username">
<span id="s_username" class="error"></span>
</td>
</tr>
<tr>
<td class="td_left"><label for="password">密码</label></td>
<td class="td_right"><input type="password" name="password" placeholder="请输入密码(请设置6-12位字符)"
id="password">
<span id="s_password" class="error"></span>
</td>
</tr>
<tr>
<td class="td_left"><label for="repassword">确认密码</label></td>
<td class="td_right"><input type="password" name="repassword" placeholder="请输入密码"
id="repassword">
<span id="s_repassword" class="error"></span>
</td>
</tr>
<tr>
<td class="td_left"><label for="email">Email</label></td>
<td class="td_right"><input type="email" name="email" placeholder="请输入Email" id="Email">
<span id="s_email" class="error"></span>
</td>
</tr>
<tr>
<td class="td_left"><label for="rename">姓名</label></td>
<td class="td_right"><input type="text" name="rename" placeholder="请输入真实姓名" id="rename">
<span id="s_rename" class="error"></span>
</td>
</tr>
<tr>
<td class="td_left"><label for="Telphone">手机号</label></td>
<td class="td_right"><input type="text" name="telphone" placeholder="请输入您的手机号"
id="Telphone">
<span id="s_telphone" class="error"></span>
</td>
</tr>
<tr>
<td class="td_left"><label>性别</label></td>
<td class="td_right"><input type="radio" name="gender" value="male" checked> 男
<input type="radio" name="gender" value="female"> 女
</td>
</tr>
<tr>
<td class="td_left"><label for="Birthday">生日(选填)</label></td>
<td class="td_right"><input type="date" name="birthday" id="Birthday"></td>
</tr>
<tr>
<td class="td_left"><label for="checkcode">验证码</label></td>
<td class="td_right">
<div class="all">
<input type="text" name="checkcode" id="checkcode">
<div class="code"></div>
<a href="#" class="change">看不清,换一张</a>
</div>
</td>
</tr>
</table>
<tr>
<td colspan="2" align="center">
<button type="button" value="注册" id="btn_sub" onclick="submitForm()">注册</button>
</td>
<td colspan="2" align="center">
<button id="reset" value="重置" >重置</button>
</td>
</tr>
</form>
</div>
</div>
<div class="reg_right">
<p>已有账号?<a href="login.html">立即登录</a></p>
</div>
{% endblock %}
与静态界面相似,成功加载。
对于注册,我们肯定需要验证,验证码是否输入正确,用户名和密码是否符号长度规则,密码是否正确,那么它如何实现呢,这里用一个名为flask-wtf
的模块来进行实现,所以首先我们需要去安装一下这个模块,在终端执行pip install flask-wtf
即可
而后我们在根目录下新建一个文件,这里命名为form.py
,其内容如下
#forms.py
import wtforms
from wtforms.validators import Email,Length,EqualTo
from models import UserModel ,EmailCaptchaModel
from models import db
class RegisterFrom(wtforms.Form)
email = wtforms.StringField(validators=[Email(message="邮箱格式错误")])
captcha = wtforms.StringField(validators=[Length(min=4,max=4,message="验证码格式错误!")])
username = wtforms.StringField(validators=[Length(min=3,max=20,message="用户名格式错误!")])
password = wtforms.StringField(validators=[Length(min=6,max=20,message="密码格式错误!")])
password_confirm = wtforms.StringField(validators=[EqualTo("password")])
# 自定义验证
#1、邮箱是否被注册
#2、验证码是否正确
def validate_email(self,filed):
email = filed.data
user = UserModel.query.filter_by(email=email).first()
if user:
raise wtforms.ValidationError(message="该邮箱已被注册!")
def validate_captcha(self,filed):
captcha = filed.data
email = self.email.data
capthcha_model = EmailCaptchaModel.query.filter_by(email=email,captcha=captcha).first()
if not capthcha_model:
raise wtfroms.ValidationError(message="邮箱或验证码错误!")
else:
# todo: 可以删掉captcha_model
db.session.delete(capthcha_model)
db.session.commit()
最后的那个删除验证码的是为了减轻服务器负担,但它与数据库交互过多是不太好的,所以else
那里可写可不写,当写else
语句时,models.py
中需要添加一个字段,如下所示
# models.py
from exts import db
from datetime import datetime
class UserModel(db.Model):
__tablename__ = "user"
id = db.Column(db.Integer,primary_key=True,autoincrement=True)
username = db.Column(db.String(100),nullable=True)
password = db.Column(db.String(100), nullable=True)
email = db.Column(db.String(100), nullable=True,unique=True)
join_time = db.Column(db.DateTime, default=datetime.now)
class EmailcaptchaModel(db.Model)
__table__ = "email_captcha"
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
email = db.Column(db.String(100), nullable=False)
captcha = db.Column(db.String(100),nullable=False)
#used = db.Column(db.Boolean,default=False)
这个used
字段用来校验验证码是否被用过,如果用过则进行一个删除。然后接下来对于登录和注册界面的话,我们还需要进行一个参数的获取,即提交的内容,我们该如何获取呢,我们这里访问这个界面,需要渲染模板,我们还需要获取数据,因此这里考虑到使用GET
方法和POST
方法来进行区分,当使用GET
方法时,那它就是对模板进行一个渲染,而当使用POST
方法时,它则是为了获取数据,其具体内容如下所示
#auth.py
import random
from flask import Blueprint, render_template, request
from .forms import RegisterForm
bp = Blueprint("auth",__name__,url_prefix="/auth")
@bp.route("/login")
def login():
pass
@bp.route("/register",methods=['GET','POST'])
def register():
if request.method == 'GET':
return render_template("register.html")
else:
form = RegisterForm(request.form)
if form.validate():
return "success"
else:
print(form.errors)
return "fail"
其他文件的内容也简单列一下
#app.py
from flask import Flask
import config
from exts import db
from models import UserModel
from blueprints.qa import bp as qa_bp
from blueprints.auth import bp as auth_bp
from flask_migrate import Migrate
app = Flask(__name__)
#绑定配置文件
app.config.from_object(config)
db.init_app(app)
migrate = Migrate(app,db)
app.register_blueprint(qa_bp)
app.register_blueprint(auth_bp)
@app.route('/')
def hello_world(): # put application's code here
return 'Hello World!'
if __name__ == '__main__':
app.run()
#qa.py
from flask import Blueprint
bp = Blueprint("qa",__name__,url_prefix="/")
@bp.route("/")
def index():
pass
而后运行程序,访问auth/register
路由
此时可以发现它是已经存在一个校验了,当我们输入的密码不是6-20
位时,就会提示密码格式有误
,其他亦是如此,不过这里我是没有写具体的验证码的那些发送及实现过程的,所以后续它还用到了验证码的表
,由于这些比较繁琐,所以我直接将有关验证码的进行了删除。最终修改部分代码如下
#forms.py
import wtforms
from wtforms.validators import Email,Length,EqualTo
from models import UserModel,EmailCaptchaModel
from exts import db
class RegisterForm(wtforms.Form):
email = wtforms.StringField(validators=[Email(message="邮箱格式错误")])
username = wtforms.StringField(validators=[Length(min=3,max=20,message="用户名格式错误!")])
password = wtforms.StringField(validators=[Length(min=6,max=20,message="密码格式错误!")])
# 自定义验证
#1、邮箱是否被注册
#2、验证码是否正确
def validate_email(self,filed):
email = filed.data
user = UserModel.query.filter_by(email=email).first()
if user:
raise wtforms.ValidationError(message="该邮箱已被注册!")
提交后
接下来我们把他写入到数据库中即可,我们这里通过修改auth.py
来实现,这里的话还有一个就是对密码进行了一个加密,因为明文的话容易造成用户隐私泄露。修改后的auth.py
内容如下
#auth.py
from exts import db
from flask import Blueprint, render_template, request,redirect,url_for
from .forms import RegisterForm
from models import UserModel
from werkzeug.security import generate_password_hash
bp = Blueprint("auth",__name__,url_prefix="/auth")
@bp.route("/login")
def login():
return "这是登录界面!"
@bp.route("/register",methods=['GET','POST'])
def register():
if request.method == 'GET':
return render_template("register.html")
else:
form = RegisterForm(request.form)
if form.validate():
email = form.email.data
username = form.username.data
password = form.password.data
user = UserModel(email=email,username=username,password=generate_password_hash(password))
db.session.add(user)
db.session.commit()
return redirect(url_for("auth.login"))
else:
print(form.errors)
return redirect(url_for("auth.register"))
接下来去尝试进行一个注册
然后进行注册,此时回显注册成功,再去Navicat
中查看数据表
成功注册。
此时的注册界面就算完成
类似之前的注册界面,我们需要将顶部和尾部相同的部分,通过Jinjia2
模板使用父模板的即可,只保存中间部分的。
源码如下
#login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
<link rel="shortcut icon" href="images/camera.ico">
<link rel="stylesheet" href="css/login.css">
<link rel="stylesheet" href="css/footer.css">
<script type="text/javascript" src="js/regist.js"></script>
</head>
<body>
<!--头部-->
<div id="header">
<!--头部中间信息-->
<div class="h_center">
<img src="images/logo.png" alt="">
<p>欢迎来到未命名图库:请先登录!</p>
</div>
</div>
<!--中部-->
<div id="login_body">
<div class="login_b_center">
<div class="login_bg">
<h1>密码登录</h1>
<form action="#" id="login">
<!-- //用户名-->
<div class="userName">
<span></span><input type="text">
</div>
<!-- //密码-->
<div class="password">
<span></span><input type="password">
</div>
<!-- //登录按钮-->
<div class="login_btn">
<a href="index.html">
<input type="button" value="登录">
</a>
<td colspan="2" align="center"><button id="reset" value="重置" onclick="remove()">取消</button></td>
</div>
<div class="forgot_password">
<a href="">忘记密码</a>
<a href="regist.html">注册账号</a>
<a href="regist.html">帮助</a>
</div>
</form>
</div>
</div>
</div>
<!--尾部-->
<div id="footer">
<div class="link">
<a href="">关于我们</a>
|
<a href="">联系我们</a>
|
<a href="">商家入驻</a>
|
<a href="">友情链接</a>
|
</div>
<div class="copyright">
Copyright © 1999 - 2020 LZH. All Rights Reserved.
</div>
</div>
</body>
</html>
改动后如下
{% extends "base.html" %}
{% block title %}登录界面{% endblock %}
{% block head %}
<link rel="stylesheet" href="{{ url_for('static',filename='css/login.css') }}">
<link rel="stylesheet" href="{{ url_for('static',filename='css/footer.css') }}">
{% endblock %}
{% block body %}
<!--头部-->
<div id="header">
<!--头部中间信息-->
<div class="h_center">
<img src="{{ url_for('static',filename='images/logo.png') }}" alt="">
<p>欢迎来到未命名图库:请先登录!</p>
</div>
</div>
<!--中部-->
<div id="login_body">
<div class="login_b_center">
<div class="login_bg">
<h1>密码登录</h1>
<form action="#" id="login">
<!-- //用户名-->
<div class="userName">
<span></span><input type="text">
</div>
<!-- //密码-->
<div class="password">
<span></span><input type="password">
</div>
<!-- //登录按钮-->
<div class="login_btn">
<a href="index.html">
<input type="button" value="登录">
</a>
<td colspan="2" align="center"><button id="reset" value="重置" onclick="remove()">取消</button></td>
</div>
<div class="forgot_password">
<a href="">忘记密码</a>
<a href="regist.html">注册账号</a>
<a href="regist.html">帮助</a>
</div>
</form>
</div>
</div>
</div>
{% endblock %}
而后我们再更改一下auth.py
中注册成功的那个代码,让他跳转到登录界面
具体代码如下
#auth.py
from exts import db
from flask import Blueprint, render_template, request,redirect,url_for
from .forms import RegisterForm
from models import UserModel
from werkzeug.security import generate_password_hash
bp = Blueprint("auth",__name__,url_prefix="/auth")
@bp.route("/login")
def login():
return render_template("login.html")
@bp.route("/register",methods=['GET','POST'])
def register():
if request.method == 'GET':
return render_template("register.html")
else:
form = RegisterForm(request.form)
if form.validate():
email = form.email.data
username = form.username.data
password = form.password.data
user = UserModel(email=email,username=username,password=generate_password_hash(password))
db.session.add(user)
db.session.commit()
return redirect(url_for("auth.login"))
else:
print(form.errors)
return redirect(url_for("auth.register"))
此时注册完成后,可以发现直接跳转到了登录界面
这个的话,我们需要验证账号密码,所以首先我们需要在forms.py
写入校验用户和密码格式的,具体内容如下
import wtforms
from wtforms.validators import Email,Length,EqualTo
from models import UserModel,EmailCaptchaModel
from exts import db
class RegisterForm(wtforms.Form):
email = wtforms.StringField(validators=[Email(message="邮箱格式错误")])
username = wtforms.StringField(validators=[Length(min=3,max=20,message="用户名格式错误!")])
password = wtforms.StringField(validators=[Length(min=6,max=20,message="密码格式错误!")])
# 自定义验证
#1、邮箱是否被注册
#2、验证码是否正确
def validate_email(self,filed):
email = filed.data
user = UserModel.query.filter_by(email=email).first()
if user:
raise wtforms.ValidationError(message="该邮箱已被注册!")
class Loginform(wtforms.Form):
username = wtforms.StringField(validators=[Length(min=3,max=20,message="用户名格式错误!")])
password = wtforms.StringField(validators=[Length(min=6,max=20,message="密码格式错误!")])
接下来去写这个auth.py
登录界面,我们如何判断登录成功,即通过在数据库中查询是否存在此账号,然后校验密码的方式,如果成功登录,则让它跳转到/目录下
,如何看它是否处于一个登录状态呢,这里就要用到Session
了,设置session
的话,我们是需要有一个SECRET_KEY
的,在config.py
中进行填写即可,如下图,瞎写就行。
具体auth.py
内容如下
from exts import db
from flask import Blueprint, render_template, request,redirect,url_for,session
from .forms import RegisterForm,Loginform
from models import UserModel
from werkzeug.security import generate_password_hash,check_password_hash
bp = Blueprint("auth",__name__,url_prefix="/auth")
@bp.route("/login",methods=['GET','POST'])
def login():
if request.method == 'GET':
return render_template("login.html")
else:
form = Loginform(request.form)
if form.validate():
username = form.username.data
password = form.password.data
user = UserModel.query.filter_by(username=username).first()
if not user:
print("用户在数据库中不存在!")
return redirect(url_for("auth.login"))
if check_password_hash(user.password,password):
#cookie:
# Cookie中不适合存储太多数据,只适合存储少量数据
# cookie一般用来存放登录授权的东西
# flask中的session,是经过加密后存储在cookie中的
session['user_id'] = user.id
return redirect("/")
pass
else:
print("密码错误!")
return redirect(url_for("auth.login"))
else:
print(form.errors)
return redirect(url_for("auth.login"))
@bp.route("/register",methods=['GET','POST'])
def register():
if request.method == 'GET':
return render_template("register.html")
else:
form = RegisterForm(request.form)
if form.validate():
email = form.email.data
username = form.username.data
password = form.password.data
user = UserModel(email=email,username=username,password=generate_password_hash(password))
db.session.add(user)
db.session.commit()
return redirect(url_for("auth.login"))
else:
print(form.errors)
return redirect(url_for("auth.register"))
同时qa.py
是我们的根目录,所以需要对它进行简单设置,让它返回一个值即可
from flask import Blueprint
bp = Blueprint("qa",__name__,url_prefix="/")
@bp.route("/")
def index():
return "这是首页!"
接下来去登录,但我发现这个登录模板TMD有问题,怎么提交都是GET
,所以进行了简单修改,如下所示
{% extends "base.html" %}
{% block title %}登录界面{% endblock %}
{% block head %}
<link rel="stylesheet" href="{{ url_for('static',filename='css/login.css') }}">
<link rel="stylesheet" href="{{ url_for('static',filename='css/footer.css') }}">
{% endblock %}
{% block body %}
<!--头部-->
<div id="header">
<!--头部中间信息-->
<div class="h_center">
<img src="{{ url_for('static',filename='images/logo.png') }}" alt="">
<p>欢迎来到未命名图库:请先登录!</p>
</div>
</div>
<!--中部-->
<div id="login_body">
<div class="login_b_center">
<div class="login_bg">
<h1>密码登录</h1>
<form method="post" id="login">
<form method="post">
<!--input输入框标签,默认为text,文本框
name:为该输入框起一个名字,用来提交数据
-->
<p>用户名:<input type="text" name="username"/></p>
<!--密码框input type="password",密码框输入字符会显示为小圆点-->
<p>密码:<input type="password" name="password"/></p>
<!--submit提交
reset重置所有输入框
-->
<p><input type="submit"/>
<input type="reset"/></p>
</form>
</div>
</div>
</div>
{% endblock %}
接下来去尝试登录
可以发现此时是没有Cookie
的
登录后如下图
成功得到Session
在执行某个指令前,我们可以进行中间操作,比如一个人要去一个地方,这个人中间被进行了拦截,我们这个钩子函数就相当于此时的这个拦截作用。
比如我们在进行操作前想获取用户id
,此时我们就可以去拦截一下然后获取用户id
,将它放入某个变量中直接进行调用,而不需要再去数据库中进行查看,这样相比是比较方便的。常见的方法有before_request
和after_request
等。
同时,我们发现在登录后,点击每个界面,这种用户信息都是存在的,即用户名都在右上角写着,正常思路的话是每次都需要单独对这个渲染一次,而实际上我们其实是把它当成一个变量,每次访问都会把它置于上下文中,例如
@app.context_processor
def my_context_processor():
return {"user": g.user}
对于这个,我们只需要写一个销毁session的,即可实现。
@bp.route("/logout")
def logout():
session.clear()
return redirect("/")
但这个是视图中的,我们在实际html
代码中还需要有一个指向,比如现在有退出
按钮,我们需要修改它的href={{url_for('auth.logout')}}
。