【Flask微电影】15.电影内容管理:增删查改

个人博客,欢迎查看:https://blog.starmeow.cn/

Github地址:https://github.com/xyliurui/FlaskMovie

电影管理

电影添加

创建电影添加表单

app/admin/forms.py

class MovieForm(FlaskForm):
    title = StringField(
        label='片名',
        validators=[
            DataRequired('请输入片名!')
        ],
        description='片名',
        render_kw={
            'class': "form-control",
            'placeholder': "请输入标签名称!"
        }
    )
    url = FileField(
        label='电影文件',
        validators=[
            DataRequired('请上传电影文件!')
        ],
        description='电影文件',
    )
    info = TextAreaField(
        label='简介',
        validators=[
            DataRequired('请输入简介!')
        ],
        description='简介',
        render_kw={
            'class': "form-control",
            'rows': "10",
        }
    )
    logo = FileField(
        label='封面',
        validators=[
            DataRequired('请上传封面!')
        ],
        description='封面',
    )
    star = SelectField(
        label='星级',
        validators=[
            DataRequired('请选择星级!')
        ],
        description='星级',
        coerce=int,
        choices=[(1, '1星'), (2, '2星'), (3, '3星'), (4, '4星'), (5, '5星')],
        render_kw={
            'class': "form-control"
        }
    )
    tag_id = SelectField(
        label='标签',
        validators=[
            DataRequired('请选择标签!')
        ],
        coerce=int,
        choices=[(tag.id, tag.name) for tag in Tag.query.all()],
        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",
            'placeholder': "请选择上映时间!",
            'id': "input_release_time"  # 由于使用了时间控件,需要指定id
        }
    )
    submit = SubmitField(
        label='提交',
        render_kw={
            'class': "btn btn-primary"
        }
    )

修改movie_add电影添加视图

urllogo不能通过form.data直接获取,后面再增加

@admin.route("/movie/add/", methods=['GET', 'POST'])
@admin_login_require
def movie_add():
    form = MovieForm()
    if form.validate_on_submit():
        data = form.data
        url = ''  # 待增加
        logo = ''
        movie = Movie(
            title=data['title'],
            url=url,
            info=data['info'],
            logo=logo,
            star=data['star'],
            play_num=0,
            comment_num=0,
            tag_id=data['tag_id'],
            area=data['area'],
            release_time=data['release_time'],
            length=data['length']
        )
        db.session.add(movie)
        db.session.commit()
        flash('添加电影成功', 'ok')
        return redirect(url_for('admin.movie_add'))
    return render_template('admin/movie_add.html', form=form)

修改movie_add.html增加表单显示

增加每个字段的验证错误信息显示,以及提交表单后flash提示信息

{% with msgs = get_flashed_messages(category_filter=['ok']) %} {% if msgs %}

成功!

{% for msg in msgs %}

{{ msg }}

{% endfor %}
{% endif %} {% endwith %} {% with msgs = get_flashed_messages(category_filter=['err']) %} {% if msgs %}

失败!

{% for msg in msgs %}

{{ msg }}

{% endfor %}
{% endif %} {% endwith %}
{{ 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 %}
{{ form.csrf_token }}
image.png

进行表单文件的上传保存操作

上传表单、文件上传: http://www.pythondoc.com/flask-wtf/form.html

urllogo如何获取?

定义文件上传保存的路径。

修改app/__init__.py增加文件保存路径

# 定义文件上传保存的路径,在__init__.py文件所在目录创建media文件夹,用于保存上传的文件
app.config['UP_DIR'] = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'static/media/')

上传文件重命名,以时间字符串+随机字符串+文件后缀的名称进行重命名

import os
import uuid  # 生成唯一字符串
import datetime  # 生成时间


# 修改文件名称
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_require
def movie_add():
    form = MovieForm()
    if form.validate_on_submit():
        data = form.data

        # 提交的片名在数据库中已存在
        if Movie.query.filter_by(title=data['title']).count() == 1:
            flash('电影片名已存在,请检查', category='err')
            return redirect(url_for('admin.movie_add'))

        # 获取上传文件的名称
        file_url = secure_filename(form.url.data.filename)
        file_logo = secure_filename(form.logo.data.filename)
        # 文件保存路径操作
        file_save_path = app.config['UP_DIR']  # 文件上传保存路径
        if not os.path.exists(file_save_path):
            os.makedirs(file_save_path)  # 如果文件保存路径不存在,则创建一个多级目录
            import stat
            os.chmod(file_save_path, stat.S_IRWXU)  # 授予可读写权限
        # 对上传的文件进行重命名
        url = change_filename(file_url)
        logo = change_filename(file_logo)
        # 保存文件,需要给文件的保存路径+文件名
        form.url.data.save(file_save_path + url)
        form.logo.data.save(file_save_path + logo)

        movie = Movie(
            title=data['title'],
            url=url,
            info=data['info'],
            logo=logo,
            star=data['star'],
            play_num=0,
            comment_num=0,
            tag_id=data['tag_id'],
            area=data['area'],
            release_time=data['release_time'],
            length=data['length']
        )
        db.session.add(movie)
        db.session.commit()
        flash('添加电影成功', 'ok')
        return redirect(url_for('admin.movie_add'))
    return render_template('admin/movie_add.html', form=form)
image.png

将会在static/media文件夹下保存电信的视频和封面内容

image.png
image.png

电影列表

修改movie_list视图增加查询和分页

@admin.route("/movie/list//", methods=['GET'])
@admin_login_require
def movie_list(page=None):
    if page is None:
        page = 1
    # 查询的时候关联标签Tag进行查询:使用join(Tag)
    # 单表过滤使用filter_by,多表关联使用filter,将Tag.id与Movie的tag_id进行关联
    page_movies = Movie.query.join(Tag).filter(
        Tag.id == Movie.tag_id
    ).order_by(
        Movie.add_time.desc()
    ).paginate(page=page, per_page=10)
    return render_template('admin/movie_list.html', page_movies=page_movies)

修改movie_list.html显示电影列表和分页

修改base.html中的电影列表增加page参数,如果不增加,后面使用会报错


     电影列表

直接使用之前创建的分页模块,获取电影的标签名称,通过movie.tag.name电影外键关系关联获取


    {% for movie in page_movies.items %}
        
    {% endfor %}
    
编号 片名 片长 标签 地区 星级 播放数量 评论数量 上映时间 操作事项
{{ movie.id }} {{ movie.title }} {{ movie.length }} 分钟 {{ movie.tag.name }} {{ movie.area }} {{ movie.star }} 星 {{ movie.comment_num }} {{ movie.play_num }} {{ movie.release_time }} 编辑   删除
{% import 'admin/pagination.html' as pg %} {{ pg.render_pagination(page_movies, 'admin.movie_list') }}
image.png

电影删除

增加movie_delete删除电影视图

从数据库中查询到该电影,然后进行删除,同事需要从磁盘删除电影文件和封面文件

@admin.route("/movie/delete//", methods=['GET'])
@admin_login_require
def movie_delete(delete_id=None):
    if delete_id:
        movie = Movie.query.filter_by(id=delete_id).first_or_404()
        print(movie.logo)
        # 删除电影同时要从磁盘中删除电影的文件和封面文件
        file_save_path = app.config['UP_DIR']  # 文件上传保存路径
        # 如果存在将进行删除,不判断,如果文件不存在删除会报错
        if os.path.exists(os.path.join(file_save_path, movie.url)):
            os.remove(os.path.join(file_save_path, movie.url))
        if os.path.exists(os.path.join(file_save_path, movie.logo)):
            os.remove(os.path.join(file_save_path, movie.logo))

        # 删除数据库,提交修改,注意后面要把与电影有关的评论都要删除
        db.session.delete(movie)
        db.session.commit()
        # 删除后闪现消息
        flash('删除电影成功!', category='ok')
    return redirect(url_for('admin.movie_list', page=1))

修改movie_list.html删除电影链接和提示

{% with msgs = get_flashed_messages(category_filter=['ok']) %}
    {% if msgs %}
        

成功!

{% for msg in msgs %}

{{ msg }}

{% endfor %}
{% endif %} {% endwith %} 删除
image.png

编辑电影

创建movie_update电影编辑试图

  • 初始化表单,并增加除了文件外的初始值
  • 因为在MovieForm中要求urllogo不能为空,所以在修改视图中,需要允许上传文件为空,直接定义requiredFalse没这样在前端表单中不会要求上传文件
  • 提交表单,检查片名是否存在,如果不存在才添加数据库
  • 保存提交的修改
  • 如果电影文件和封面文件存在,先删除旧文件,然后保存新文件到数据库
@admin.route("/movie/update//", methods=['GET', 'POST'])
@admin_login_require
def movie_update(update_id=None):
    movie = Movie.query.get_or_404(int(update_id))
    # print(movie)

    # 给表单赋初始值,文件表单不处理
    form = MovieForm(
        title=movie.title,
        # url=movie.url,  # 上传文件,这样赋初始值无效,在前端可以通过上传路径+movie.url来获取文件的保存路径,显示在页面上
        info=movie.info,
        # logo=movie.logo,  # 上传图片和文件类似
        star=movie.star,
        tag_id=movie.tag_id,
        area=movie.area,
        release_time=movie.release_time,
        length=movie.length,
    )
    # 对于修改数据,电影文件和封面图已存在,可以非必填:按照教程上测试了validators参数,但始终不行,最终修改required的值就可以了
    form.url.validators = []
    print(form.url)  # 
    if form.url.render_kw:
        form.url.render_kw['required'] = False
    else:
        form.url.render_kw = {'required': False}
    print(form.url)  # 

    form.logo.validators = []  # 验证列表为空
    form.logo.render_kw = {'required': False}  # 直接修改required为False表明不要求输入

    if form.validate_on_submit():
        data = form.data
        # 提交的片名在数据库中已存在,且不是当前的电影名称
        if Movie.query.filter_by(title=data['title']).count() == 1 and movie.title != data['title']:
            flash('电影片名已存在,请检查', category='err')
            return redirect(url_for('admin.movie_update', update_id=update_id))
        # 以下和直接修改的数据
        movie.title = data['title']
        movie.info = data['info']
        movie.star = data['star']
        movie.tag_id = data['tag_id']
        movie.area = data['area']
        movie.release_time = data['release_time']
        movie.length = data['length']

        # 文件保存路径操作
        file_save_path = app.config['UP_DIR']  # 文件上传保存路径
        if not os.path.exists(file_save_path):
            os.makedirs(file_save_path)  # 如果文件保存路径不存在,则创建一个多级目录
            import stat
            os.chmod(file_save_path, stat.S_IRWXU)  # 授予可读写权限

        print(form.url.data, type(form.url.data))
        #  
        # 处理电影文件逻辑:先从磁盘中删除旧文件,然后保存新文件
        if form.url.data:  # 上传文件不为空,才进行保存
            # 删除以前的文件
            if os.path.exists(os.path.join(file_save_path, movie.url)):
                os.remove(os.path.join(file_save_path, movie.url))
            # 获取上传文件的名称
            file_url = secure_filename(form.url.data.filename)
            # 对上传的文件进行重命名
            movie.url = change_filename(file_url)
            # 保存文件,需要给文件的保存路径+文件名
            form.url.data.save(file_save_path + movie.url)

        # 处理封面图
        if form.logo.data:
            if os.path.exists(os.path.join(file_save_path, movie.logo)):
                os.remove(os.path.join(file_save_path, movie.logo))
            file_logo = secure_filename(form.logo.data.filename)
            movie.logo = change_filename(file_logo)
            form.logo.data.save(file_save_path + movie.logo)
        db.session.merge(movie)  # 调用merge方法,此时Movie实体状态并没有被持久化,但是数据库中的记录被更新了(暂时不明白)
        db.session.commit()
        flash('修改电影成功', 'ok')
        return redirect(url_for('admin.movie_update', update_id=update_id))
    return render_template('admin/movie_update.html', form=form, movie=movie)

创建movie_update.html电影编辑模板

由于在视图中已经初始化好表单的值,显示在输入框中,在页面上显示通过file: "{{ url_for('static',filename="media/"+ movie.url) }}",渲染视频的播放,以及使用来显示封面图

form role="form" method="post" enctype="multipart/form-data">
    
{% with msgs = get_flashed_messages(category_filter=['ok']) %} {% if msgs %}

成功!

{% for msg in msgs %}

{{ msg }}

{% endfor %}
{% endif %} {% endwith %} {% with msgs = get_flashed_messages(category_filter=['err']) %} {% if msgs %}

失败!

{% for msg in msgs %}

{{ msg }}

{% endfor %}
{% endif %} {% endwith %}
{{ 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 %}
{{ form.csrf_token }}

修改movie_list.html增加编辑按钮的链接

编辑

当输入一个已存在的片名就会提示已存在。

image.png
image.png

如果有上传电影文件或者是封面图片,那么将会删除旧文件,并保存新的文件到数据库,可以查看/static/media/下的文件变动。

你可能感兴趣的:(【Flask微电影】15.电影内容管理:增删查改)