文章目录
由于自己目前还在实习上班中,只能周末抽空做毕设,之前用Python做过Flask框架的教务系统,有一些经验,所以偷懒选用Flask框架做了一个简单的视频网站,如果有时间的话可以弄一个推荐算法在里面,做一步算一步吧!
自己写点日志记录下项目过程吧!
具体详情放在自己搭的博客上面了: 传送门
使用技术:
Python MTV模型
Flask微内核
Flask扩展插件配置及使用方法
根据业务开发网站前后台功能
Flask结合MySQL数据库
你将可以独立开发网站 独立部署运维网站
werkzug工具箱
pymysql数据库驱动
sqlalchemy数据库orm
wtforms表单验证工具
jinjia2模板引擎
flask-script命令行脚本
functools定义高阶函数
jwplayer播放器插件
视频限速限IP访问
flv、mp4视频格式支持
Nginx点播实现
模块
前台
会员登录及注册 / 会员中心 / 电影播放
电影评论 / 电影收藏
后台
管理员登录 / 修改密码 / 标签管理
电影管理 / 上映预告管理 / 会员管理
评论管理 / 收藏管理 / 角色管理
权限管理 / 管理员管理 / 日志管理
10.18
熟悉整个项目的部署,流程,以及所用到的库
10.19
使用flask的SQLAlchemy,创建Mysql数据库表,并创建了启动项目所需要前台home,后台admin,蓝图等等
部分mysql表
蓝图的建立
10.26
建立home.html 整个网站的框架,逐渐添加各种功能
电影:index.html
- 由logout.html(负责电影首页类目类目)
- animation.html(热映电影+轮播图)
- 利用jinjia2的语法
{% for v in range(1,13) %}
构造电影信息列表
搜索:search.html 搜索页面
登录:login.html
注册:regist.html
退出:login.html
会员:user.html
-
基于home.html
-
menu.html为会员中心的类目 id="m-*"为各种功能的id,每种功能js执行代码如下:
<% block js %> <script> $(document).ready(function () { $("#m-4").addClass("active"); }) </script> <% endblock %>
10.27
前端
404.html 报错页面
play.html 电影播放页面 有bug 无法弹出播放列表,待修复
后端
admin.html 后台整体界面
gird.html 后台类目
login.html 管理员登陆页面
因为工作忙,好久没写项目了,今天补一下
11.16
admin.html 主模板,其余继承主模板,添加主模板的功能
5-1 管理员登录-后台布局搭建
- 功能模块继承admin.gird.html 其各个功能需要添加id 定位,点击功能激活子功能
5-2 修改密码-控制面板-标签管理页面搭建
- pwd.html,index.html,tag.html-tag_add.html
5-3 电影管理-上映预告管理页面搭建
- movie_add.html,movie_add.html ,preview_list.html
5-4 会员-收藏-评论管理页面搭建
- user_list.html,movie_col.html,comment_list.html
5-5 收藏-日志-角色管理页面搭建
- role_add,role_list.html
5-6 管理员管理页面搭建 - admin_add.html,admin_list.html
11.17
6-1 管理员登陆
- 1.在model.py中添加check_pwd()函数,判断密码是否一致
- 2.在form.py中添加表单需要的信息、是否存在账户。在view.py视图中调用,判断登录状态,登录失败,返回错误信息。登录成功,建立session并返回index.html中
def login():
"""
后台登录
"""
form = LoginForm()
if form.validate_on_submit(): #表单数据
data = form.data
admin = Admin.query.filter_by(name=data["account"]).first()#查找admin
if not admin.check_pwd(data['pwd']):
flash("密码错误", "err")
return redirect(url_for("admin.login"))
session['admin'] = data['account']
return redirect(request.args.get("next") or url_for("admin.index"))
return render_template("admin/login.html", form=form)
访问控制,登录装饰器
def admin_log_req(f):
"""
登录装饰器
"""
@wraps(f)
def decorated_function(*args, **kwargs):
if "admin" not in session:
return redirect(url_for("admin.login", next=request.url))
return f(*args, **kwargs)
return decorated_function
6-2 标签的添加与编辑
- 添加标签、编辑
def tag_add():
form = TagForm()
print(form.name.errors)
if form.validate_on_submit():
data=form.data
tag=Tag.query.filter_by(name=data["name"]).count()#数据库查找信息
if tag==1:
flash("名称已经存在!","err")
return redirect(url_for('admin.tag_add'))
tag=Tag(
name=data["name"]
)
db.session.add(tag)
db.session.commit()
flash("标签添加成功", "ok") #闪现消息
redirect(url_for("admin.tag_add"))
return render_template("admin/tag_add.html", form=form)
@admin.route("/tag/edit/<int:id>", methods=["GET", "POST"])
@admin_log_req
# @admin_auth
def tag_edit(id=None):
"""
标签编辑
"""
form = TagForm()
form.submit.label.text = "修改"
tag = Tag.query.get_or_404(id) #数据库依据id查找tag
if form.validate_on_submit():
data = form.data
tag_count = Tag.query.filter_by(name=data["name"]).count()
# 说明已经有这个标签了,此时向添加一个与其他标签重名的标签。
if tag.name != data["name"] and tag_count == 1:
flash("标签已存在", "err")
return redirect(url_for("admin.tag_edit", id=tag.id))
tag.name = data["name"]
db.session.add(tag)
db.session.commit()
flash("标签修改成功", "ok")
redirect(url_for("admin.tag_edit", id=tag.id))
return render_template("admin/tag_edit.html", form=form, tag=tag)
- for循环接收form表单、闪现消息
{% for msg in get_flashed_messages(category_filter=["ok"]) %}
<div class="alert alert-success alert-dismissible">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×
</button>
<h4><i class="icon fa fa-check"></i> 操作成功</h4>
{{ msg }}
</div>
{% endfor %}
6.3 标签列表的分页设置
#标签列表
@admin.route("/tag/list/<int:page>/", methods=["GET"])
@admin_log_req
def tag_list(page=None):
if page is None:
page=1
page_data=Tag.query.order_by(
Tag.addtime.desc() #按添加时间排序
).paginate(page=page,per_page=10)
return render_template("admin/tag_list.html",page_data=page_data)
{% macro page(data,url) -%}
<!--判断数据是否存在-->
{% if data %}
<ul class="pagination pagination-sm no-margin pull-right">
<li><a href="{{ url_for(url,page=1) }}">首页</a></li>
{% if data.has_prev %}
<!-- data.prev_num 获取上一页页码-->
<li><a href="{{ url_for(url,page=data.prev_num) }}">上一页</a></li>
{% else %}
<!-- 没有上一页-->
<li class="disabled"><a href="#">上一页</a></li>
{% endif %}
<!-- iter_pages() 页码生成器-->
{% for v in data.iter_pages() %}
{% if v %}
{% if v == data.page %}
<li class="active"><a href="#">{{ v }}</a></li>
{% else %}
<li><a href="{{ url_for(url,page=v) }}">{{ v }}</a></li>
{% endif %}
{% endif %}
{% endfor %}
<!-- 下一页-->
{% if data.has_next %}
<li><a href="{{ url_for(url,page=data.next_num) }}">下一页</a></li>
{% else %}
<li class="disabled"><a href="#">下一页</a></li>
{% endif %}
<li><a href="{{ url_for(url,page=data.pages) }}">尾页</a></li>
</ul>
{% endif %}
{%- endmacro %}
12.25
6.5
- 完善预告页面的增删
- 在编辑与删除成功时,自动删除原预告
12.26
9-1上映预告-标签筛选-电影分页
跳过其他管理员后台功能的完善,先完善前台页面与视频播放的模块,早些学习新知识。
- 完善home.index页面的搭建(分页)
- home.animation 轮播图的建立(有问题)!!!!!
- 电影列表的建立,以及筛选条件的完善
@home.route("/<int:page>/", methods=["GET"])
def index(page=None):
"""
首页电影列表
"""
tags = Tag.query.all()
page_data = Movie.query
# 标签
tid = request.args.get("tid", 0)
if int(tid) != 0:
page_data = page_data.filter_by(tag_id=int(tid))
# 星级
star = request.args.get("star", 0)
if int(star) != 0:
page_data = page_data.filter_by(star=int(star))
# 时间
time = request.args.get("time", 0)
if int(time) != 0:
if int(time) == 1:
page_data = page_data.order_by(
Movie.addtime.desc()
)
else:
page_data = page_data.order_by(
Movie.addtime.asc()
)
# 播放量
pm = request.args.get("pm", 0)
if int(pm) != 0:
if int(pm) == 1:
page_data = page_data.order_by(
Movie.playnum.desc()
)
else:
page_data = page_data.order_by(
Movie.playnum.asc()
)
# 评论量
cm = request.args.get("cm", 0)
if int(cm) != 0:
if int(cm) == 1:
page_data = page_data.order_by(
Movie.commentnum.desc()
)
else:
page_data = page_data.order_by(
Movie.commentnum.asc()
)
if page is None:
page = 1
page_data = page_data.paginate(page=page, per_page=8) ## 为筛选后的电影列表
p = dict(
tid=tid,
star=star,
time=time,
pm=pm,
cm=cm,
)
return render_template("home/index.html", tags=tags, p=p, page_data=page_data)
9-2 电影搜索-电影详情
@home.route("/search/<int:page>/")
def search(page=None):
"""
搜索
"""
if page is None:
page = 1
key = request.args.get("key", "") ## search 关键字
movie_count = Movie.query.filter(
Movie.title.ilike('%' + key + '%')
).count()
page_data = Movie.query.filter(
Movie.title.ilike('%' + key + '%')
).order_by(
Movie.addtime.desc()
).paginate(page=page, per_page=10)
page_data.key = key
return render_template("home/search.html", movie_count=movie_count, key=key, page_data=page_data)
5-4 会员-收藏-评论管理页面搭建
@admin.route("/user/list/<int:page>/",methods=["GET", "POST"])
@admin_log_req
def user_list(page=None):
"""
会员列表
"""
if page is None:
page = 1
page_data = User.query.order_by(
User.addtime.desc()
).paginate(page=page, per_page=10)
return render_template("admin/user_list.html", page_data=page_data)
@admin.route("/user/view/<int:id>/",methods=["GET"])
@admin_log_req
def user_view(id=None):
"""
查看会员详情
"""
from_page = request.args.get('fp')
if not from_page:
from_page = 1
user = User.query.get_or_404(int(id))
return render_template("admin/user_view.html", user=user, from_page=from_page)
@admin.route("/user/del/<int:id>/", methods=["GET"])
@admin_log_req
# @admin_auth
def user_del(id=None):
"""
删除会员
"""
# # # 因为删除当前页。假如是最后一页,这一页已经不见了。回不到。
# from_page = int(request.args.get('fp')) - 1
# # # 此处考虑全删完了,没法前挪的情况,0被视为false
# if not from_page:
# from_page = 1
user = User.query.get_or_404(int(id))
db.session.delete(user)
db.session.commit()
flash("删除会员成功!", "ok")
return redirect(url_for('admin.user_list', page=1))
6-8 修改密码
@admin.route("/pwd/", methods=["GET", "POST"])
@admin_log_req
def pwd():
"""
后台密码修改
"""
form = PwdForm()
if form.validate_on_submit():
data = form.data
admin = Admin.query.filter_by(name=session["admin"]).first()
from werkzeug.security import generate_password_hash
admin.pwd = generate_password_hash(data["new_pwd"])
db.session.add(admin)
db.session.commit()
flash("修改密码成功,请重新登录!", "ok")
return redirect(url_for('admin.logout'))
return render_template("admin/pwd.html", form=form)
在form表单中验证密码的错误
def validate_old_pwd(self, field):
from flask import session
pwd = field.data
name = session["admin"]
admin = Admin.query.filter_by(
name=name
).first()
if not admin.check_pwd(pwd):
raise ValidationError("旧密码错误!")
6-9 日志管理
- 管理员操作日志
# 添加 管理员增加标签log
oplog = Oplog(
admin_id=session["admin_id"],
ip=request.remote_addr,
reason="添加标签%s" % data["name"]
)
db.session.add(oplog)
db.session.commit()
# 操作日志
@admin.route("/oplog/list/<int:page>", methods=["GET"])
@admin_log_req
def oplog_list(page=None):
if page is None:
page = 1
page_data = Oplog.query.join(Admin).filter(
Oplog.admin_id == Admin.id,
).order_by(
Oplog.addtime.desc()
).paginate(page=page, per_page=10)
return render_template("admin/oplog_list.html",page_data=page_data)
- 管理员登录日志
在管理员登陆时,插入登录日志
def login():
"""
后台登录
"""
form = LoginForm()
if form.validate_on_submit(): # 表单数据
data = form.data
admin = Admin.query.filter_by(name=data["account"]).first() # 查找admin
if not admin.check_pwd(data['pwd']):
flash("密码错误", "err")
return redirect(url_for("admin.login"))
session['admin'] = data['account']
session["admin_id"] = admin.id #增加管理员id,用于管理员操作日志
# 管理员登陆日志
adminlog = Adminlog(
admin_id=admin.id,
ip=request.remote_addr, #获取ip
)
db.session.add(adminlog)
db.session.commit()
return redirect(request.args.get("next") or url_for("admin.index"))
return render_template("admin/login.html", form=form)
1.1
完善前后端的优化工作,并部署到服务器
遇到的问题
1. 当验证在Flask中失败时,如何覆盖html默认的“请填写此字段”?
你应该让浏览器来处理这个问题,这些消息是标准的,并根据用户计算机的区域设置进行调整。有一个JavaScriptAPI来控制这些消息( ,尽管WTForms没有提供与它的任何集成(但是,扩展是一个好主意)。
-
如果你真的想要禁用这个,你可以通过required=False同时渲染一个字段。
{{ form.name(required=False) }}
-
你可以通过重写整个窗体来禁用它。
class NoRequiredForm(Form): class Meta: def render_field(self, field, render_kw): render_kw.setdefault('required', False) return super().render_field(field, render_kw)
-
你可以通过从禁用该窗体的基窗体继承它来禁用多个窗体。
class UserForm(NoRequiredForm):
…
-
还可以通过设置novalidate属性在HTML上。
<form novalidate> </form>
2. ValueError: View function did not return a response
添加关键字return,视图无返回值
3. nginx 限制ip并发 及速率
https://blog.csdn.net/weixin_43746433/article/details/103897789