1、帖子评论和阅读数功能

添加阅读数量字段:前台模型文件 apps/front/models.py

# 帖子编辑提交模型
class PostModel(db.Model):__tablename__ = "post"id = db.Column(db.Integer, primary_key=True, autoincrement=True)title = db.Column(db.String(100), nullable=True)                                      # 帖子标题content = db.Column(db.Text, nullable=True)                                           # 帖子内容content_html = db.Column(db.Text)create_time = db.Column(db.DateTime, default=datetime.now)                            # 默认当前时间# 阅读数量字段read_count = db.Column(db.Integer, default=0)# 外键,用于查询排序board_id = db.Column(db.Integer, db.ForeignKey('cms_board.id'))      # 'cms_board.id'中cms_board是cms/models.py的表名author_id = db.Column(db.String(100), db.ForeignKey('front_user.id'))# 这里的id使用String是因为上面定义前台用户id时,使用的就是Str类型shortuuid# 反向查询属性,board = db.relationship("BoardModel", backref="posts")              # posts变成cms/models/BoardModel的属性author = db.relationship("Front_User", backref="posts")             # posts变成Front_User的属性# 实现将用户输入的content文件text类型转换成content_html的html文件,再进行存储@staticmethoddef content_to_content_html(target, value, oldvalue, initiator):# content_html文件中允许使用的标签集合allowed_tags = ['a', 'abbr', 'acronym', 'b', 'blockquote', 'code', 'em', 'i', 'ol', 'pre','strong', 'ul', 'h1', 'h2', 'h3', 'p', 'img', 'video', 'div', 'iframe','p', 'br', 'span', 'hr', 'src', 'class']# content_html文件中允许使用的属性allowed_attrs = {'*': ['class'],'a': ['href', 'rel'],'img': ['src', 'alt']}# 目标文件content_html,由bleach库进行转换         markdown将源文件显示成html文件target.content_html = bleach.linkify(bleach.clean(markdown(value, output_format='html'),                                # output_format='html'输出格式为htmltags=allowed_tags, strip=True, attributes=allowed_attrs))             # strip=True去空格# 监听PostModel.content文件如果调用了set方法,就调用content_to_content_html方法进行转换格式到html文件
db.event.listen(PostModel.content, 'set', PostModel.content_to_content_html)

映射到数据库中,同时需要修改下之前已经存在的数据:

2、帖子分类功能实现

前台蓝图文件:apps/front/views.py,将评论数据和阅读数据查询,并传递到前端页面渲染;针对不同功能选择的状态码来选择排序方式。

# -*- encoding: utf-8 -*-
"""
@File    : views.py
@Time    : 2020/5/11 9:59
@Author  : chen
前台蓝图文件:apps/front/views.py
"""
# 前台的蓝图文件  类视图函数写在这里
from flask import (Blueprint,render_template,views,make_response,                  # make_response生成response对象,用于返回前端模板request,session,g,
)# 导入图像验证码生成文件
from utils.captcha import Captcha# 图形验证码image是二进制数据,需要转换成字节流才能使用
from io import BytesIO# 将图形验证码保存到Redis         restful输出信息弹窗
from utils import redis_captcha, restful# 验证码表单信息验证
from .forms import (SignupForm,               # 注册的Form表单信息收集SigninForm,               # 登录的Form表单信息收集AddPostForm,              # 帖子提交表单信息AddCommentForm,           # 添加帖子评论
)# 导入前台用户模型
from .models import (Front_User,PostModel,CommentModel,             # 评论模型
)# 导入数据库连接 db
from exts import db# 确保URL安全的文件:utils/safe_url.py
from utils import safe_urlfrom apps.cms.models import (BannerModel,                      # 导入后台轮播图模型BannerModelBoardModel,                       # 导入后台板块管理模型HighlightPostModel,               # 帖子加精模型
)
# 导入分页功能库
from flask_paginate import Pagination, get_page_parameter
# 导入前台界面权限验证装饰器
from .decorators import login_required
# 导入配置文件
import config
from sqlalchemy import func                      # 导入求和方法front_bp = Blueprint("front", __name__)          # 前端不用前缀,直接在首页显示,front是蓝图,在front_signup.html调用生成图形验证码时候需要用# 权限验证  需要在front_bp产生后,再导入
from .hooks import before_request# BBS的首页界面路由
@front_bp.route("/")
def index():banners = BannerModel.query.order_by(BannerModel.priority.desc()).limit(4)   # 通过权重查询,每页显示4条boards = BoardModel.query.all()                                              # 查询板块中的所有board_id = request.args.get('board_id', type=int, default=None)              # get方法需要使用args,注意这里的数据类型需要改成int# 接收参数st的状态值,用于判断不同种类的排序sort = request.args.get('st', type=int, default=1)page = request.args.get(get_page_parameter(), type=int, default=1)           # 获取当前页码start = (page-1)*config.PER_PAGE                                             # 起始页码是(当前页码-1)*10end = start + config.PER_PAGE                                                # 每页都是起始页码+10# 接收参数st,根据sort的参数不同,判断不同种类的排序query_obj = Noneif sort == 1:                                                                # 用户选择最新帖子的顺序query_obj = PostModel.query.order_by(PostModel.create_time.desc())       # 帖子创建时间倒序elif sort == 2:                                                              # 用户选择精华帖子的顺序query_obj = db.session.query(PostModel).join(HighlightPostModel).order_by(  # 内关联PostModel,HighlightPostModel交集HighlightPostModel.create_time.desc()                                   # 根据创建时间倒序排序)                     # 帖子加精模型elif sort == 3:                                                              # 用户选择阅读最多的顺序query_obj = PostModel.query.order_by(PostModel.read_count.desc())        # 查询阅读数量elif sort == 4:                                                              # 用户选择评论最多的顺序query_obj = db.session.query(PostModel).join(CommentModel).group_by(PostModel.id                                                         # 先分组,根据不同帖子id进行分组再排序).order_by(func.count(CommentModel.id).desc()                                   # 通过评论总数排序)# 实现根据不同board_id进行帖子分类显示,即用户选择不同板块,显示的帖子种类相对应if board_id:posts = query_obj.filter(PostModel.board_id == board_id).slice(start, end)        # slice(start, end)分页total = query_obj.filter(PostModel.board_id == board_id).count()                  # 计算该板块的总数else:posts = query_obj.slice(start, end)                                    # 帖子信息传输,如果用户不选择板块,查询所有total = query_obj.count()                                              # 计算帖子总数# pagination是一个对象,bs_version=3是bootstrap的版本为3,per_page参数添加,pagination.links正常显示所有pagination = Pagination(bs_version=3, page=page, total=total,per_page=config.PER_PAGE,                          # config.py中的每页10条数据inner_window=3, outer_window=1)                    # inner_window=3是内层显示页码的样式,默认为2,# print(pagination.links)                                                  # 当数据量小的时候,不显示,添加per_page参数就能解决context = {                                                                # 多种数据传输到前台界面"banners": banners,"boards": boards,"current_board_id": board_id,"posts": posts,"pagination": pagination,"current_sort": sort,}return render_template("front/front_index.html", **context)            # 渲染到首页界面,查询数据传输到前台界面# 图形验证码路由
@front_bp.route("/captcha/")
def graph_captcha():try:                                                 # 异常处理# 图像验证码生成文件中返回两个参数   text, imagetext, image = Captcha.gene_graph_captcha()      # 生成图形验证码,image是二进制数据,需要转换成字节流才能使用print("发送的图形验证码是:{}".format(text))# 将图形验证码保存到Redis数据库中redis_captcha.redis_set(text.lower(), text.lower())  # redis_set中需要传参key和value,text没有唯一对应的key,只能都传参text# BytesIO是生成的字节流out = BytesIO()image.save(out, 'png')                          # 把图片image保存在字节流中,并指定为png格式# 文件流指针out.seek(0)                                     # 从字节流最初开始读取# 生成response对象,用于返回前端模板中resp = make_response(out.read())resp.content_type = 'image/png'                 # 指定数据类型except:return graph_captcha()                          # 没有生成验证码就再调用一次return resp                                         # 返回对象# 测试referrer的跳转
@front_bp.route("/test/")
def test():return render_template("front/front_test.html")# 用户注册类视图
class SingupView(views.MethodView):def get(self):# 图像验证码生成文件中返回两个参数   text, image# text, image = Captcha.gene_graph_captcha()# print(text)                      # 验证码# print(image)                     # 图形文件,图形类<PIL.Image.Image image mode=RGBA size=100x30 at 0x1EFC9000C88># 从当前页面跳转过来就是None   从其他页面跳转过来输出就是上一个页面信息     referrer是页面的跳转# print(request.referrer)                           # http://127.0.0.1:9999/test/return_to = request.referrer# 确保URL安全的文件:utils/safe_url.pyprint(safe_url.is_safe_url(return_to))              # 判断return_to是否来自站内,是否是安全url,防爬虫if return_to and return_to != request.url and safe_url.is_safe_url(return_to):       # 跳转的url不能是当前页面,request.url是当前的url地址return render_template("front/front_signup.html", return_to=return_to)           # return_to渲染到前端界面else:return render_template("front/front_signup.html")                                # 如果没获取url,直接渲染注册界面# 验证码的form表单信息提交验证def post(self):form = SignupForm(request.form)                       # 收集表单信息# 表单验证通过if form.validate():# 保存到数据库telephone = form.telephone.datausername = form.username.datapassword = form.password1.data                    # forms表单信息# 前台用户模型数据添加到数据库user = Front_User(telephone=telephone, username=username, password=password)db.session.add(user)db.session.commit()                                                   # 提交到数据库# 表单验证通过,提交到数据库成功return restful.success()else:return restful.params_error(message=form.get_error())                  # 表单信息验证出错# 用户登录的类视图
class SinginView(views.MethodView):def get(self):return_to = request.referrer                                                    # referrer是上一个urlif return_to and return_to != request.url and safe_url.is_safe_url(return_to):  # 跳转的url不能是当前页面,判断url是否安全return render_template("front/front_signin.html", return_to=return_to)      # return_to渲染到前端界面else:return render_template("front/front_signin.html")                           # 如果没获取url,直接渲染注册界面def post(self):form = SigninForm(request.form)                                            # 登录界面的Form表单信息if form.validate():                                                        # 表单信息存在# 收集form表单信息telephone = form.telephone.datapassword = form.password.dataremember = form.remember.datauser = Front_User.query.filter_by(telephone=telephone).first()         # 通过手机号验证该用户是否存在数据库if user and user.check_password(password):                             # 判断密码和用户是否正确# 'front_user_id'命名防止与后台验证session相同,会产生覆盖情况bugsession['front_user_id'] = user.id                                 # 用户的id存储到session中,用于登录验证if remember:                                                       # 如果remember状态是1# session持久化session.permanent = Truereturn restful.success()                                           # 成功else:return restful.params_error(message="手机号或者密码错误")           # 密码是、用户不正确else:return restful.params_error(message=form.get_error())                  # 表单信息不存在,输出异常信息#  帖子编辑提交  的类视图     富文本编辑
class PostView(views.MethodView):# 登录验证,实现帖子编辑前进行权限验证decorators = [login_required]# 表单信息收集,传输def get(self):# 查询boards数据进行传输boards = BoardModel.query.all()                                           # boards是list类型return render_template("front/front_apost.html", boards=boards)           # boards数据传输到前端front_apost.html页面# 帖子的Form表单信息收集查询def post(self):form = AddPostForm(request.form)                     # 查询帖子提交的Form表单信息if form.validate():title = form.title.databoard_id = form.board_id.data                    # 收集表单中提交的信息content = form.content.data# 查询用户信息是否在数据库中存在board = BoardModel.query.get(board_id)if not board:return restful.params_error(message="没有这个版块名称")           # 数据库中不存在,返回异常信息# 数据库中board信息存在,传输数据到数据库表中,并修改名称post = PostModel(title=title, board_id=board_id, content=content)post.board = board                                                   # 外键中的信息修改赋值post.author = g.front_user                                           # g对象db.session.add(post)db.session.commit()return restful.success()                                             # 提交成功,为json数据else:return restful.params_error(message=form.get_error())# 前台 帖子详情 路由
@front_bp.route("/p/<post_id>")                  # 蹄子详情路由需要传参帖子id:post_id
def post_detail(post_id):post = PostModel.query.get(post_id)          # 通过post_id查找数据库中的帖子信息if not post:return restful.params_error(message="帖子不存在!")# 阅读数量计数post.read_count += 1db.session.commit()# 评论数量查询,先查询帖子模型中的idcomment_count = db.session.query(PostModel).filter(PostModel.id == post_id).join(CommentModel                                                                     # 关联查询评论模型).count()                                                                            # 再整体求和return render_template("front/front_detail.html", post=post, comment_count=comment_count)  # 查找到帖子信息,传输数据到帖子详情页渲染# 添加评论  的路由
@front_bp.route("/acomment/", methods=['POST'])
@login_required                                      # 登录验证
def add_comment():form = AddCommentForm(request.form)              # 网页发送的request.form表单信息放入AddCommentForm进行验证if form.validate():content = form.content.data                  # form表单信息post_id = form.post_id.datapost = PostModel.query.get(post_id)          # 通过post_id查询帖子信息if post:comment = CommentModel(content=content)  # 将AddCommentForm中验证后的content信息传给CommentModel模型# 外键关联,反向属性backref,从CommentModel中调用comment.post = post                      # 外键关联的是post表中的id字段comment.author = g.front_user            # 将apps/front/hooks.py中的g对象赋值外键的作者的iddb.session.add(comment)                  # 添加对象信息到数据库db.session.commit()return restful.success()                 # 提交成功else:return restful.params_error(message="没有这篇帖子")  # 数据库中查询不到信息else:return restful.params_error(message=form.get_error())   # 表单验证失败# 绑定类视图的路由
front_bp.add_url_rule("/signup/", view_func=SingupView.as_view("signup"))          # "signup"视图中不需要反斜线,决定了url_for的路由地址
front_bp.add_url_rule("/signin/", view_func=SinginView.as_view("signin"))          # "signin"视图中不需要反斜线
front_bp.add_url_rule("/apost/", view_func=PostView.as_view("apost"))              # 绑定帖子编辑提交路由

前台首页页面文件:templates/front/front_index.html,接收传递的评论数据和阅读数据,不同排序方式的状态码定义。

<!-- 前台首页页面文件:templates/front/front_index.html  -->{% extends 'front/front_base.html' %}{% block title %}
首页
{% endblock %}<!-- 模板继承 -->
{% block main_content %}
<!--   居中样式  -->
<div class="main-container"><div class="lg-container"><!-- bootstrop中复制来的轮播图  --><div id="carousel-example-generic" class="carousel slide" data-ride="carousel"><!-- 指令 --><ol class="carousel-indicators"><li data-target="#carousel-example-generic" data-slide-to="0" class="active"></li><li data-target="#carousel-example-generic" data-slide-to="1"></li><li data-target="#carousel-example-generic" data-slide-to="2"></li></ol><!-- 轮播图 --><div class="carousel-inner" role="listbox"><!--    循环apps/front/views.py文件传输的banners数据      -->{% for banner in banners %}<!--    判断是否第一次循环      -->{% if loop.first %}<div class="item active">{% else %}<div class="item">{% endif %}<!--    轮播图路径,style="width: 300px;height: 300px"轮播图大小 --><img src="{{ banner.image_url }}" alt="..."style="width: 300px;height: 300px"><div class="carousel-caption"></div></div>{% endfor %}</div><!-- 轮播图左右切换按钮 --><a class="left carousel-control" href="#carousel-example-generic" role="button" data-slide="prev"><span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span><span class="sr-only">Previous</span></a><a class="right carousel-control" href="#carousel-example-generic" role="button" data-slide="next"><span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span><span class="sr-only">Next</span></a></div><!-- bootstrop中复制来的轮播图 代码结束   --><!--   帖子排序方式     --><div class="post-group"><ul class="post-group-head"><!--        根据不同种类进行排序,url中传输两个参数st和 board_id           -->{% if current_sort == 1 %}<li class="active"><a href="{{ url_for('front.index', st=1, board_id=current_board) }}">最新</a></li>{% else %}<li class=""><a href="{{ url_for('front.index', st=1, board_id=current_board) }}">最新</a></li>{% endif %}{% if current_sort == 2 %}<li class="active"><a href="{{ url_for('front.index', st=2, board_id=current_board) }}">精华帖子</a></li>{% else %}<li class=""><a href="{{ url_for('front.index', st=2, board_id=current_board) }}">精华帖子</a></li>{% endif %}{% if current_sort == 3 %}<li class="active"><a href="{{ url_for('front.index', st=3, board_id=current_board) }}">阅读最多</a></li>{% else %}<li class=""><a href="{{ url_for('front.index', st=3, board_id=current_board) }}">阅读最多</a></li>{% endif %}{% if current_sort == 4 %}<li class="active"><a href="{{ url_for('front.index', st=4, board_id=current_board) }}">评论最多</a></li>{% else %}<li class=""><a href="{{ url_for('front.index', st=4, board_id=current_board) }}">评论最多</a></li>{% endif %}</ul><ul class="post-list-group"><!--         循环帖子信息,首页渲染           -->{% for post in posts %}<li><div class="author-avatar-group"><img src="#" alt=""></div><div class="post-info-group"><p class="post-title"><!--  front.post_detail反转需要写的是路由的函数名,post_id=post.id传输帖子id,post_id是名字,post.id是value      --><a href="{{ url_for('front.post_detail', post_id=post.id) }}">{{ post.title }}</a><!--   精华帖判定条件:当前帖子存在highlight这个外联属性     -->{% if post.highlight %}<span class="label label-danger">精华帖</span>{% endif %}</p><p class="post-info"><!-- post模型中的author外键调用Front_User中的username信息  --><span>作者:{{ post.author.username }}</span><span>发表时间:{{ post.create_time }}</span><span>评论:{{ comment_count }}</span><span>阅读:{{ post.read_count }}</span></p></div></li>{% endfor %}</ul><divstyle="text-align:center;"><!--       页码分页展示, pagination.links数据由 apps/front/views.py传输过来           -->{{ pagination.links }}</div></div></div><!--      帖子标签内容      --><div class="sm-container"><divstyle="padding-bottom:10px;"><!--       重定向到/apost/路由,文本编辑界面        --><a href="{{ url_for('front.apost') }}" class="btn btn-warning btn-block">发布帖子</a></div><div class="list-group"><!--      保留st=current_sort用户当前选择的排序方式          --><a href="{{ url_for('front.index', st=current_sort ) }}" class="list-group-item active">所有板块</a><!--     循环显示前台蓝图文件:apps/front/views.py中传输的数据**context           -->{% for board in boards %}<!--         注意这里的current_board_id数据类型是int,才能与board.id相比较           -->{% if current_board_id == board.id %}<!--    url_for('front.index', board_id=board.id)每次点击跳转到front_index.html页面,即当前界面,且传输给一个board_id的参数值,由board.id赋值            --><a href="{{ url_for('front.index', board_id=board.id, st=current_sort ) }}" class="list-group-item active">{{ board.name }}</a>{% else %}<!--  没被选中,即没有被传输相同的board.id,图标样式是class="list-group-item">    --><a href="{{ url_for('front.index', board_id=board.id, st=current_sort  ) }}" class="list-group-item">{{ board.name }}</a>{% endif %}{% endfor %}</div></div></div><!--  居中样式  -->
{% endblock %}

帖子详情页面文件:templates/front/front_detail.html,接收传递的评论数据和阅读数据。

<!-- 帖子详情页面文件:templates/front/front_detail.html  -->
{% extends 'front/front_base.html' %}{% block title %}帖子详情{% endblock %}{% block head %}<!-- 百度的富文本编辑器加载 --><script src="{{ url_for('static', filename='ueditor/ueditor.config.js') }}"></script><script src="{{ url_for('static', filename='ueditor/ueditor.all.min.js') }}"></script><link rel="stylesheet" href="{{ url_for('static', filename='front/css/front_pdetail.css') }}"><script src="{{ url_for('static', filename='front/js/front_pdetail.js') }}"></script>
{% endblock %}{% block main_content %}
<div class="main-container"><div class="lg-container"><div class="post-container"><!--      帖子标题,前台蓝图文件:apps/front/views.py中的路由定义中传输过来      --><h2>{{ post.title }}</h2><p class="post-info-group"><span>发表时间:{{ post.create_time }}</span><!--  author和board这两个字段是PostModel的外键,关联了Front_User和BoardModel模型中的username、name字段       --><span>作者:{{ post.author.username }}</span><span>所属板块:{{ post.board.name }}</span><span>阅读数:{{ post.read_count }}</span><span>评论数:{{ comment_count }}</span></p><!--     data-id="{{ post.id }} 传输帖子id到front_pdetail.js文件进行获取      --><article class="post-content" id="post-content" data-id="{{ post.id }}"><!--   safe用于转义成安全字符串,content_html才能在页面渲染出标签的效果,content中包含有标签内容         -->{{ post.content_html|safe }}</article></div><div class="comment-group"><h3>评论列表</h3><ul class="comment-list-group"><!--  comments是反向引用的属性  -->{% for comment in post.comments %}<li><div class="avatar-group"><img src="{{ url_for('static', filename='common/images/logo.png') }}"alt=""></div><div class="comment-content"><p class="author-info"><!--   comment.author外键,从CommentModel中调用    --><span>{{ comment.author.username }}</span><span>{{ comment.create_time }}</span></p><p class="comment-txt">{{ comment.content|safe }}</p></div></li>{% endfor %}</ul></div><div class="add-comment-group"><h3>发表评论</h3><!--   这是绑定front_pdetail.js中的百度文本编辑器的id="editor",这里的标签是 script       --><script id="editor" type="text/plain"style="height:100px;"></script><div class="comment-btn-group"><!--    绑定id="comment-btn"  --><button class="btn btn-primary" id="comment-btn">发表评论</button></div></div></div><div class="sm-container"></div>
</div>
{% endblock %}

3、项目的完善

1、前台用户的导航栏功能

前台模板页面文件:templates/front/front_base.html,重定向到不同路由界面。

<!-- 前台模板页面文件:templates/front/front_base.html  --><!doctype html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport"content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><!--  在头文件中接收csrf信息  --><meta name="csrf-token" content="{{ csrf_token() }}"><title>{% block title %} {% endblock %}</title><!--  bootstrap支持css,js的样式  --><script src="http://cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script><link href="http://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"><script src="http://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script><!--  关联front_index.html的css样式  --><link href="{{ url_for('static', filename='front/css/front_index.css') }}" rel="stylesheet"><link href="{{ url_for('static', filename='front/css/front_base.css') }}" rel="stylesheet"><!--  提示框的静态资源文件  --><link rel="stylesheet" href="{{ url_for('static', filename='common/sweetalert/sweetalert.css') }}"><!-- 关联提示框的js样式  --><script src="{{ url_for('static', filename='common/sweetalert/lgalert.js') }}"></script><script src="{{ url_for('static', filename='common/sweetalert/sweetalert.min.js') }}"></script><!-- 导入lgajax.js文件  --><script src="{{ url_for('static', filename='common/lgajax.js') }}"></script><!--  模板继承,补充内容使用  -->{% block head %}{% endblock %}
</head>
<body>
<!--  bootstrop中复制来的导航条  -->
<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="#">BBS论坛</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="active"><a href="/">首页 <span class="sr-only">(current)</span></a></li></ul><form class="navbar-form navbar-left"><div class="form-group"><input type="text" class="form-control" placeholder="Search"></div><button type="submit" class="btn btn-default">搜索</button></form><ul class="nav navbar-nav navbar-right"><!--     判断是否登录,没有g对象信息就进行登录           -->{% if g.front_user %}<li class="dropdown"><!--   {{ g.front_user.username }}   显示g对象信息 --><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"aria-expanded="false">{{ g.front_user.username }}<span class="caret"></span></a><ul class="dropdown-menu"><li><a href="{{ url_for('cms.profile') }}">个人中心</a></li><li><a href="{{ url_for('cms.index') }}">设置</a></li><li><a href="{{ url_for('front.index') }}">退出登录</a></li></ul></li>{% else %}<!--    关联登录注册的url,front.signin是类视图中路由决定     --><li><a href="{{ url_for('front.signin') }}">登陆</a></li><li><a href="{{ url_for('front.signup') }}">注册</a></li>{% endif %}</ul></div><!-- /.navbar-collapse --></div><!-- /.container-fluid -->
</nav>
<!--   bootstrop中复制的导航条代码结束 --><!-- 模板继承 -->
{% block main_content %}{% endblock %}
</body>
</html>

2、评论前验证登录权限


3、帖子详情页面样式修改

前台帖子详情页面样式文件:static/front/css/front_pdetail.css

/*** // 前台帖子详情页面样式文件:static/front/css/front_pdetail.css*/
.post-container{border: 1px solid #e6e6e6;padding: 10px;
}.post-info-group{font-size: 12px;color: #8c8c8c;border-bottom: 1px solid #e6e6e6;margin-top: 15px;padding-bottom: 10px;
}.post-info-group span{margin-right: 20px;
}.post-content{margin-top: 50px;
}.post-content img{max-width: 100%;
}.comment-group{margin-top: 20px;border: 1px solid #e8e8e8;padding: 10px;
}.add-comment-group{margin-top: 20px;padding: 10px;border: 1px solid #e8e8e8;
}.add-comment-group h3{margin-bottom: 10px;
}.comment-btn-group{margin-top: 10px;text-align:right;
}.comment-list-group li{overflow: hidden;padding: 10px 0;border-bottom: 1px solid #e8e8e8;
}.avatar-group{float: left;
}.avatar-group img{width: 50px;height: 50px;border-radius: 50%;
}.comment-content{float: left;margin-left:10px;
}.comment-content .author-info{font-size: 12px;color: #8c8c8c;
}.author-info span{margin-right: 10px;
}.comment-content .comment-txt{margin-top: 10px;
}

4、引入Celery异步发送邮件

Celery是Python开发的分布式任务调度模块,Celery本身不含消息服务,它使用第三方消息服务来传递任务。单个 Celery 进程每分钟可处理数以百万计的任务,而保持往返延迟在亚毫秒级。目前,Celery支持的消息服务有RabbitMQ、Redis甚至是数据库,当然Redis是最佳选择。


安装Celery

pip install Celery

clery的任务执行文件:task.py

# -*- encoding: utf-8 -*-
"""
@File    : task.py
@Time    : 2020/6/6 20:37
@Author  : chen
clery的任务执行文件:task.py
"""
from celery import Celery
from flask_mail import Message
from exts import mail
from flask import Flask
import configapp = Flask(__name__)                 # 新创建一个app用于执行clery异步发送,为了防止互相引用mail.init_app(app)                    # 初始化def make_celery(app):                 # 使用clery运行appcelery = Celery(app.import_name,backend=config.CELERY_BROKER_URL,broker=config.CELERY_RESULT_BACKEND# backend=app.config['CELERY_RESULT_BACKEND'],# broker=app.config['CELERY_BROKER_URL'])celery.conf.update(app.config)              # 导入配置文件class ContextTask(celery.Task):def __call__(self, *args, **kwargs):with app.app_context():return self.run(*args, **kwargs)celery.Task = ContextTaskreturn celerycelery = make_celery(app)@celery.task
def send_mail(subject, recipients, body):message = Message(subject=subject, recipients=recipients, body=body)mail.send(message)

项目配置文件:config.py

# -*- encoding: utf-8 -*-
"""
@File    : config.py
@Time    : 2020/5/11 10:08
@Author  : chen
项目配置文件:config.py
"""CELERY_BROKER_URL = "redis://127.0.0.1:6379/0"
CELERY_RESULT_BACKEND = "redis://127.0.0.1:6379/0"

Redis服务开启:

视图文件:apps/cms/views.py文件,修改发送邮件的方法,改为clery模块中的send_mail方法异步发送。

# -*- encoding: utf-8 -*-
"""
@File    : views.py
@Time    : 2020/5/11 9:59
@Author  : chen
视图文件:apps/cms/views.py文件
"""
# 蓝图文件:实现模块化应用,应用可以分解成一系列的蓝图   后端的类视图函数写在这个文件
from flask import (request, redirect, url_for,                      # 页面跳转redirect   request请求收集Blueprint, render_template, views, session,      # 定义类视图,显示模板文件jsonify, g                                       # jsonify强制转换成json数据
)
from exts import db, mail                            # 数据库中更新密码、邮箱等使用from apps.cms.forms import (LoginForm, ResetPwdForm,                        # ResetPwdForm修改密码的form信息ResetEmailForm,                                 # 导入forms.py文件中的邮箱验证的表单信息类AddBannerForm,                                  # 导入 添加轮播图 的表单信息UpdateBannerForm,                               # 导入 更新轮播图 的表单信息AddBoardsForm,                                  # 导入 增加板块管理 的表单信息UpdateBoardsForm,                               # 导入 编辑板块管理 的表单信息
)from apps.cms.models import (CMS_User,                                       # 后台用户模型CMSPersmission,                                 # CMSPersmission验证用户不同模块权限CMSRole,                                        # 用户角色模型BannerModel,                                    # 导入 轮播图模型BannerModelBoardModel,                                     # 导入 板块管理模型HighlightPostModel,                             # 帖子加精模型
)
# 导入 帖子 模型文件
from apps.front.models import PostModelfrom .decorators import permission_required            # 传参装饰器验证用户不同模块权限# 导入装饰器:判断当前界面是否是登录界面,不是就将url重定向到登录界面,一般不用,使用的主要是钩子函数
from .decorators import login_required# 导入restful.py中的访问网页状态码的函数          redis_captcha:redis存储、提取、删除验证码功能
from utils import restful, random_captcha, redis_captcha           # 随机生成验证码函数random_captcha()# 导入flask-mail中的Message
from flask_mail import Message# 导入clery的异步发送邮件模块
from task import send_mailcms_bp = Blueprint("cms", __name__, url_prefix='/cms/')     # URL前缀url_prefix# 钩子函数是在cms_bp创建之后才创建的,顺序在cms_bp创建之后
from .hooks import before_request@cms_bp.route("/")                                          # 后台界面
# @login_required             # 装饰器判定当前界面是否是登录界面,但是需要每个路由函数都要加该装饰器,比较麻烦,推荐使用钩子函数
def index():# return "cms index:后端类视图文件"return render_template('cms/cms_index.html')  # 登陆之后进入CMS后台管理界面# 用户注销登录
@cms_bp.route("/logout/")                              # 需要关联到cms/cms_index.html中的注销属性
def logout():# session清除user_iddel session['user_id']# 重定向到登录界面return redirect(url_for('cms.login'))             # 重定向(redirec)为把url变为重定向的url# 定义个人中心的路由
@cms_bp.route("/profile/")
def profile():return render_template("cms/cms_profile.html")   # 模板渲染(render_template)则不会改变url,模板渲染是用模板来渲染请求的url# 定义类视图,显示模板文件   用户登录功能实现
class LoginView(views.MethodView):def get(self, message=None):                                         # message=None时候不传输信息到cms_login.html页面return render_template("cms/cms_login.html", message=message)    # 针对post方法中同样要返回到cms_login.html页面进行代码简化# 用户登录操作验证def post(self):# 收集表单信息login_form = LoginForm(request.form)if login_form.validate():# 数据库验证email = login_form.email.datapassword = login_form.password.dataremember = login_form.remember.data# 查询数据库中的用户信息user = CMS_User.query.filter_by(email=email).first()    # 邮箱唯一,用于查询验证用户if user and user.check_password(password):              # 验证用户和密码是否都正确session['user_id'] = user.id                        # 查询到用户数据时,保存session的id到浏览器# session['user_name'] = user.username                # 将数据库中的user.username保存到session中,在hooks.py中判断# session['user_email'] = user.email                  # 将数据库中的email保存到session中,方便html调用信息# session['user_join_time'] = user.join_time          # 将数据库中的join_time保存到session中,方便html调用信息if remember:                                        # 如果用户点击了remember选择,在浏览器中进行数据持久化session.permanent = True                        # 数据持久化,默认31天,需要设置session_key在config.py中# 登录成功,跳转到后台首页return redirect(url_for('cms.index'))               # 在蓝图中必须加cms   跳转到index方法else:# return "邮箱或密码错误"                              # 登录出错,返回结果# return render_template("cms/cms_login.html", message="邮箱或密码错误")  # 登录出错,返回结果渲染到cms_login.html页面return self.get(message="邮箱或密码错误")             # 传参到get方法中,多加一个传输错误信息的参数到方法中else:# print(login_form.errors)                                 # forms.py中的错误信息  字典类型数据# print(login_form.errors.popitem())                       # forms.py中的错误信息  元祖类型数据# return "表单验证错误"                                     # 错误信息需要渲染到cms_login.html页面# return self.get(message=login_form.errors.popitem()[1][0])  # 字典类型数据信息提取return self.get(message=login_form.get_error())            # login_form是收集到的表单信息,信息提取放置到forms.py的父类中实现# 修改密码的类视图验证
class ResetPwd(views.MethodView):def get(self):return render_template('cms/cms_resetpwd.html')         # 模板渲染到cms_resetpwd.html# post提交密码修改def post(self):# 先审查旧密码是否与数据库中的信息相同form = ResetPwdForm(request.form)if form.validate():oldpwd = form.oldpwd.datanewpwd = form.newpwd.data# 对象user = g.cms_user# 将用户输入的密码进行加密检测是否与数据库中的相同if user.check_password(oldpwd):# 更新我的密码  将新密码赋值,此时的新密码已经经过验证二次密码是否一致user.password = newpwd         # user.password已经调用了models.py中的 @property装饰器进行密码加密# 数据库更新db.session.commit()# return jsonify({"code": 400, "message": "密码修改成功"})        # 代码改写为下面return restful.success("密码修改成功")             # 调用restful.py中定义的访问网页成功的函数else:# 当前用户输入的旧密码与数据库中的不符# return jsonify({"code": 400, "message": "旧密码输入错误"})return restful.params_error(message="旧密码输入错误")      # 参数错误else:# ajax 需要返回一个json类型的数据# message = form.errors.popitem()[1][0]                     # 收集错误信息# return jsonify({"code": 400, "message": message})         # 将数据转换成json类型return restful.params_error(message=form.get_error())       # 参数错误,信息的收集在forms.py的父类函数中实现  form是收集到的信息# 定义修改邮箱的类视图 验证
class ResetEmail(views.MethodView):def get(self):return render_template("cms/cms_resetemail.html")      # 返回到修改邮箱页面urldef post(self):form = ResetEmailForm(request.form)                    # 接收邮箱验证的form表单信息if form.validate():                                    # 验证表单信息是否通过email = form.email.data                            # 获取form表单中填写的邮箱地址# 查询数据库# CMS_User.query.filter_by(email=email).first()# CMS_User.query.filter(CMS_User.email == email).first()g.cms_user.email = email                           # 数据库中的查询在apps/cms/hooks.py文件中确定了该用户的数据库信息,用全局对象g.cms_user修改邮箱db.session.commit()return restful.success()                           # 邮箱修改成功else:return restful.params_error(form.get_error())      # form是这个类中的所有表单信息# 发送测试邮件进行验证
@cms_bp.route("/send_email/")
def send_mail_1():                                                  # 防止与clery中异步发送邮件方法名相同message = Message('邮件发送', recipients=['727506892@qq.com'], body='测试邮件发送')   # 主题:邮件发送;收件人:recipients;邮件内容:测试邮件发送mail.send(message)                   # 发送邮件return "邮件已发送"# 邮件发送
class EmailCaptcha(views.MethodView):def get(self):                                  # 根据resetemail.js中的ajax方法来写函数,不需要post请求email = request.args.get('email')           # 查询email参数是否存在if not email:return restful.params_error('请传递邮箱参数')# 发送邮件,内容为一个验证码:4、6位数字英文组合captcha = random_captcha.get_random_captcha(4)            # 生成4位验证码# message = Message('BBS论坛邮箱验证码', recipients=[email], body='您的验证码是:%s' % captcha)# 异常处理try:send_mail.delay('BBS论坛邮箱验证码', recipients=[email], body='您的验证码是:%s' % captcha)   # 引用clery异步发送邮件# mail.send(message)except:return restful.server_error(message="服务器错误,邮件验证码未发送!")   # 发送异常,服务器错误# 验证码保存,一般有时效性,且频繁请求变化,所以保存在Redis中redis_captcha.redis_set(key=email, value=captcha)        # redis中都是键值对类型,存储验证码return restful.success("邮件验证码发送成功!")# 轮播图管理路由
@cms_bp.route("/banners/")
def banners():# 通过模型中定义的权重priority的倒叙来排序banners = BannerModel.query.order_by(BannerModel.priority.desc()).all()return render_template("cms/cms_banners.html", banners=banners)           # 传输banners数据到cms_banners.html界面渲染# 添加轮播图功能路由,且方法需要与static/cms/js/banners.js中绑定的方法POST相同
@cms_bp.route("/abanner/", methods=['POST'])
def abanner():form = AddBannerForm(request.form)                  # 接收添加轮播图的form表单信息if form.validate():name = form.name.dataimage_url = form.image_url.datalink_url = form.link_url.datapriority = form.priority.databanner = BannerModel(name=name, image_url=image_url, link_url=link_url, priority=priority)     # 轮播图模型db.session.add(banner)                                                                         # 提交数据库db.session.commit()return restful.success()                                                                       # 轮播图信息提交成功else:return restful.params_error(message=form.get_error())                                          # 表单信息错误# 修改 轮播图 路由,方法与static/cms/js/banners.js中绑定的方法POST相同
@cms_bp.route("/ubanner/", methods=['POST'])
def ubanner():# 修改根据banner_id查询再修改form = UpdateBannerForm(request.form)             # 表单信息UpdateBannerForm中的requestif form.validate():                                # 先查询页面表单信息是否存在banner_id = form.banner_id.data               # 收集用户输入的表单信息name = form.name.dataimage_url = form.image_url.datalink_url = form.link_url.datapriority = form.priority.databanner = BannerModel.query.get(banner_id)     # 通过轮播图的模型BannerModel的banner_id查询数据库中轮播图对象if banner:                                     # 再查询数据库对象数据是否存在banner.name = name                        # 将UpdateBannerForm中收集到的form信息命名给数据库中的banner对象banner.image_url = image_urlbanner.link_url = link_urlbanner.priority = prioritydb.session.commit()                       # 数据库信息直接提交修改即可,不用添加新的对象return restful.success()else:return restful.params_error(message=form.get_error())    # 表单信息错误# 删除  轮播图路由,路由命名与banners.js绑定
@cms_bp.route("/dbanner/", methods=['POST'])
def dbanner():'''request.form.get("key", type=str, default=None)      获取表单数据request.args.get("key")                              获取get请求参数request.values.get("key")                            获取所有参数'''# 修改根据banner_id查询再修改,获取post请求参数         get请求方式使用request.args.get()banner_id = request.form.get('banner_id')            # 获取表单数据,这里没有单独创建删除的Form表单,使用之前创建的if not banner_id:return restful.params_error(message="轮播图不存在")banner = BannerModel.query.get(banner_id)           # 根据banner_id查询数据库if banner:db.session.delete(banner)                       # 删除该bannerdb.session.commit()return restful.success()                        # 返回成功else:return restful.params_error("轮播图不存在")      # 根据banner_id查询数据库信息不存在# 帖子管理路由 ,需要和cms_base.js中命名的相同才可以
@cms_bp.route("/posts/")
@permission_required(CMSPersmission.POSTER)                # 传参装饰器验证不同用户不同模块权限
def posts():posts = PostModel.query.all()                          # 数据库查询帖子信息,进行传输到后端页面cms_posts.html渲染return render_template("cms/cms_posts.html", posts=posts)# 帖子 加精的 后台管理,路由名称在static/cms/js/posts.js文件定义好了
@cms_bp.route("/hpost/", methods=['POST'])                # 方法确定为post方式,默认支持的是get方法
@permission_required(CMSPersmission.POSTER)                # 传参装饰器验证不同用户不同模块权限
def hpost():# 接收外键,post接收方式使用formpost_id = request.form.get("post_id")                  # 接收post_id进行查询if not post_id:return restful.params_error(message="请输入帖子ID")post = PostModel.query.get(post_id)                    # 从帖子的数据表中查找该帖子对象if not post:return restful.params_error(message="没有这篇帖子")highlight = HighlightPostModel()                      # 创建模型highlight.post = post                                 # 外键关联,加精帖子补充到新的表中db.session.add(highlight)db.session.commit()                                   # 提交return restful.success()                              # 加精成功,视图函数必须有返回值# 帖子 取消加精的 后台管理,路由名称在static/cms/js/posts.js文件定义好了
@cms_bp.route("/uhpost/", methods=["POST"])               # 方法确定为post方式,默认支持的是get方法
@permission_required(CMSPersmission.POSTER)                # 传参装饰器验证不同用户不同模块权限
def uhpost():# 接收外键,post接收方式使用formpost_id = request.form.get("post_id")                  # 接收post_id进行查询if not post_id:return restful.params_error(message="请输入帖子ID")post = PostModel.query.get(post_id)                     # 从帖子的数据表中查找该帖子对象if not post:return restful.params_error(message="没有这篇帖子")highlight = HighlightPostModel.query.filter_by(post_id=post_id).first()db.session.delete(highlight)db.session.commit()                                     # 提交return restful.success()                                # 视图函数必须有返回值# 评论管理路由
@cms_bp.route("/comments/")
@permission_required(CMSPersmission.COMMENTER)             # 传参装饰器验证不同用户不同模块权限
def comments():return render_template("cms/cms_comments.html")# 板块管理路由
@cms_bp.route("/boards/")
@permission_required(CMSPersmission.BOARDER)               # 传参装饰器验证不同用户不同模块权限
def boards():boards = BoardModel.query.all()                        # 数据库查询所有板块名称return render_template("cms/cms_boards.html", boards=boards)        # 数据渲染到cms_boards.html# 增加 板块管理名称 路由,与static/cms/js/banners.js中绑定的方法、路由要相同
@cms_bp.route("/aboard/", methods=['POST'])
@permission_required(CMSPersmission.BOARDER)               # 传参装饰器验证不同用户不同模块权限
def aboards():form = AddBoardsForm(request.form)                     # 表单信息传输过来,方便修改调用if form.validate():name = form.name.data                              # 表单信息收集board = BoardModel(name=name)                      # 添加信息到板块模型中db.session.add(board)db.session.commit()                                # 提交数据库return restful.success()                           # 数据库添加成功else:return restful.params_error(message=form.get_error())    # 表单信息错误# 编辑 板块管理名称 路由,与static/cms/js/banners.js中绑定的方法、路由要相同
@cms_bp.route("/uboard/", methods=['POST'])
@permission_required(CMSPersmission.BOARDER)             # 传参装饰器验证不同用户不同模块权限
def uboards():form = UpdateBoardsForm(request.form)                # 表单信息传输过来,方便修改调用if form.validate():board_id = form.board_id.data                    # 表单信息收集name = form.name.databoard = BoardModel.query.get(board_id)           # 根据表单中提交的board_id查询数据库中对象信息if board:board.name = name                            # 表单中提交的name命名给数据库中对象的名字db.session.commit()                          # 修改数据后提交数据库return restful.success()                     # 数据库修改成功else:return restful.params_error(message="没有这个分类板块")  # 数据库中对象信息不存在else:return restful.params_error(message=form.get_error())  # 表单信息错误# 删除 板块管理名称 路由,与static/cms/js/banners.js中绑定的方法、路由要相同
@cms_bp.route("/dboard/", methods=['POST'])
@permission_required(CMSPersmission.BOARDER)             # 传参装饰器验证不同用户不同模块权限
def dboards():board_id = request.form.get('board_id')                  # 查询表单信息中的board_id,这里没有单独创建删除的Form表单,使用之前创建的if not board_id:return restful.params_error(message="分类板块不存在")  # 表单信息不存在board = BoardModel.query.get(board_id)               # 根据表单中提交的board_id查询数据库中对象信息,注意.getif not board:return restful.params_error(message="分类板块不存在")  # 数据库中对象信息不存在db.session.delete(board)                             # 删除数据库中的信息db.session.commit()                                  # 提交数据库修改return restful.success()                             # 删除成功# 前台用户管理路由
@cms_bp.route("/fusers/")
@permission_required(CMSPersmission.FRONTUSER)             # 传参装饰器验证不同用户不同模块权限
def fuser():return render_template("cms/cms_fuser.html")# 后用户管理路由
@cms_bp.route("/cusers/")
@permission_required(CMSPersmission.CMSUSER)               # 传参装饰器验证不同用户不同模块权限
def cuser():return render_template("cms/cms_cuser.html")# 添加登录路由
cms_bp.add_url_rule("/login/", view_func=LoginView.as_view('login'))    # view_func 命名操作名字,"/login/"路由地址# 类视图函数添加绑定路由  注意类视图需要修改ResetPwd.as_view('resetpwd')
cms_bp.add_url_rule("/resetpwd/", view_func=ResetPwd.as_view('resetpwd'))  # view_func 命名操作名字,/resetpwd/路由地址# 添加修改邮箱的类视图路由绑定,路由的命名和cms_base.js中的命名要相同,否则不关联,url=/resetemail/必须要和resetemail.js中的ajax绑定的路由相同
cms_bp.add_url_rule("/resetemail/", view_func=ResetEmail.as_view('resetemail'))# 绑定路由,路由的命名和cms_base.js中的命名要相同,必须要和resetemail.js中的ajax绑定的路由相同
cms_bp.add_url_rule("/email_captcha/", view_func=EmailCaptcha.as_view('email_captcha'))

命令行执行下面文件:

# 先导入future模块
pip install future# Window系统下执行
celery -A task.celery worker --loglevel=info --pool=solo# linux系统
celery -A task.celery worker --loglevel=info


运行项目后修改邮箱,测试邮箱验证码发送是否正常:

再查看Clery监控程序:

目前项目中已经引入了Clery异步发送邮件的功能。

Flask项目实战——12—(帖子评论和阅读数功能、帖子分类功能实现、项目完善、引入Celery异步发送邮件)相关推荐

  1. html5 签到系统功能,项目实战之基于JavaScript实现每日签到打卡轨迹功能

    今天扣丁学堂HTML5培训老师给大家介绍一下关于js实现每日签到打卡轨迹功能的具体代码,希望对同学们学习HTML5开发有所帮助,下面我们一起来看一下吧. 1.核心文件calendar.js var c ...

  2. Gavin老师Transformer直播课感悟 - Rasa项目实战之银行金融Financial Bot智能业务对话机器人业务功能微服务解析与调试(八十九)

    本文继续围绕工业级业务对话平台和框架Rasa,对Rasa项目实战之银行金融Financial Bot智能业务对话机器人的主要业务功能所使用的微服务进行解析,并通过Rasa Interactive的调试 ...

  3. Gavin老师Transformer直播课感悟 - Rasa项目实战之银行金融Financial Bot智能业务对话机器人业务功能微服务解析与调试(八十一)

    本文继续围绕工业级业务对话平台和框架Rasa,对Rasa项目实战之银行金融Financial Bot智能业务对话机器人的主要业务功能所使用的微服务进行解析,并通过Rasa Interactive的调试 ...

  4. Django通过celery 异步发送邮件 : django开发之天天生鲜项目知识总结【5】

    这里初次学习celery,只简单讲解一下如何使用celery 异步发送邮件,在以后的总结中还会,多次提到celery,因为后面很多任务都需要用到celery执行任务,后面再专门针对celery做具体的 ...

  5. 项目实战12.1—企业级监控工具应用实战-zabbix安装与基础操作

    无监控,不运维.好了,废话不多说,下面都是干货. 警告:流量党勿入,图片太多!!! 项目实战系列,总架构图 http://www.cnblogs.com/along21/p/8000812.html ...

  6. 【项目实战课】基于Pytorch的EfficientNet血红细胞分类竞赛实战

    欢迎大家来到我们的项目实战课,本期内容是<基于Pytorch的EfficientNet血红细胞分类竞赛实战>.所谓项目课,就是以简单的原理回顾+详细的项目实战的模式,针对具体的某一个主题, ...

  7. Rasa项目实战之银行金融Financial Bot智能业务对话机器人业务功能微服务解析与调试(九十二)

    一.Rasa项目实战之银行金融Financial Bot智能业务对话机器人支付业务流程解析与演示 支付业务是银行金融业务中的核心功能,对于本项目来说,先来看一下这个业务流程是如何工作的.首先运行Ras ...

  8. 【项目实战】Python基于孤立森林算法(IsolationForest)实现数据异常值检测项目实战

    说明:这是一个机器学习实战项目(附带数据+代码+文档+代码讲解),如需数据+代码+文档+代码讲解可以直接到文章最后获取. 1.项目背景 孤立森林是基于Ensemble的快速异常检测方法,具有线性时间复 ...

  9. 【项目实战】Python基于波动率模型(ARCH和GARCH)进行股票数据分析项目实战

    说明:这是一个机器学习实战项目(附带数据+代码+文档+代码讲解),如需数据+代码+文档+代码讲解可以直接到文章最后获取. 1.项目背景 在衍生产品定价和风险管理中,对当前波动率是很感兴趣的,这是因为需 ...

最新文章

  1. 针对《评人工智能如何走向新阶段》一文,继续发布国内外的跟贴留言427-438条如下:
  2. Instr()函数用法
  3. 前端学习(2678):懂代码之表格BaseTable编辑操作
  4. 中科院硕士起诉导师,二审败诉
  5. tomcat监控脚本
  6. 《零基础》MySQL LIKE 子句(十六)
  7. linux vi脚本,linux下vi(vim)的新的用法总结
  8. ES5(一)——保护对象
  9. 一不小心,老司机又翻车了
  10. android Linkify的用法
  11. Codeforces 1039D You Are Given a Tree (看题解)
  12. 《Linux内核设计与实现》读书笔记 第一章 Linux内核简介
  13. linux操作系统开机启动,简述linux操作系统启动流程
  14. 自动化框架应该具备的要点
  15. C++中清理map的代码
  16. 基于单片机的数控直流稳压电源设计
  17. 电脑桌面老是弹出计算机,电脑桌面老是弹出游戏怎么解决
  18. Python:实现gauss easte高斯复活节日期算法(附完整源码)
  19. b500k带开关电位器内部构造_b500k开关摇杆电位器,直滑电位器103
  20. 如何正确的撰写软文的结尾?

热门文章

  1. MySQL 嵌套JSON解析
  2. 阿里旺旺分析系列一:实时获取阿里旺旺聊天消息,实现旺旺客服机器人
  3. 为什么说麻将比围棋难?游戏AI复杂度怎么算
  4. 服务器的学习(Windows Server)
  5. 京东×欧莱雅:用5年,打造了化妆品电商神话
  6. 科技译文mysql数据库_中国科技翻译杂志
  7. 五个方法,让你站着把钱赚了!
  8. 简单的均线突破交易系统
  9. MallBook成功备案中国支付清算协会聚合支付技术服务机构
  10. 什么是 AirServer?2023最新版本有哪些新功能