后台管理
实现后台管理系统使用flask sqlalchemy结合mysql数据库进行增删改查操作、分页的使用、路由装饰器定义、模板中变量调用、登录会话机制、上传文件、flask wtforms表单使用。
管理员登录
对models
进行重构,将数据库的配置信息放在app/__init__.py
文件中
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+mysqlconnector://root:[email protected]:3306/movie'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
app.config['SECRET_KEY'] = 'cb34xxxxxxxxxxxxxxxxxxbae30d90f6'
db = SQLAlchemy(app)
在models
文件中直接引入db
from app import db
定义登陆表单字段
app/admin/forms.py
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, ValidationError
from app.models import Admin
class LoginForm(FlaskForm):
'''
管理员登陆表单
'''
account = StringField(
label='账号',
validators=[
DataRequired("请输入账号!")
],
description="账号",
render_kw={
"class": "form-control",
"placeholder": "请输入账号!",
"required": "required"
}
)
pwd = PasswordField(
label='密码',
validators=[
DataRequired("请输入密码!")
],
description="密码",
render_kw={
"class": "form-control",
"placeholder": "请输入密码!",
"required": "required"
}
)
submit = SubmitField(
'登录',
render_kw={
"class": "btn btn-primary btn-block btn-flat",
}
)
def validate_account(self, field):
account = field.data
admin = Admin.query.filter_by(name=account).count()
if admin == 0:
raise ValidationError("账号不存在!")
render_kw
里的样式是前端代码中的
编写试图函数
# 装饰器用来进行访问控制
def admin_login_req(func):
@wraps(func)
def decorated_function(*args, **kwargs):
if session.get('admin', None) is None:
return redirect(url_for('admin.login', next=request.url))
return func(*args, **kwargs)
return decorated_function
@admin.route('/login/', methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
data = form.data
admin = Admin.query.filter_by(name=data['account']).first()
if not admin.check_pwd(data['pwd']):
flash("账号或密码错误! ")
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)
@admin.route('/logout/')
@admin_login_req
def logout():
session.clear()
return redirect(url_for('admin.login'))
在每一个需要进行登陆才能操作的视图函数中加入装饰器,像logout
视图一样
在Admin
模型中添加密码校验函数
def check_pwd(self, pwd):
from werkzeug.security import check_password_hash
return check_password_hash(self.pwd, pwd)
修改模板app/templates/admin/login.html
{% for message in get_flashed_messages() %}
{{ message }}
{% endfor %}
标签管理
创建一个表单form
app/admin/forms.py
class TagForm(FlaskForm):
name = StringField(
label='名称',
validators=[
DataRequired("请输入标签!")
],
description="名称",
render_kw={
"class": "form-control",
"id": "input_name",
"placeholder": "请输入标签名称!"
}
)
submit = SubmitField(
'编辑',
render_kw={
"class": "btn btn-primary"
}
)
添加标签
视图函数
@admin.route('/tag/add/', methods=['GET', 'POST'])
@admin_login_req
def tag_add():
form = TagForm()
if form.validate_on_submit():
data = form.data
tag = Tag.query.filter_by(name=data['name']).count()
if tag == 1:
flash("标签已存在!", 'error')
return redirect(url_for('admin.tag_add'))
tag = Tag(
name=data['name']
)
db.session.add(tag)
db.session.commit()
flash("标签添加成功!", 'info')
return redirect(url_for('admin.tag_add'))
return render_template('admin/tag_add.html', form=form)
修改前端代码
标签列表
视图函数
@admin.route('/tag/list//')
@admin_login_req
def tag_list(page=1):
if page <= 0:
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)
修改前端代码
当标签较多时,需要对标签进行分页
分页http://www.pythondoc.com/flask-sqlalchemy/api.html?highlight=paginate#id4
新建一个分页的macro
app/templates/ui/admin_page.html
{% macro pagination(data, url) -%}
{% if data %}
{% endif %}
{%- endmacro %}
在标签列表中使用这个macro
{% extends 'admin/admin.html' %}
{% from 'ui/admin_page.html' import pagination %}
...
删除标签
视图函数
@admin.route('/tag/del//')
@admin_login_req
def tag_del(id=None):
tag = Tag.query.filter_by(id=id).first_or_404()
db.session.delete(tag)
db.session.commit()
flash('删除标签成功!', 'info')
return redirect(url_for('admin.tag_list', page=1))
修改标签列表中删除按钮的a标签
删除
修改标签
视图函数
@admin.route('/tag/edit//', methods=['GET', 'POST'])
@admin_login_req
def tag_edit(id=None):
form = TagForm()
tag = Tag.query.get_or_404(id)
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("标签已存在!", 'error')
return redirect(url_for('admin.tag_edit', id=id))
tag.name=data['name']
db.session.add(tag)
db.session.commit()
flash("标签修改成功!", 'info')
return redirect(url_for('admin.tag_list', page=1))
return render_template('admin/tag_edit.html', form=form, tag=tag)
新建app/templates/admin/tag_edit.html
,用来进行标签的修改
代码和添加标签中的几乎一致,只是需要显示标签的名字
{{ form.name(value=tag.name) }}
{% for err in form.name.errors %}
{{ err }}
{% endfor %}
电影管理
新建电影表单
app/admin/forms.py
class MovieForm(FlaskForm):
title = StringField(
label='片名',
validators=[
DataRequired("请输入片名!")
],
description="片名",
render_kw={
"class": "form-control",
"id": "input_title",
"placeholder": "请输入片名!"
}
)
url = FileField(
label='文件',
validators=[
DataRequired("请上传文件!")
],
description="文件",
)
info = TextAreaField(
label='简介',
validators=[
DataRequired("请输入简介!")
],
description="简介",
render_kw={
"class": "form-control",
"rows": "10",
"id": "input_info",
}
)
logo = FileField(
label='封面',
validators=[
DataRequired("请上传封面!")
],
description="封面",
)
star = SelectField(
label='星级',
validators=[
DataRequired("请选择星级!")
],
coerce=int,
choices=[(1, '1星'), (2, '2星'), (3, '3星'), (4, '4星'), (5, '5星')],
description="星级",
render_kw={
"class": "form-control",
}
)
tag_id = SelectField(
label='标签',
validators=[
DataRequired("请选择标签!")
],
coerce=int,
choices=[(v.id, v.name) for v in tags],
description="标签",
render_kw={
"class": "form-control",
}
)
area = StringField(
label='地区',
validators=[
DataRequired("请输入地区!")
],
description="地区",
render_kw={
"class": "form-control",
"placeholder": "请输入地区!"
}
)
length = StringField(
label='片长',
validators=[
DataRequired("请输入片长!")
],
description="片长",
render_kw={
"class": "form-control",
"placeholder": "请输入片长!"
}
)
release_time = StringField(
label='上映时间',
validators=[
DataRequired("请选择上映时间!")
],
description="上映时间",
render_kw={
"class": "form-control",
"id": "input_release_time",
"placeholder": "请选择上映时间!"
}
)
submit = SubmitField(
'编辑',
render_kw={
"class": "btn btn-primary"
}
)
def validate_title(self, field):
title = field.data
num = Movie.query.filter_by(title=title).count()
if num > 0:
raise ValidationError("该电影已存在!")
在app初始化文件中定义上传文件的目录
app.config['UP_DIR'] = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'static/uploads/')
添加电影
视图函数
文件名称检测
from werkzeug.utils import secure_filename
def change_filename(filename):
fileinfo = os.path.splitext(filename)
filename = datetime.datetime.now().strftime('%Y%m%d%H%M%S') + str(uuid.uuid4().hex) + fileinfo[-1]
return filename
@admin.route('/movie/add/', methods=['GET', 'POST'])
@admin_login_req
def movie_add():
form = MovieForm()
if form.validate_on_submit():
data = form.data
file_url = secure_filename(form.url.data.filename)
file_logo = secure_filename(form.logo.data.filename)
if not os.path.exists(app.config['UP_DIR']):
os.makedirs(app.config['UP_DIR'])
os.chmod(app.config['UP_DIR'], 6)
url = change_filename(file_url)
logo = change_filename(file_logo)
form.url.data.save(app.config['UP_DIR'] + url)
form.logo.data.save(app.config['UP_DIR'] + logo)
movie = Movie(
title=data['title'],
url=url,
info=data['info'],
logo=logo,
star=int(data['star']),
playnum=0,
commentnum=0,
tag_id=int(data['tag_id']),
area=data['area'],
release_time=data['release_time'],
length=data['length']
)
db.session.add(movie)
db.session.commit()
flash('电影添加成功!', 'info')
return redirect(url_for('admin.movie_add'))
return render_template('admin/movie_add.html', form=form)
app/templates/admin/movie_add.html
{% extends 'admin/admin.html' %}
{% block content %}
微电影管理系统
添加电影
{% endblock %}
电影列表
视图函数
@admin.route('/movie/list//')
@admin_login_req
def movie_list(page=1):
if page <= 0:
page = 1
page_data = Movie.query.join(Tag).filter(
Tag.id == Movie.tag_id
).order_by(
Movie.addtime.desc()
).paginate(page=page, per_page=10)
return render_template('admin/movie_list.html', page_data=page_data)
app/templates/admin/movie_list.html
{% extends 'admin/admin.html' %}
{% from 'ui/admin_page.html' import pagination %}
{% block content %}
微电影管理系统
电影列表
{% for message in get_flashed_messages(category_filter=['info']) %}
操作成功!
{{ message }}
{% endfor %}
编号
片名
片长
标签
地区
星级
播放数量
评论数量
上映时间
操作事项
{% for data in page_data.items %}
{{ data.id }}
{{ data.title }}
{{ data.length }}分钟
{{ data.tag.name }}
{{ data.area }}
{{ data.star }}
{{ data.playnum }}
{{ data.commentnum }}
{{ data.release_time }}
编辑
删除
{% endfor %}
{% endblock %}
{% block js %}
{% endblock %}
修改app/templates/admin/grid.html
电影列表
删除电影
视图函数
@admin.route('/movie/del//')
@admin_login_req
def movie_del(id=None):
movie = Movie.query.get_or_404(int(id))
db.session.delete(movie)
db.session.commit()
flash('电影删除成功!', 'info')
return redirect(url_for('admin.movie_list', page=1))
修改一下前端删除按钮a标签
修改电影
视图函数
@admin.route('/movie/edit/', methods=['GET', 'POST'])
@admin_login_req
def movie_edit(id=None):
form = MovieForm()
# # 如果不设置,默认依然会让上传文件
# form.url.flags.required = False
# form.logo.flags.required = False
# # 取消校验,可能没有上传文件
# form.url.validators=[]
# form.logo.validators=[]
# 取消后如果没有上传文件,form.url.data是一个str对象
# 上传文件后是才是一个文件对象
# 为了方便,设置必须上传文件
movie=Movie.query.get_or_404(int(id))
if request.method =='GET':
form.info.data = movie.info
form.tag_id.data = movie.tag_id
form.star.data = movie.star
if form.validate_on_submit():
data = form.data
movie_count = Movie.query.filter_by(title=data['title']).count()
if movie_count == 1 and movie.title != data['title']:
flash('该电影已存在!', 'error')
return redirect(url_for('admin.movie_edit', id=id))
if not os.path.exists(app.config['UP_DIR']):
os.makedirs(app.config['UP_DIR'])
os.chmod(app.config['UP_DIR'], 6)
if form.url.data.filename !='':
file_url = secure_filename(form.url.data.filename)
movie.url = change_filename(file_url)
form.url.data.save(app.config['UP_DIR'] + movie.url)
if form.logo.data.filename != '':
file_logo = secure_filename(form.logo.data.filename)
movie.logo = change_filename(file_logo)
form.logo.data.save(app.config['UP_DIR'] + movie.logo)
movie.star = data['star']
movie.tag_id = data['tag_id']
movie.info = data['info']
movie.title = data['title']
movie.area = data['area']
movie.length = data['length']
movie.release_time = data['release_time']
db.session.add(movie)
db.session.commit()
flash('电影修改成功!', 'info')
return redirect(url_for('admin.movie_add', id=movie.id))
return render_template('admin/movie_edit.html', form=form, movie=movie)
app/templates/admin/movie_edit.html
{% extends 'admin/admin.html' %}
{% block content %}
微电影管理系统
修改电影
{% endblock %}
{% block js %}
{% endblock %}
修改电影列表中的编辑按钮
编辑
预告管理
创建预告表单
class PreviewForm(FlaskForm):
title = StringField(
label='预告标题',
validators=[
DataRequired("请输入预告标题!")
],
description="预告标题",
render_kw={
"class": "form-control",
"placeholder": "请输入预告标题!"
}
)
logo = FileField(
label='预告封面',
validators=[
DataRequired("请上传预告封面!"),
],
description="预告封面",
)
submit = SubmitField(
'编辑',
render_kw={
"class": "btn btn-primary"
}
)
def validate_title(self, field):
title = field.data
num = Preview.query.filter_by(title=title).count()
if num > 0:
raise ValidationError("该预告已存在!")