Flask+Bootstrap实战二

本次的实战是仿照视频制作一个简易的博客网站

项目教程地址:

Youtobe源地址:Bootstrap Flask
Bilibili搬运:哔哩哔哩


一.创建数据库完成用户注册任务

Flask-SQLAlChemy创建用户模型

首先安装flask-sqlalchemy库,之后需要创建一个config文件进行数据库的配置


1. config.py原始文件链接

2. flask-sqlalchemy教程文档链接

config.py文件内容如下,他的意思就是首先会去环境变量中获取数据库的url,如果没有的话会新建一个app.db

import os
basedir = os.path.abspath(os.path.dirname(__file__))

class Config(object):
	# Database configuration
	SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or 'sqlite:///' + os.path.join(basedir, 'app.db')
	SQLALCHEMY_TRACK_MODIFICATIONS = False

新创建一个models.py文件,文件内容如下

from sqlalchemy import Integer, String
from sqlalchemy.orm import Mapped, mapped_column
from app import db

class User(db.Model):
	id = db.Column(db.Integer, primary_key=True)
	username = db.Column(db.String(20), unique=True)
	password = db.Column(db.String(20), unique=True)
	email = db.Column(db.String(120), unique=True)

	def __repr__(self):
		return '<User %r>' % self.username

之后,需要修改app.py,使config创建的用户模型能够被读取到

主要是需要加上这两行代码

from config import Config,以及app.config.from_object(Config);db = SQLAlchemy(app)

注意:创建数据库要在配置之前,否则会报错

RuntimeError: Either 'SQLALCHEMY_DATABASE_URI' or 'SQLALCHEMY_BINDS' 
must be set.

新建一个数据库

进入flask shell,导入from models import db,之后新建一个db.create_all(),即可新建一个app.db文件

from models import db
>>> db.create_all()
>>> from models import User
>>> user = User(username = "jack", password = "123456", email = "jack@qq.com")
>>> db.session.add(user)
>>> db.session.commit()
>>> User.query.all()
[<User 'jack'>]
>>>

按照上面依次输入即可

二.使用Flask WTF创建用户注册表单

1. flask-wtf教程文档

2. Flask-Bootstrap的教程文档

在这里可以查看WTF支持的示例,使用quick_form宏

3. Bootstrap的组件库

首先需要修改navbar.html

<nav class="navbar navbar-default">
	<div class="container-fluid">
	  <!-- Brand and toggle get grouped for better mobile display -->
	  <div class="navbar-header">
		<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
		  <span class="sr-only">Toggle navigation</span>
		  <span class="icon-bar"></span>
		  <span class="icon-bar"></span>
		  <span class="icon-bar"></span>
		</button>
		<a class="navbar-brand" href="#">Flask App</a>
	  </div>

	  <!-- Collect the nav links, forms, and other content for toggling -->
	  <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
		<ul class="nav navbar-nav">
		  <li class="{% if request.endpoint == 'index' %}active{% endif %}">
			<a href="{{url_for('index')}}">Home</a>
		  </li>
		</ul>
	  </div><!-- /.navbar-collapse -->
	</div><!-- /.container-fluid -->
  </nav>

需要新建一个路由

@app.route('/register')
def register():
	return render_template("register.html")

之后需要新建一个register.html

{% block_app content %}
 <h1>Register Now</h1>
{% endblock %}

使用flask-wtf定义forms

新建一个forms.py代码如下

from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, Length, Email, EqualTo

class RegisterForm(FlaskForm):
	username = StringField("Username", validators=[DataRequired(), Length(min=6, max=20)])
	password = PasswordField("Password", validators=[DataRequired(), Length(min=6, max=20)])
	email = StringField("Email", validators=[DataRequired(), Email()])
	conform = StringField("Repeat Password", validators=[DataRequired(), EqualTo("password")])
	submit = SubmitField("Register")

在app.py中导入forms.py文件

做如下修改

@app.route('/register')
def register():
	form = RegisterForm()
	return render_template("register.html", form=form)

在config.py文件中修改key

import os
basedir = os.path.abspath(os.path.dirname(file))

class Config(object):

SECRET_KEY = os.environ.get('DATABASE_URL') or 'A VERY LONG SECRET_KEY'

# Database configuration
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or 'sqlite:///' + os.path.join(basedir, 'app.db')
SQLALCHEMY_TRACK_MODIFICATIONS = False

修改register.html

{% extends "base.html" %}

{% block content %}
 <h1>Register Now</h1>
 <br>
 <div class="row">
  <div class="col-md-8">
   {% import bootstrap/wtf.html as wtf %}
   {{ wtf.quick_form(form) }}
  </div>
 </div>
{% endblock %}

最后效果展示


这里就是这样正常处理完之后,会出现一个小错误

这里需要再回到app.py中进行修改如下。有两处修改,一是使register函数可以处理GET和POST请求,一是检查用户输入是否通过表单验证

@app.route('/register', methods=['GET', 'POST'])
def register():
	form = RegisterForm()
	if form.validate_on_submit():
		pass
	return render_template("register.html", form=form)

然后就可以了

添加一个reCAPTURE

这个很酷,我们试着来操作一下,wtf教程文档点击这里

但是由于谷歌这里的原因,用梯子试了等一下不行

这个就先跳过了

三.将register登录的信息存储到数据库中

首先是flask-bcrypt教程

使用Bcrypt进行用户数据加密

对app.py文件进行修改后如下


@app.route('/register', methods=['GET', 'POST'])
def register():
	form = RegisterForm()
	if form.validate_on_submit():
		username = form.username.data
		email = form.email.data
		password = bcrypt.generate_password_hash(form.password.data)
		print(username, password, email)

即可打印出加密后的密码的哈希字符串

将用户注册信息写入数据库

循环导入报错

修改app.py中register的路由,改成这样

@app.route('/register', methods=['GET', 'POST'])
def register():
	form = RegisterForm()
	if form.validate_on_submit():
		username = form.username.data
		email = form.email.data
		password = bcrypt.generate_password_hash(form.password.data)
		user = User(username=username, email=email, password=password)
		db.session.add(user)
		db.session.commit()
		flash("Congratulation, registeration success!", category="success")

之后在app.py中添加from models import User,就会报错,会出现循环导入

这个错误是由于循环导入引起的。循环导入发生在两个或多个模块相互导入对方,导致 Python 无法正确解析依赖关系。在你的情况下,app.py 导入了 models.py,而 models.py 又试图导入 app.py,这就导致了循环导入。

首先搞清楚,就是在app.py中,因为操作数据库会不可避免地用到app = Flask(__name__)db = SQLAlchemy(app),而在models.py中from app import db之后进行用户模型定义。最后,我们又企图在app.py中利用建好的用户模型进行注册信息的写入user = User(username=username, email=email, password=password),才会发生循环导入

目录结构修改

可以看到的是,这个目录结构设置的就比较巧妙,首先是新建了一个app目录,在__init__.py文件中使用app = Flask(_name_)db = SQLAlchemy(app)新建了app,这样做的目的就是在models.py中进行app导入时,可以不用修改路径直接导入,因为__init__.py文件的特殊性,也不用from app.__init__ import app,所以可以直接导入。另外就是routes导入User的时候也不用修改路径,直接from models import User

另外就是在原先的app.py位置新建一个run.py用于启动


那整个的顺序就是变成了__init__.py来新建的app.py,之后models.py调用一次之后,由routes.py再进行调用,这样就避免了循环导入。最后再由run.py进行直接启动

最后一步,因为routes.py导入html需要和templates文件夹在同一模板下面,所以需要将templates文件夹整个cut到app文件夹下面,最后整个的结构如图所示

验证用户名和密码是否存在于数据库中

我们需要在wtf建立的用户注册表单中进行修改,就是在forms.py文件中

这里修改为from models import User,为什么不能是from app.routes import User呢?一旦这么写就又会发生循环导入

在你的情况下,forms.py 导入了 app.routes,而 app.routes 又试图导入 forms.py,这就导致了循环导入。

之后可以看到

写Flash弹窗消息

flask-flashing官方文档

这个按照视频里的写法有一些小bug,就是在每次注册完成后反而并不显示flash的弹窗消息,会直接重定向到index界面,而再次打开register注册界面才会出现那个绿色的弹出,而且必须得刷新一下才能消失。就是不符合我们正常的操作习惯,他应该是先显示弹窗2秒之后,消失再重定向到index界面

咨询了一下GPT,这么写。首先绿色弹窗的样式都不用动,我们新建一个register_success.html文件,代码如下

点击查看代码
{% extends "bootstrap/base.html" %}


{% block content %}
  <div class = "container">
    <div class="row">
      <div class="col-md-6">
        {% with messages = get_flashed_messages(with_categories=True) %}
          {% if messages %}
            {% for category, message in messages %}
              <div class="alert alert-{{ category }}">
                {{ message }}
              </div>
            {% endfor %}
          {% endif %}
        {% endwith %}
      </div>
    </div>
  </div>

  <script>
    // Wait for 2 seconds and then redirect to the index page
    setTimeout(function() {
        window.location.href = "{{ url_for('index') }}";
    }, 2000);
  </script>
{% endblock %}

里面script的部分就是控制的延时函数

之后在routes.py中写入一个新的路由用来重定向

@app.route('/register_success')
def register_success():
	return render_template("register_success.html")

这样原先的register路由就会变成

@app.route('/register', methods=['GET', 'POST'])
def register():
	form = RegisterForm()
	if form.validate_on_submit():
		username = form.username.data
		email = form.email.data
		password = bcrypt.generate_password_hash(form.password.data)
		user = User(username=username, email=email, password=password)
		db.session.add(user)
		db.session.commit()
		flash("Congratulation, registeration success!", category="success")
		return redirect(url_for("register_success"))

	return render_template("register.html", form=form)

最后的效果如下

posted on 2025-03-05 21:19  bnbncch  阅读(91)  评论(0)    收藏  举报