Flask 构建微电影视频网站(4.1)

后台管理

实现后台管理系统使用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

    

标签管理

创建一个表单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)

修改前端代码

{% for message in get_flashed_messages(category_filter=['info']) %}

操作成功!

{{ message }}
{% endfor %} {% for message in get_flashed_messages(category_filter=['error']) %}

操作失败!

{{ message }}
{% endfor %}
{{ form.name }} {% for err in form.name.errors %}
{{ err }}
{% endfor %}
标签列表

视图函数

@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)

修改前端代码

{% for message in get_flashed_messages(category_filter=['info']) %}

操作成功!

{{ message }}
{% endfor %} {% for tag in page_data.items %} {% endfor %}
编号 名称 添加时间 操作事项
{{ tag.id }} {{ tag.name }} {{ tag.addtime }} 编辑   删除

当标签较多时,需要对标签进行分页
分页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 %}
    

微电影管理系统

添加电影

{% for message in get_flashed_messages(category_filter=['info']) %}

操作成功!

{{ message }}
{% endfor %}
{{ form.title }} {% for err in form.title.errors %}
{{ err }}
{% endfor %}
{{ form.url }} {% for err in form.url.errors %}
{{ err }}
{% endfor %}
{#
#}
{{ form.info }} {% for err in form.info.errors %}
{{ err }}
{% endfor %}
{{ form.logo }} {% for err in form.logo.errors %}
{{ err }}
{% endfor %} {# #}
{{ form.star }} {% for err in form.star.errors %}
{{ err }}
{% endfor %}
{{ form.tag_id }} {% for err in form.tag_id.errors %}
{{ err }}
{% endfor %}
{{ form.area }} {% for err in form.area.errors %}
{{ err }}
{% endfor %}
{{ form.length }} {% for err in form.length.errors %}
{{ err }}
{% endfor %}
{{ form.release_time }} {% for err in form.release_time.errors %}
{{ err }}
{% endfor %}
{% 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 %} {% endfor %}
编号 片名 片长 标签 地区 星级 播放数量 评论数量 上映时间 操作事项
{{ data.id }} {{ data.title }} {{ data.length }}分钟 {{ data.tag.name }} {{ data.area }} {{ data.star }} {{ data.playnum }} {{ data.commentnum }} {{ data.release_time }} 编辑   删除
{% 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 %}
        

    微电影管理系统

    修改电影

    {% for message in get_flashed_messages(category_filter=['info']) %}

    操作成功!

    {{ message }}
    {% endfor %} {% for message in get_flashed_messages(category_filter=['error']) %}

    操作失败!

    {{ message }}
    {% endfor %}
    {{ form.title(value=movie.title) }} {% for err in form.title.errors %}
    {{ err }}
    {% endfor %}
    {{ form.url }} {% for err in form.url.errors %}
    {{ err }}
    {% endfor %}
    {{ form.info }} {% for err in form.info.errors %}
    {{ err }}
    {% endfor %}
    {{ form.logo }} {% for err in form.logo.errors %}
    {{ err }}
    {% endfor %}
    {{ form.star }} {% for err in form.star.errors %}
    {{ err }}
    {% endfor %}
    {{ form.tag_id }} {% for err in form.tag_id.errors %}
    {{ err }}
    {% endfor %}
    {{ form.area(value=movie.area) }} {% for err in form.area.errors %}
    {{ err }}
    {% endfor %}
    {{ form.length(value=movie.length) }} {% for err in form.length.errors %}
    {{ err }}
    {% endfor %}
    {{ form.release_time(value=movie.release_time) }} {% for err in form.release_time.errors %}
    {{ err }}
    {% endfor %}
    {% 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("该预告已存在!")
    

    你可能感兴趣的:(Flask 构建微电影视频网站(4.1))