flask实战(问答平台)

课程链接

问答平台项目结构搭建

先创建一个配置文件config.py,后面有些配置写在这里
在这里插入图片描述

#app.py
from flask import Flask
import configapp = Flask(__name__)
#绑定配置文件
app.config.from_object(config)@app.route('/')
def hello_world():  # put application's code herereturn 'Hello World!'if __name__ == '__main__':app.run()

再新建一个exts.py和models.py

#exts.py
#flask-sqlalchemy
from flask_sqlalchemy import SQLAlchemydb=SQLAlchemy()#models.py
from exts import dbclass UserModel(db.Model):pass#app.py
from flask import Flask
import config
from exts import db
from models import UserModelapp = Flask(__name__)
#绑定配置文件
app.config.from_object(config)#可以让你先创建,再绑定
db.init_app(app)@app.route('/')
def hello_world():  # put application's code herereturn 'Hello World!'if __name__ == '__main__':app.run()

防止循环引用,如果不加exts.py会导致下面这种情况
在这里插入图片描述
加了就可以变成这样子

在这里插入图片描述
下面设置蓝图,用来做模块化的
在这里插入图片描述

在这里插入图片描述
再在这里新建两个py文件,一个是auth.py和授权相关的,一个是qa.py和问答相关的

#auth.py
from flask import Blueprint# /auth,后面所有的路由都是以这个开头,比如/auth/login
bp=Blueprint("auth",__name__,url_prefix="/auth")@bp.route("/login")
def login():pass#qa.py
from flask import Blueprintbp=Blueprint("qa",__name__,url_prefix="/")@bp.route("/")
def index():pass

再在app.py中导入和绑定

#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_bpapp = Flask(__name__)
#绑定配置文件
app.config.from_object(config)#可以让你先创建,再绑定
db.init_app(app)app.register_blueprint(qa_bp)
app.register_blueprint(auth_bp)if __name__ == '__main__':app.run()

User模型创建

先在navicat里新建数据库
在这里插入图片描述
在这里插入图片描述

#config.py
# 数据库的配置信息
HOSTNAME = '127.0.0.1'
PORT = '3306'
USERNAME = 'root'
PASSWORD = '123456'
DATABASE = 'zhiliaooa_course'DB_URI = 'mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)
SQLALCHEMY_DATABASE_URI = DB_URI#model.py
from exts import db
from datetime import datetimeclass UserModel(db.Model):__tablename__ = "user"id = db.Column(db.Integer, primary_key=True, autoincrement=True)username = db.Column(db.String(100), nullable=False)password = db.Column(db.String(100), nullable=False)email = db.Column(db.String(100), nullable=False)join_time = db.Column(db.DateTime, default=datetime.now)#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 Migrateapp = 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)if __name__ == '__main__':app.run()

然后运行三步
flask db init(只需第一次操作一下)
flask db migrate
flask db upgrade

然后数据库刷新下就可以看到
在这里插入图片描述

在这里插入图片描述

注册页面模型渲染

我们已经准备好了html文件和其静态样式,将其放入自己的文件内

其中本来只有红框标记的这几个
在这里插入图片描述
register.js是为了登录自己写的
base.html是为了减少代码量,能复用父组件base.html写的
(这部分可先跳过,后面会说)

#register.js
function bindEmailCaptchaClick(){$("#captcha-btn").click(function (event){// $this:代表的是当前按钮的jquery对象var $this = $(this);// 阻止默认的事件event.preventDefault();var email = $("input[name='email']").val();$.ajax({// http://127.0.0.1:500// /auth/captcha/email?email=xx@qq.comurl: "/auth/captcha/email?email="+email,method: "GET",success: function (result){var code = result['code'];if(code == 200){var countdown = 5;// 开始倒计时之前,就取消按钮的点击事件$this.off("click");var timer = setInterval(function (){$this.text(countdown);countdown -= 1;// 倒计时结束的时候执行if(countdown <= 0){// 清掉定时器clearInterval(timer);// 将按钮的文字重新修改回来$this.text("获取验证码");// 重新绑定点击事件bindEmailCaptchaClick();}}, 1000);// alert("邮箱验证码发送成功!");}else{alert(result['message']);}},fail: function (error){console.log(error);}})});
}// 整个网页都加载完毕后再执行的
$(function (){bindEmailCaptchaClick();
});
#base.html
<!DOCTYPE html>
<html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="{{ url_for('static', filename='bootstrap/bootstrap.4.6.min.css') }}"><link rel="stylesheet" href="{{ url_for('static', filename='css/init.css') }}">{% block head %}{% endblock %}<title>{% block title %}{% endblock %}</title>
</head><body><nav class="navbar navbar-expand-lg navbar-light bg-light"><div class="container"><a class="navbar-brand" href="#">知了问答</a><button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><div class="collapse navbar-collapse" id="navbarSupportedContent"><ul class="navbar-nav mr-auto"><li class="nav-item active"><a class="nav-link" href="/">首页 <span class="sr-only">(current)</span></a></li><li class="nav-item"><a class="nav-link" href="{{ url_for('qa.index') }}">发布问答</a></li><li class="nav-item ml-2"><form class="form-inline my-2 my-lg-0" method="GET" action="{{ url_for('qa.index') }}"><input class="form-control mr-sm-2" type="search" placeholder="关键字" aria-label="Search" name="q"><button class="btn btn-outline-success my-2 my-sm-0" type="submit">搜索</button></form></li></ul><ul class="navbar-nav">{% if user %}<li class="nav-item"><span class="nav-link">{{ user.username }}</span></li><li class="nav-item"><a class="nav-link" href="{{ url_for('auth.logout') }}">退出登录</a></li>{% else %}<li class="nav-item"><a class="nav-link" href="{{ url_for('auth.login') }}">登录</a></li><li class="nav-item"><a class="nav-link" href="{{ url_for('auth.register') }}">注册</a></li>{% endif %}</ul></div></div></nav><div class="container">{% block body %}{% endblock %}</div>
</body></html>

然后再auth.py里渲染前端页面

from flask import Blueprint,render_template# /auth,后面所有的路由都是以这个开头,比如/auth/login
bp=Blueprint("auth",__name__,url_prefix="/auth")@bp.route("/login")
def login():return "11"@bp.route("/register")
def register():return render_template("register.html")

在这里插入图片描述

Flask发送邮件功能实现

pip install flask_mail
然后准备好qq邮箱,其中一个这样子设置,是邮件的发送方
在这里插入图片描述
开启这个服务,我这里是之前已经开启过,如果没有开启的话开启
在这里插入图片描述

在这里插入图片描述
记住你的授权码
在这里插入图片描述
然后再config.py里进行邮箱配置

#邮箱配置
MAIL_SERVER = 'smtp.qq.com'
MAIL_PORT = 465
MAIL_USE_SSL = True
MAIL_USERNAME = '填入你刚刚操作的邮箱'
MAIL_PASSWORD = '填入你刚刚的授权码'
MAIL_DEFAULT_SENDER = '填入你刚刚操作的邮箱,和上面一致'

接下去进行如下操作

#exts.py
# flask-sqlalchemy
from flask_sqlalchemy import SQLAlchemy
from flask_mail import Maildb = SQLAlchemy()
mail = Mail()#app.py
from flask import Flask
import config
from exts import db,mail
from models import UserModel
from blueprints.qa import bp as qa_bp
from blueprints.auth import bp as auth_bp
from flask_migrate import Migrateapp = Flask(__name__)
# 绑定配置文件
app.config.from_object(config)# 可以让你先创建,再绑定
db.init_app(app)
mail.init_app(app)migrate = Migrate(app, db)app.register_blueprint(qa_bp)
app.register_blueprint(auth_bp)if __name__ == '__main__':app.run()

去auth.py测试一下

from flask import Blueprint,render_template
from exts import mail
from flask_mail import Message# /auth,后面所有的路由都是以这个开头,比如/auth/login
bp=Blueprint("auth",__name__,url_prefix="/auth")@bp.route("/login")
def login():return "11"@bp.route("/register")
def register():return render_template("register.html")@bp.route("mail/test")
def mail_test():message=Message(subject="邮箱测试",recipients=["填入你想发送人的邮箱,自己的也行"],body="这是一条测试邮件")mail.send(message)return "邮件发送成功"

在这里插入图片描述

发送邮箱验证码功能实现(1)

#auth.py
import random
import stringfrom flask import Blueprint,render_template
from exts import mail
from flask_mail import Message
from flask import request# /auth,后面所有的路由都是以这个开头,比如/auth/login
bp=Blueprint("auth",__name__,url_prefix="/auth")@bp.route("/login")
def login():return "11"@bp.route("/register")
def register():return render_template("register.html")@bp.route("/captcha/email")
def get_email_captcha():#/captcha/email/<email>#/captcha/email?email=123@qq.comemail=request.args.get("email")#4/6:随机数组、字母、数组和字母的组合source=string.digits*4captcha=random.sample(source,4)captcha="".join(captcha)print(captcha)return "success"@bp.route("mail/test")
def mail_test():message=Message(subject="邮箱测试",recipients=["123……@qq.com"],body="这是一条测试邮件")mail.send(message)return "邮件发送成功"

测试了一下验证码获取正常,继续完善

@bp.route("/captcha/email")
def get_email_captcha():#/captcha/email/<email>#/captcha/email?email=123@qq.comemail=request.args.get("email")#4/6:随机数组、字母、数组和字母的组合source=string.digits*4captcha=random.sample(source,4)captcha="".join(captcha)message = Message(subject="知了传课注册验证码", recipients=[email], body=f"您的验证码是:{captcha}")mail.send(message)return "success"

http://127.0.0.1:5000/auth/captcha/email?email=123……@qq.com网页中输入这段代码

在这里插入图片描述
现在有一个问题,如何验证用户提交的邮箱和验证码是否对应且正确
答:memcached/redis用数据库表的方式存储

发送邮箱验证码功能实现(2)

去model.py新建一个类

class EmailCaptchaModel(db.Model):__tablename__ = "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)

然后终端执行如下代码
在这里插入图片描述
数据库里可以看到新建了一个
在这里插入图片描述

#auth.py
import random
import stringfrom flask import Blueprint,render_template,jsonify
from exts import mail,db
from flask_mail import Message
from flask import request
from models import EmailCaptchaModel# /auth,后面所有的路由都是以这个开头,比如/auth/login
bp=Blueprint("auth",__name__,url_prefix="/auth")@bp.route("/login")
def login():return "11"@bp.route("/register")
def register():return render_template("register.html")@bp.route("/captcha/email")
def get_email_captcha():#/captcha/email/<email>#/captcha/email?email=123@qq.comemail=request.args.get("email")#4/6:随机数组、字母、数组和字母的组合source=string.digits*4captcha=random.sample(source,4)captcha="".join(captcha)message = Message(subject="知了传课注册验证码", recipients=[email], body=f"您的验证码是:{captcha}")mail.send(message)# 用数据库表的方式存储email_captcha=EmailCaptchaModel(email=email,captcha=captcha)db.session.add(email_captcha)db.session.commit()#RESTful API#{code:200/400/500,message:"",data:{}}return jsonify({"code":200,"message":"","data":None})@bp.route("mail/test")
def mail_test():message=Message(subject="邮箱测试",recipients=["1065088270@qq.com"],body="这是一条测试邮件")mail.send(message)return "邮件发送成功"

在这里插入图片描述

发送邮箱验证码功能实现(3)

#register.js
function bindEmailCaptchaClick(){$("#captcha-btn").click(function (event){// $this:代表的是当前按钮的jquery对象var $this = $(this);// 阻止默认的事件event.preventDefault();var email = $("input[name='email']").val();$.ajax({// http://127.0.0.1:500// /auth/captcha/email?email=xx@qq.comurl: "/auth/captcha/email?email="+email,method: "GET",success: function (result){var code = result['code'];if(code == 200){var countdown = 5;// 开始倒计时之前,就取消按钮的点击事件$this.off("click");var timer = setInterval(function (){$this.text(countdown);countdown -= 1;// 倒计时结束的时候执行if(countdown <= 0){// 清掉定时器clearInterval(timer);// 将按钮的文字重新修改回来$this.text("获取验证码");// 重新绑定点击事件bindEmailCaptchaClick();}}, 1000);// alert("邮箱验证码发送成功!");}else{alert(result['message']);}},fail: function (error){console.log(error);}})});
}// 整个网页都加载完毕后再执行的
$(function (){bindEmailCaptchaClick();
});

实现功能如下
在这里插入图片描述

发送邮箱验证码功能实现(4)

这部分是加入倒计时,也就是上面的内容,代码合在一块了

后端注册表单验证器实现

验证用户提交的邮箱和验证码是否对应且正确
pip install flask-wtf
在blueprints文件夹里新建forms.py

#forms.py
import wtforms
from wtforms.validators import Email,Length,EqualTo
from models import UserModel,EmailCaptchaModel
from exts import db# Form:主要就是用来验证前端提交的数据是否符合要求
class RegisterForm(wtforms.Form):email=wtforms.StringField(validators=[Email(message="邮箱格式不正确")])captcha=wtforms.StringField(validators=[Length(min=4,max=4,message="验证码长度必须为4位")])username=wtforms.StringField(validators=[Length(min=3,max=30,message="用户名长度必须在3-30之间")])password=wtforms.StringField(validators=[Length(min=6,max=30,message="密码长度必须在6-30之间")])password_confirm=wtforms.StringField(validators=[EqualTo("password",message="两次密码不一致")])# 自定义验证:#1、邮箱是否已经被注册def validate_email(self,field):email=field.datauser=UserModel.query.filter_by(email=email).first()if user:raise wtforms.ValidationError(message="邮箱已经被注册")# 2、验证码是否正确def validate_captcha(self,field):captcha=field.dataemail=self.email.datacaptcha_model=EmailCaptchaModel.query.filter_by(email=email).first()if not captcha_model:raise wtforms.ValidationError(message="邮箱或验证码错误!")# 最好是写一个脚本,自动多长时间清除# else:#     db.session.delete(captcha_model)#     db.session.commit()

后端注册功能完成

pip install email_validator
然后记得model.py里的password长度设置成200,因为用哈希加密后会超过原来设置的100,会报错(然后记得重新提交更新下数据库)

#auth.py
import random
import stringfrom flask import Blueprint,render_template,jsonify,redirect,url_for
from exts import mail,db
from flask_mail import Message
from flask import request
from models import EmailCaptchaModel
from werkzeug.security import generate_password_hashfrom .forms import RegisterForm
from models import UserModel# /auth,后面所有的路由都是以这个开头,比如/auth/login
bp=Blueprint("auth",__name__,url_prefix="/auth")@bp.route("/login")
def login():return "11"# GET:从服务器上获取数据
# POST:将客户端的数据向服务器提交
@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.datausername=form.username.datapassword=form.password.datauser=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"))@bp.route("/captcha/email")
def get_email_captcha():#/captcha/email/<email>#/captcha/email?email=123@qq.comemail=request.args.get("email")#4/6:随机数组、字母、数组和字母的组合source=string.digits*4captcha=random.sample(source,4)captcha="".join(captcha)message = Message(subject="知了传课注册验证码", recipients=[email], body=f"您的验证码是:{captcha}")mail.send(message)# 用数据库表的方式存储email_captcha=EmailCaptchaModel(email=email,captcha=captcha)db.session.add(email_captcha)db.session.commit()#RESTful API#{code:200/400/500,message:"",data:{}}return jsonify({"code":200,"message":"","data":None})@bp.route("mail/test")
def mail_test():message=Message(subject="邮箱测试",recipients=["1065088270@qq.com"],body="这是一条测试邮件")mail.send(message)return "邮件发送成功"

登录页面模板渲染完成

就是把login.html用base.html复用,然后渲染出来,给的资料里已经操作好了

#app.py
@bp.route("/login")
def login():return render_template("login.html")

登录功能后端实现

#forms.py新增一个类
class LoginForm(wtforms.Form):email=wtforms.StringField(validators=[Email(message="邮箱格式不正确")])password=wtforms.StringField(validators=[Length(min=6,max=30,message="密码长度必须在6-30之间")])#auth.py
import random
import stringfrom flask import Blueprint,render_template,jsonify,redirect,url_for,session
from exts import mail,db
from flask_mail import Message
from flask import request
from models import EmailCaptchaModel
from werkzeug.security import generate_password_hash,check_password_hashfrom .forms import RegisterForm,LoginForm
from models import UserModel# /auth,后面所有的路由都是以这个开头,比如/auth/login
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():email=form.email.datapassword=form.password.datauser=UserModel.query.filter_by(email=email).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.idreturn redirect("/")else:print("密码错误")return redirect(url_for("auth.login"))else:print(form.errors)return redirect(url_for("auth.login"))
# GET:从服务器上获取数据
# POST:将客户端的数据向服务器提交
@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.datausername=form.username.datapassword=form.password.datauser=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"))@bp.route("/captcha/email")
def get_email_captcha():#/captcha/email/<email>#/captcha/email?email=123@qq.comemail=request.args.get("email")#4/6:随机数组、字母、数组和字母的组合source=string.digits*4captcha=random.sample(source,4)captcha="".join(captcha)message = Message(subject="知了传课注册验证码", recipients=[email], body=f"您的验证码是:{captcha}")mail.send(message)# 用数据库表的方式存储email_captcha=EmailCaptchaModel(email=email,captcha=captcha)db.session.add(email_captcha)db.session.commit()#RESTful API#{code:200/400/500,message:"",data:{}}return jsonify({"code":200,"message":"","data":None})@bp.route("mail/test")
def mail_test():message=Message(subject="邮箱测试",recipients=["1065088270@qq.com"],body="这是一条测试邮件")mail.send(message)return "邮件发送成功"#config.py新增一个
SECRET_KEY="adaasdwedda::@" #输入什么都行

你可以通过f12进去,查看cookie存储情况
在这里插入图片描述

两个钩子函数

#app.py
from flask import Flask,session,g
import config
from exts import db,mail
from models import UserModel
from blueprints.qa import bp as qa_bp
from blueprints.auth import bp as auth_bp
from flask_migrate import Migrateapp = Flask(__name__)
# 绑定配置文件
app.config.from_object(config)# 可以让你先创建,再绑定
db.init_app(app)
mail.init_app(app)migrate = Migrate(app, db)app.register_blueprint(qa_bp)
app.register_blueprint(auth_bp)# before_request/before_first_request/after_request
# hook
@app.before_request
def my_before_request():user_id=session.get("user_id")if user_id:user=UserModel.query.get(user_id)setattr(g,"user",user)else:setattr(g,"user",None)@app.context_processor
def my_context_processor():return {"user":g.user}if __name__ == '__main__':app.run()

这两段代码是使用 Python 的 Flask 框架编写的,用于处理Web应用程序的请求和上下文管理。让我分别解释它们的作用:

  1. @app.before_request 装饰器函数:
    这是一个 Flask 路由装饰器,用于注册一个在每个请求处理之前执行的函数。具体来说,这段代码定义了一个名为 my_before_request 的函数,该函数会在每个请求处理之前执行。在函数内部,它首先尝试从会话(session)中获取用户ID(user_id),这通常是通过用户登录认证过程中设置的。然后,它检查是否成功获取了用户ID。
  • 如果成功获取了用户ID,它使用这个用户ID查询数据库中的用户数据(通过 UserModel.query.get(user_id))。然后,使用 setattr(g, "user", user) 将用户对象存储在 Flask 的上下文变量 g 中,以便在请求处理过程中的其他部分可以轻松地访问用户对象。
  • 如果未成功获取用户ID(即用户未登录或未认证),它将在上下文变量 g 中设置一个空值用户对象。

这个函数的主要目的是在每个请求之前检查用户的登录状态,如果用户已登录,将用户对象存储在 g 上下文变量中,以便后续请求处理可以使用该用户对象。
2. @app.context_processor 装饰器函数:
这是另一个 Flask 装饰器,用于注册一个上下文处理器函数,该函数可以将一些数据添加到模板上下文中,以便在渲染模板时访问这些数据。上下文处理器通常用于在所有模板中共享一些通用数据,以简化模板的代码。

  • 这段代码定义了一个名为 my_context_processor 的函数,该函数不接受任何参数。在函数内部,它返回一个字典,其中包含了一个键为 “user” 的项,其值为 g.user,即在前面的 my_before_request 函数中设置的用户对象。
  • 这意味着在渲染模板时,您可以在模板中访问 user 变量,以获取当前登录用户的相关信息。例如,您可以在模板中使用 {{ user.username }} 来显示当前登录用户的用户名。

总的来说,这两段代码结合使用,用于在每个请求处理之前检查用户的登录状态,并将用户对象存储在上下文变量 g 中,然后在模板中使用上下文处理器函数将用户对象添加到模板上下文中,以便在模板中方便地访问用户信息。这对于构建需要用户认证的 Web 应用程序非常有用。

登录和非登录状态切换

前端修改部分已改好

#auth.py新增一个
@bp.route("/logout")
def logout():session.clear()return redirect("/")#和base.html的 这部分呼应
<li class="nav-item">
<a class="nav-link" href="{{ url_for('auth.logout') }}">退出登录</a>
</li>

发布问答页面渲染

#qa.py
from flask import Blueprint,request,render_templatebp=Blueprint("qa",__name__,url_prefix="/")@bp.route("/")
def index():return "00"@bp.route("/qa/public",methods=['GET','POST'])
def public_qa():if request.method=='GET':return render_template("public_question.html")

在这里插入图片描述

发布问答后端功能实现

#models.py加入
class QuestionModel(db.Model):__tablename__="question"id = db.Column(db.Integer, primary_key=True, autoincrement=True)title=db.Column(db.String(100), nullable=False)content=db.Column(db.Text,nullable=False)create_time=db.Column(db.DateTime,default=datetime.now)# 外键author_id=db.Column(db.Integer,db.ForeignKey("user.id"))author=db.relationship(UserModel,backref="questions")

flask db migrate
flask db upgrade

然后我们就可以写发布问答的逻辑了

先设置表单验证

#forms.py
class QuestionForm(wtforms.Form):title=wtforms.StringField(validators=[Length(min=3,max=100,message="标题格式错误")])content=wtforms.StringField(validators=[Length(min=3,message="内容格式错误!")])

然后写问答逻辑

#qa.py
from flask import Blueprint,request,render_template,g,redirect,url_for
from .forms import QuestionForm
from models import QuestionModel
from exts import dbbp=Blueprint("qa",__name__,url_prefix="/")@bp.route("/")
def index():return "00"@bp.route("/qa/public",methods=['GET','POST'])
def public_question():if request.method=='GET':return render_template("public_question.html")else:form=QuestionForm(request.form)if form.validate():title=form.title.datacontent=form.content.dataquestion=QuestionModel(title=title,content=content,author=g.user)db.session.add(question)db.session.commit()return redirect("/")else:print(form.errors)return redirect(url_for("qa.public_question"))

登录装饰器的实现

和config.py同级的地方新建一个decorators.py文件

#decorators.py
from functools import wraps
from flask import g,redirect,url_fordef login_required(func):#保留func的信息@wraps(func)#func(a,b,c)#func(1,2,c=3)def inner(*args,**kwargs):if g.user:return func(*args,**kwargs)else:return redirect(url_for("auth.login"))return inner
#qa.py
from flask import Blueprint,request,render_template,g,redirect,url_for
from .forms import QuestionForm
from models import QuestionModel
from exts import db
from decorators import login_requiredbp=Blueprint("qa",__name__,url_prefix="/")@bp.route("/")
def index():return "00"@bp.route("/qa/public",methods=['GET','POST'])
@login_required
def public_question():if request.method=='GET':return render_template("public_question.html")else:form=QuestionForm(request.form)if form.validate():title=form.title.datacontent=form.content.dataquestion=QuestionModel(title=title,content=content,author=g.user)db.session.add(question)db.session.commit()return redirect("/")else:print(form.errors)return redirect(url_for("qa.public_question"))

首页问答列表渲染完成

#qa.py修改下这里
@bp.route("/")
def index():questions=QuestionModel.query.order_by(QuestionModel.create_time.desc()).all()return render_template("index.html",questions=questions)

问答列表页渲染

前端部分修改直接修改好了放在文件夹里的

#qa.py里加入
@bp.route("/qa/detail/<qa_id>")
def qa_detail(qa_id):question=QuestionModel.query.get(qa_id)return render_template("detail.html",question=question)

答案模型创建

#models.py新增
class AnswerModel(db.Model):__tablename__="answer"id=db.Column(db.Integer,primary_key=True,autoincrement=True)content=db.Column(db.Text,nullable=False)create_time=db.Column(db.DateTime,default=datetime.now)#外键question_id=db.Column(db.Integer,db.ForeignKey("question.id"))author_id=db.Column(db.Integer,db.ForeignKey("user.id"))#关系question=db.relationship(QuestionModel,backref=db.backref("answers",order_by=create_time.desc()))author=db.relationship(UserModel,backref="answers")

发布答案功能完成

#forms.py
import wtforms
from wtforms.validators import Email,Length,EqualTo,InputRequired
from models import UserModel,EmailCaptchaModel
from exts import db# Form:主要就是用来验证前端提交的数据是否符合要求
class RegisterForm(wtforms.Form):email=wtforms.StringField(validators=[Email(message="邮箱格式不正确")])captcha=wtforms.StringField(validators=[Length(min=4,max=4,message="验证码长度必须为4位")])username=wtforms.StringField(validators=[Length(min=3,max=30,message="用户名长度必须在3-30之间")])password=wtforms.StringField(validators=[Length(min=6,max=30,message="密码长度必须在6-30之间")])password_confirm=wtforms.StringField(validators=[EqualTo("password",message="两次密码不一致")])# 自定义验证:#1、邮箱是否已经被注册def validate_email(self,field):email=field.datauser=UserModel.query.filter_by(email=email).first()if user:raise wtforms.ValidationError(message="邮箱已经被注册")# 2、验证码是否正确def validate_captcha(self,field):captcha=field.dataemail=self.email.datacaptcha_model=EmailCaptchaModel.query.filter_by(email=email).first()if not captcha_model:raise wtforms.ValidationError(message="邮箱或验证码错误!")# 最好是写一个脚本,自动多长时间清除# else:#     db.session.delete(captcha_model)#     db.session.commit()class LoginForm(wtforms.Form):email=wtforms.StringField(validators=[Email(message="邮箱格式不正确")])password=wtforms.StringField(validators=[Length(min=6,max=30,message="密码长度必须在6-30之间")])class QuestionForm(wtforms.Form):title=wtforms.StringField(validators=[Length(min=3,max=100,message="标题格式错误")])content=wtforms.StringField(validators=[Length(min=3,message="内容格式错误!")])class AnswerForm(wtforms.Form):content=wtforms.StringField(validators=[Length(min=3,message="内容格式错误!")])question_id=wtforms.IntegerField(validators=[InputRequired(message="问题id不能为空")])
#qa.py
from flask import Blueprint,request,render_template,g,redirect,url_for
from .forms import QuestionForm,AnswerForm
from models import QuestionModel,AnswerModel
from exts import db
from decorators import login_requiredbp=Blueprint("qa",__name__,url_prefix="/")@bp.route("/")
def index():questions=QuestionModel.query.order_by(QuestionModel.create_time.desc()).all()return render_template("index.html",questions=questions)@bp.route("/qa/public",methods=['GET','POST'])
@login_required
def public_question():if request.method=='GET':return render_template("public_question.html")else:form=QuestionForm(request.form)if form.validate():title=form.title.datacontent=form.content.dataquestion=QuestionModel(title=title,content=content,author=g.user)db.session.add(question)db.session.commit()return redirect("/")else:print(form.errors)return redirect(url_for("qa.public_question"))@bp.route("/qa/detail/<qa_id>")
def qa_detail(qa_id):question=QuestionModel.query.get(qa_id)return render_template("detail.html",question=question)# @bp.route("/answer/public",methods=['POST'])
@bp.post("answer/public")
@login_required
def public_answer():form=AnswerForm(request.form)if form.validate():content=form.content.dataquestion_id=form.question_id.dataanswer=AnswerModel(content=content,question_id=question_id,author_id=g.user.id)db.session.add(answer)db.session.commit()return redirect(url_for("qa.qa_detail",qa_id=question_id))else:print(form.errors)return redirect(url_for("qa.qa_detail",qa_id=request.form.get("question_id")))

搜索功能实现

主要是前端代码的修改,主要是完善上一个步骤


答案列表的渲染

#qa.py新增
@bp.route("/search")
def search():#/search?q=flask#/search/<q>#post,request.formq=request.args.get("q")questions=QuestionModel.query.filter(QuestionModel.title.contains(q)).all()return render_template("index.html",questions=questions)

完结!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/163204.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

嵌入式行业是否会面临中年危机?

今日话题&#xff1a;嵌入式行业是否会面临中年危机&#xff1f;事实上&#xff0c;无论你在哪个行业工作&#xff0c;都可能面临下岗风险。因此&#xff0c;我的建议是选择一个有前景、发展空间大的行业和方向&#xff0c;并不断提升自己的技能价值。为了帮助你在嵌入式领域取…

实测文心一言4.0,真的比GPT-4毫不逊色吗?

10月17日&#xff0c;李彦宏在百度世界2023上表示。当天&#xff0c;李彦宏以《手把手教你做AI原生应用》为主题发表演讲&#xff0c;发布文心大模型4.0版本。 今天&#xff0c;咱们就开门见山啊。这一回要测一测&#xff0c;昨天才发布的文心一言大模型 4.0。 之所以要测它&…

JVM(Java Virtual Machine)内存模型篇

前言 本文是JVM系列的内存模型篇&#xff0c;参考资料为《深入理解Java虚拟机》&#xff0c;本文章将会以HotSpot 虚拟机为介绍基础。 1.JVM简单介绍 Java Virtual Machine是运行Java程序的基础&#xff0c;JVM基于C、C实现&#xff0c;JVM有很多种类&#xff0c;但是这些虚…

扬帆起航:许战海方法论日文版正式发布

近日&#xff0c;中国头部战略咨询机构‘许战海咨询’最新研究成果《中国汽车行业新能源转型战略》行业白皮书日文版&#xff0c;即将在日本发布。同时发布的日文版核心方法论白皮书还有《主品牌进化战略》、《第二招牌增长战略》、《链主品牌&#xff1a;制造业的竞争之王》等…

关于代理服务器那点事

问题&#xff1a;我们有时候会遇到&#xff0c;后端再本地服务器开发&#xff0c;不是测试服上&#xff0c;这时候我们就需要代码几个baseurl 答&#xff1a;一个axios只能代理一个baseurl axios 库本身只能代理一个 baseURL。在一个 axios 实例中&#xff0c;只能指定一个基…

Spring framework day 01:spring 整合数据源(连接池)

前言 在现代的企业应用开发中&#xff0c;数据库是不可或缺的一部分。而对于大部分的应用程序来说&#xff0c;与数据库的交互涉及到频繁的连接、查询和事务操作。为了提高应用程序的性能和可扩展性&#xff0c;使用连接池来管理数据库连接是一个不错的选择。而 Spring 框架提…

Spring事件ApplicationEvent源码浅读

文章目录 demo应用实现基于注解事件过滤异步事件监听 源码解读总结 ApplicationContext 中的事件处理是通过 ApplicationEvent 类和 ApplicationListener 接口提供的。如果将实现了 ApplicationListener 接口的 bean 部署到容器中&#xff0c;则每次将 ApplicationEvent 发布到…

为什么Redis集群的最大槽数是16384个?

对于客户端请求的key&#xff0c;根据公式HASH_SLOTCRC16(key) mod 16384&#xff0c;计算出映射到哪个分片上&#xff0c;然后Redis会去相应的节点进行操作&#xff01; 为什么有16384个槽&#xff1f; Redis集群并没有使用一致性hash而是引入了哈希槽的概念。Redis 集群有1…

线性代数-Python-01:向量的基本运算 -手写Vector -学习numpy的基本用法

文章目录 代码目录结构Vector.py_globals.pymain_vector.pymain_numpy_vector.py 一、创建属于自己的向量1.1 在控制台测试__repr__和__str__方法1.2 创建实例测试代码 二、向量的基本运算2.1 加法2.2 数量乘法2.3 向量运算的基本性质2.4 零向量2.5 向量的长度2.6 单位向量2.7 …

C++string类重要函数模拟实现

为了和C标准库区分&#xff0c;以下代码除主函数外均在namespace空间 目录 一.成员 二、带参构造函数 三、拷贝构造函数和赋值运算符重载 四、析构函数 五、重要成员函数实现 1. c_str函数 2. operator[]重载 3. size函数和capacity函数 4.reverse函数 5. push_back和…

【解决】运行vue项目,启动报错 in ./node_modules/@intlify/core-base/dist/core-base.cjs

我的处理方式: 一开始查了好多方法&#xff0c;删除node_modules&#xff0c;重新安装&#xff0c;切换node版本等&#xff0c;但是发现并没有用 之后来发现是安装依赖包的时候有些包安装失败导致的&#xff0c;只要有针对性的重新安装依赖就可以了 例如&#xff1a; in ./n…

Splashtop 与 Canopy 携手共同增强对物联网设备的远程管理

2023年10月17日 加利福尼亚州库比蒂诺 Splashtop 在安全远程访问解决方案领域处于领先地位&#xff0c;Canopy 则是用于复杂硬件部署的领先 RMM 平台&#xff0c;双方今天宣布达成战略合作伙伴关系&#xff0c;以进一步增强和简化对物联网设备的远程管理。通过此次合作&#x…

JSX的本质

一、本质 React.createElement即h函数&#xff0c;返回vnode第一个参数&#xff0c;可能是组件&#xff0c;也可能是html tag组件名&#xff0c;首字母必须大写&#xff08;React规定&#xff09; 二、babel试一试 &#xff08;babel集成了jsx的编译环境&#xff09; // JSX…

Linux生产者消费者模型

生产者消费者模型 生产者消费者模型生产者消费者模型的概念生产者消费者模型的特点生产者消费者模型优点 基于BlockingQueue的生产者消费者模型基于阻塞队列的生产者消费者模型模拟实现基于阻塞队列的生产消费模型 生产者消费者模型 生产者消费者模型的概念 生产者消费者模式就…

Spring framework Day 23:容器事件

前言 容器事件是 Spring Framework 中的一个重要概念&#xff0c;它提供了一种机制&#xff0c;使我们能够更好地了解和响应 Spring 容器中发生的各种事件。通过容器事件&#xff0c;我们可以在特定的时间点监听和处理容器中的各种状态变化、操作和事件触发&#xff0c;以实现…

Go 存储系列:LSM存储引擎 LevelDB

概念介绍 LSM-Tree 被是一种面向写多读少应用场景的数据结构 &#xff0c;被 Hbase、RocksDB 等强力 NoSQL 数据库采用作为底层文件组织方式。 简单的LSM-Tree 包含 2 层树状数据结构&#xff1a; Memtable 并完全驻留在内存中&#xff08;假设 T0&#xff09; SStables 存储…

Unity SRP 管线【第二讲:Draw Call】

参考&#xff1a; https://edu.uwa4d.com/lesson-detail/282/1309/0?isPreview0 文章目录 参考&#xff1a;一、Shader1.HLSL引入2.获取Unity提供的标准输入3.Unity提供的运算库SpaceTransform库的宏对应补充&#xff1a; 4.标准库Common.hlsl5.SpaceTransforms库引入Commo…

【笔记】Endnote20插入文献

方法一 1.首先选中要参考的文章 2.在word里选好格式 3.在word里点击插入已选文献 前提&#xff1a;已经将光标放在要插入的位置了 4.插入文献即可&#xff0c;效果如下 方法二&#xff08;方便些&#xff0c;但是word容易闪退&#xff09; 1.点击要插入的文献&#xff0c;…

Apache Log4j Server (CVE-2017-5645) 反序列化命令执行漏洞

文章目录 Apache Log4j Server 反序列化命令执行漏洞&#xff08;CVE-2017-5645&#xff09;1.1 漏洞描述1.2 漏洞复现1.2.1 环境启动1.2.2 漏洞验证1.2.3 漏洞利用 1.3 加固建议 Apache Log4j Server 反序列化命令执行漏洞&#xff08;CVE-2017-5645&#xff09; 1.1 漏洞描述…