前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Flask 从0到0.1 part-02

Flask 从0到0.1 part-02

作者头像
用户9691112
发布2023-05-18 13:53:44
1K0
发布2023-05-18 13:53:44
举报
文章被收录于专栏:quan9i的安全笔记

Flask

连接mysql数据库

这个前提的话我们需要两个模块,即pymysqlsqlalchemy,前者用于连接mysql,后者是他提供了一种名为ORM的技术,使得我们不用通过Mysql的原生指令来执行命令,而是通过Python操作普通对象似的执行mysql语句。

主入口文件app.py内容如下

代码语言:javascript
复制
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

image-20230314203203585
image-20230314203203585

接下来启动项目

image-20230314204031920
image-20230314204031920

可以发现结果为1,与select 1对应,说明连接成功

ORM模型

ORM,即对象关系映射

一个ORM模型对应数据库中的一个表,ORM模型中的每个类属性分别对应表的每个字段,ORM模型中的每个类属性分别对应表的每个字段,ORM模型的每个实例对象对应表中每条记录。

接下来说一下创建数据表,然后设置字段即标明主键的实现方式,主入口文件app.py内容如下

代码语言:javascript
复制
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()

接下来运行文件

image-20230314225608340
image-20230314225608340

navicat中可以发现成功创建了user表,且其内含有三个字段,idusernamepassword

增删改查

首先说一下增,大致就是三步

1、创建ORM对象 2、将对象添加到db.session中 3、将db.session中的改变同步于数据库中

具体代码如下所示

代码语言:javascript
复制
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路由

image-20230314230419275
image-20230314230419275

接下来去Navicat中查看user

image-20230314230459292
image-20230314230459292

成功添加

具体查的方法有两种

代码语言:javascript
复制
1、get查找:根据主键查找
2、filter_by查找

第一种方法一次只能查一条数据,所以我们常用的是第二个,接下来使用具体代码来进行演示。

app.py内容如下

代码语言:javascript
复制
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路由

image-20230314231455947
image-20230314231455947

而后我们可以发现此时已经有回显了

image-20230314231509918
image-20230314231509918

成功查找

具体步骤就是三步

1、查到具体的数据

2、对数据进行修改

3、数据同步

具体app.py内容如下

代码语言:javascript
复制
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路由

image-20230314232504763
image-20230314232504763

查看Navicat

image-20230314232516094
image-20230314232516094

可以发现成功修改

具体步骤也是三步

1、查找数据

2、通过db.session删除数据

3、同步到数据库

具体代码如下

代码语言:javascript
复制
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()
image-20230314234414668
image-20230314234414668

接下来查看Navicat

image-20230314234433395
image-20230314234433395
外键

关系型数据库中有一个强大的功能,即多个表之间可以建立关系。比如文章表中,需要存储作者数据,我们这里不需要把作者数据放入文章表中,通过外键引用用户表即可。接下来说一下具体实现

首先我们在前的基础上创建一个article表,具体代码如下

代码语言:javascript
复制
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,它其实就是自动查找了一下

代码语言:javascript
复制
author = db.relationship("User") #此时它会自动寻找User表中有关此外键的信息
#操作类似于  article.author = user.query.get(article.author_id)

接下来我们是可以通过这个来访问到author,但如果我们想获取某作者所有的文章,该如何进行操作呢,具体代码如下

代码语言:javascript
复制
author = db.relationship("User",backref="articles")
#backref会自动给User模型添加一个article属性,用来获取文章列表

综上,整体代码如下

代码语言:javascript
复制
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()
迁移ORM模型

这里的话,用到了一个模块,migrate,所以我们首先需要去下载一下这个模块,具体指令如下

代码语言:javascript
复制
pip install flask-migrate

ORM模型映射成表的三步

1、flask db init 只需执行一次

2、flask db migrate 识别ORM模型的改变,生成脚本

3、flask db upgrade 运行脚本,同步到数据库中

主入口文件app.py内容如下

代码语言:javascript
复制
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,而后会生成

image-20230315185010110
image-20230315185010110

接下来执行flask db migrateflask db upgrade

image-20230315184952140
image-20230315184952140

接下来查看数据库,这个alembic_version用于查看版本

image-20230315184628008
image-20230315184628008

接下来点击设计表

image-20230315184655904
image-20230315184655904

这里可以发现我们的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文件。

他们的关系如下图

image-20230315225245799
image-20230315225245799

而此时就会造成一个循环引用的问题,这样引用超过一定次数就会出现bug,而当引用exts.py后,它的关系如下所示

image-20230315225212103
image-20230315225212103

此时就完美解决了它的问题

然后呢,我们还需要一个蓝图文件夹,用于进行一个简单分类。比如豆瓣网,存在电影、读书、生活等多个方面,那我们就把电影作为蓝图文件夹下的一个文件,读书作为蓝图文件夹下的另一个文件,生活亦是如此。

那我们这里的话,比如我们想实现一个问答界面,它肯定是需要注册和登录的,所以我们这里就可以去创建两个文件,一个命名为auth.py,用于搞用户注册和登录,另一个命名为qa,用于搞问答。所以最终的大致情况如下所示

image-20230315230309813
image-20230315230309813

各文件内容如下

代码语言:javascript
复制
#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
User模型创建

首先我们需要配置连接数据库,这里我们新建一个数据库,命名为demo

image-20230315231000135
image-20230315231000135

然后呢,我们需要去配置一下这个config.py文件,使得它可以与数据库实现连接

代码语言:javascript
复制
#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用于显示注册时间。

代码语言:javascript
复制
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的简单配置,具体文件如下

代码语言:javascript
复制
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

此时再查看数据库

image-20230316000157705
image-20230316000157705

user表已建立成功。

注册界面设计

接下来首先我们需要使用一个HTML+CSS+JS模板,这里我使用的是这个https://blog.csdn.net/weixin_46048542/article/details/127968514

接下来将index.html界面上传到templates文件夹下,cssjs文件则上传到static文件夹下

image-20230316004701366
image-20230316004701366

接下来修改auth.py文件,对内容进行一个渲染

代码语言:javascript
复制
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路由

image-20230316004836691
image-20230316004836691

此时发现这个没有美化,这是为什么呢,这是因为html中加载css的方式是绝对路径的方式

image-20230316005000460
image-20230316005000460

而在Flask这里,我们应该使用的是{{url_for}}这种方式,所以需要对它进行简单更改,修改为如下方式

代码语言:javascript
复制
<link rel="stylesheet" href="{{ url_for('static',filename='css/style.css') }}">

此时再次进行访问

image-20230316005205898
image-20230316005205898

可以发现界面一样,但这个太简单了,只有一个index.html,没有办法去写继承什么的,所以我又找了一个https://github.com/Yourdaylight/javaWeb-Study

下载后我们做同上操作即可,然后我们就可以开始继承了

image-20230316013055619
image-20230316013055619
image-20230316013104118
image-20230316013104118

可以发现上层和底部,都是一样的,所以我们可以写入一个base.html,然后让其他html界面引用,实现一个继承,具体代码如下所示

代码语言:javascript
复制
<!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界面

代码语言:javascript
复制
{% 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 %}
image-20230316013242872
image-20230316013242872

与静态界面相似,成功加载。

注册界面验证

对于注册,我们肯定需要验证,验证码是否输入正确,用户名和密码是否符号长度规则,密码是否正确,那么它如何实现呢,这里用一个名为flask-wtf的模块来进行实现,所以首先我们需要去安装一下这个模块,在终端执行pip install flask-wtf即可

而后我们在根目录下新建一个文件,这里命名为form.py,其内容如下

代码语言:javascript
复制
#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中需要添加一个字段,如下所示

代码语言:javascript
复制
# 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方法时,它则是为了获取数据,其具体内容如下所示

代码语言:javascript
复制
#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"

其他文件的内容也简单列一下

代码语言:javascript
复制
#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路由

image-20230317003030428
image-20230317003030428

此时可以发现它是已经存在一个校验了,当我们输入的密码不是6-20位时,就会提示密码格式有误,其他亦是如此,不过这里我是没有写具体的验证码的那些发送及实现过程的,所以后续它还用到了验证码的表,由于这些比较繁琐,所以我直接将有关验证码的进行了删除。最终修改部分代码如下

代码语言:javascript
复制
#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="该邮箱已被注册!")
image-20230317010820826
image-20230317010820826

提交后

image-20230317010841993
image-20230317010841993

接下来我们把他写入到数据库中即可,我们这里通过修改auth.py来实现,这里的话还有一个就是对密码进行了一个加密,因为明文的话容易造成用户隐私泄露。修改后的auth.py内容如下

代码语言:javascript
复制
#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"))

接下来去尝试进行一个注册

image-20230317012740136
image-20230317012740136

然后进行注册,此时回显注册成功,再去Navicat中查看数据表

image-20230317013052475
image-20230317013052475

成功注册。

此时的注册界面就算完成

登录界面渲染

类似之前的注册界面,我们需要将顶部和尾部相同的部分,通过Jinjia2模板使用父模板的即可,只保存中间部分的。

源码如下

代码语言:javascript
复制
#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>

改动后如下

代码语言:javascript
复制
{% 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中注册成功的那个代码,让他跳转到登录界面

image-20230317014256642
image-20230317014256642

具体代码如下

代码语言:javascript
复制
#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"))

此时注册完成后,可以发现直接跳转到了登录界面

image-20230317014311829
image-20230317014311829
登录界面验证

这个的话,我们需要验证账号密码,所以首先我们需要在forms.py写入校验用户和密码格式的,具体内容如下

代码语言:javascript
复制
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中进行填写即可,如下图,瞎写就行。

image-20230317024245716
image-20230317024245716

具体auth.py内容如下

代码语言:javascript
复制
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是我们的根目录,所以需要对它进行简单设置,让它返回一个值即可

代码语言:javascript
复制
from flask import Blueprint

bp = Blueprint("qa",__name__,url_prefix="/")

@bp.route("/")
def index():
    return  "这是首页!"

接下来去登录,但我发现这个登录模板TMD有问题,怎么提交都是GET,所以进行了简单修改,如下所示

代码语言:javascript
复制
{% 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 %}

接下来去尝试登录

image-20230317024151557
image-20230317024151557

可以发现此时是没有Cookie

登录后如下图

image-20230317024126541
image-20230317024126541

成功得到Session

钩子函数

在执行某个指令前,我们可以进行中间操作,比如一个人要去一个地方,这个人中间被进行了拦截,我们这个钩子函数就相当于此时的这个拦截作用。

比如我们在进行操作前想获取用户id,此时我们就可以去拦截一下然后获取用户id,将它放入某个变量中直接进行调用,而不需要再去数据库中进行查看,这样相比是比较方便的。常见的方法有before_requestafter_request等。

同时,我们发现在登录后,点击每个界面,这种用户信息都是存在的,即用户名都在右上角写着,正常思路的话是每次都需要单独对这个渲染一次,而实际上我们其实是把它当成一个变量,每次访问都会把它置于上下文中,例如

代码语言:javascript
复制
@app.context_processor
def my_context_processor():
    return {"user": g.user}
非登录

对于这个,我们只需要写一个销毁session的,即可实现。

代码语言:javascript
复制
@bp.route("/logout")
def logout():
    session.clear()
    return redirect("/")

但这个是视图中的,我们在实际html代码中还需要有一个指向,比如现在有退出按钮,我们需要修改它的href={{url_for('auth.logout')}}

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023/03/17 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Flask
    • 连接mysql数据库
      • ORM模型
    • 问答平台搭建
    相关产品与服务
    数据库
    云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档