Flask微电影网站开发

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

2018-11-11  本文已影响32人  吾星喵

个人博客,欢迎查看: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提示信息

<form role="form" method="post" enctype="multipart/form-data">
    <div class="box-body">

        {% with msgs = get_flashed_messages(category_filter=['ok']) %}
            {% if msgs %}
                <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>
                    {% for msg in msgs %}
                        <p>{{ msg }}</p>
                    {% endfor %}
                </div>
            {% endif %}
        {% endwith %}

        {% with msgs = get_flashed_messages(category_filter=['err']) %}
            {% if msgs %}
                <div class="alert alert-danger alert-dismissible">
                    <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
                    <h4><i class="icon fa fa-ban"></i> 失败!</h4>
                    {% for msg in msgs %}
                        <p>{{ msg }}</p>
                    {% endfor %}
                </div>
            {% endif %}
        {% endwith %}

        <div class="form-group">
            <label for="input_title">{{ form.title.label }}</label>
            {{ form.title }}
            {% for err in form.title.errors %}
                <div class="col-md-12" style="color: red">{{ err }}</div>
            {% endfor %}
        </div>
        <div class="form-group">
            <label for="input_url">{{ form.url.label }}</label>
            {{ form.url }}
            {% for err in form.url.errors %}
                <div class="col-md-12" style="color: red">{{ err }}</div>
            {% endfor %}
            <div style="margin-top:5px;">
                <div id="moviecontainer"></div>
            </div>
        </div>
        <div class="form-group">
            <label for="input_info">{{ form.info.label }}</label>
            {{ form.info }}
            {% for err in form.info.errors %}
                <div class="col-md-12" style="color: red">{{ err }}</div>
            {% endfor %}
        </div>
        <div class="form-group">
            <label for="input_logo">{{ form.logo.label }}</label>
            {{ form.logo }}
            {% for err in form.logo.errors %}
                <div class="col-md-12" style="color: red">{{ err }}</div>
            {% endfor %}
            <img data-src="holder.js/262x166" style="margin-top:5px;" class="img-responsive"
                 alt="">
        </div>
        <div class="form-group">
            <label for="input_star">{{ form.star.label }}</label>
            {{ form.star }}
            {% for err in form.star.errors %}
                <div class="col-md-12" style="color: red">{{ err }}</div>
            {% endfor %}
        </div>
        <div class="form-group">
            <label for="input_tag_id">{{ form.tag_id.label }}</label>
            {{ form.tag_id }}
            {% for err in form.tag_id.errors %}
                <div class="col-md-12" style="color: red">{{ err }}</div>
            {% endfor %}
        </div>
        <div class="form-group">
            <label for="input_area">{{ form.area.label }}</label>
            {{ form.area }}
            {% for err in form.area.errors %}
                <div class="col-md-12" style="color: red">{{ err }}</div>
            {% endfor %}
        </div>
        <div class="form-group">
            <label for="input_length">{{ form.length.label }}</label>
            {{ form.length }}
            {% for err in form.length.errors %}
                <div class="col-md-12" style="color: red">{{ err }}</div>
            {% endfor %}
        </div>
        <div class="form-group">
            <label for="input_release_time">{{ form.release_time.label }}</label>
            {{ form.release_time }}
            {% for err in form.release_time.errors %}
                <div class="col-md-12" style="color: red">{{ err }}</div>
            {% endfor %}
        </div>
    </div>
    {{ form.csrf_token }}
    <div class="box-footer">
        {{ form.submit }}
    </div>
</form>
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/<int:page>/", 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参数,如果不增加,后面使用会报错

<a href="{{ url_for('admin.movie_list', page=1) }}">
    <i class="fa fa-circle-o"></i> 电影列表
</a>

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

<table class="table table-hover">
    <tbody>
    <tr>
        <th>编号</th>
        <th>片名</th>
        <th>片长</th>
        <th>标签</th>
        <th>地区</th>
        <th>星级</th>
        <th>播放数量</th>
        <th>评论数量</th>
        <th>上映时间</th>
        <th>操作事项</th>
    </tr>
    {% for movie in page_movies.items %}
        <tr>
            <td>{{ movie.id }}</td>
            <td>{{ movie.title }}</td>
            <td>{{ movie.length }} 分钟</td>
            <td>{{ movie.tag.name }}</td>
            <td>{{ movie.area }}</td>
            <td>{{ movie.star }} 星</td>
            <td>{{ movie.comment_num }}</td>
            <td>{{ movie.play_num }}</td>
            <td>{{ movie.release_time }}</td>
            <td>
                <a class="label label-success">编辑</a>
                &nbsp;
                <a class="label label-danger">删除</a>
            </td>
        </tr>
    {% endfor %}
    </tbody>
</table>


<!--增加分页模块到页面底部,参考标签的分页-->

<!--页码模块-->
{% import 'admin/pagination.html' as pg %}
{{ pg.render_pagination(page_movies, 'admin.movie_list') }}
image.png

电影删除

增加movie_delete删除电影视图

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

@admin.route("/movie/delete/<int:delete_id>/", 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 %}
        <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>
            {% for msg in msgs %}
                <p>{{ msg }}</p>
            {% endfor %}
        </div>
    {% endif %}
{% endwith %}


<a class="label label-danger" href="{{ url_for('admin.movie_delete', delete_id=movie.id) }}">删除</a>
image.png

编辑电影

创建movie_update电影编辑试图

@admin.route("/movie/update/<int:update_id>/", 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)  # <input id="url" name="url" required type="file">
    if form.url.render_kw:
        form.url.render_kw['required'] = False
    else:
        form.url.render_kw = {'required': False}
    print(form.url)  # <input id="url" name="url" type="file">

    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))
        # <FileStorage: 'ssh.jpg' ('image/jpeg')> <class 'werkzeug.datastructures.FileStorage'>
        # 处理电影文件逻辑:先从磁盘中删除旧文件,然后保存新文件
        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) }}",渲染视频的播放,以及使用<img src="{{ url_for('static', filename='media/' + movie.logo) }}" style="margin-top:5px;" class="img-responsive" alt="">来显示封面图

form role="form" method="post" enctype="multipart/form-data">
    <div class="box-body">

        {% with msgs = get_flashed_messages(category_filter=['ok']) %}
            {% if msgs %}
                <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>
                    {% for msg in msgs %}
                        <p>{{ msg }}</p>
                    {% endfor %}
                </div>
            {% endif %}
        {% endwith %}

        {% with msgs = get_flashed_messages(category_filter=['err']) %}
            {% if msgs %}
                <div class="alert alert-danger alert-dismissible">
                    <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
                    <h4><i class="icon fa fa-ban"></i> 失败!</h4>
                    {% for msg in msgs %}
                        <p>{{ msg }}</p>
                    {% endfor %}
                </div>
            {% endif %}
        {% endwith %}

        <div class="form-group">
            <label for="input_title">{{ form.title.label }}</label>
            {{ form.title }}
            {% for err in form.title.errors %}
                <div class="col-md-12" style="color: red">{{ err }}</div>
            {% endfor %}
        </div>
        <div class="form-group">
            <label for="input_url">{{ form.url.label }}</label>
            {{ form.url }}
            {% for err in form.url.errors %}
                <div class="col-md-12" style="color: red">{{ err }}</div>
            {% endfor %}
            <div style="margin-top:5px;">
                <div id="moviecontainer"></div>
            </div>
        </div>
        <div class="form-group">
            <label for="input_info">{{ form.info.label }}</label>
            {{ form.info }}
            {% for err in form.info.errors %}
                <div class="col-md-12" style="color: red">{{ err }}</div>
            {% endfor %}
        </div>
        <div class="form-group">
            <label for="input_logo">{{ form.logo.label }}</label>
            {{ form.logo }}
            {% for err in form.logo.errors %}
                <div class="col-md-12" style="color: red">{{ err }}</div>
            {% endfor %}
            <img src="{{ url_for('static', filename='media/' + movie.logo) }}" style="margin-top:5px;" class="img-responsive" alt="">
        </div>
        <div class="form-group">
            <label for="input_star">{{ form.star.label }}</label>
            {{ form.star }}
            {% for err in form.star.errors %}
                <div class="col-md-12" style="color: red">{{ err }}</div>
            {% endfor %}
        </div>
        <div class="form-group">
            <label for="input_tag_id">{{ form.tag_id.label }}</label>
            {{ form.tag_id }}
            {% for err in form.tag_id.errors %}
                <div class="col-md-12" style="color: red">{{ err }}</div>
            {% endfor %}
        </div>
        <div class="form-group">
            <label for="input_area">{{ form.area.label }}</label>
            {{ form.area }}
            {% for err in form.area.errors %}
                <div class="col-md-12" style="color: red">{{ err }}</div>
            {% endfor %}
        </div>
        <div class="form-group">
            <label for="input_length">{{ form.length.label }}</label>
            {{ form.length }}
            {% for err in form.length.errors %}
                <div class="col-md-12" style="color: red">{{ err }}</div>
            {% endfor %}
        </div>
        <div class="form-group">
            <label for="input_release_time">{{ form.release_time.label }}</label>
            {{ form.release_time }}
            {% for err in form.release_time.errors %}
                <div class="col-md-12" style="color: red">{{ err }}</div>
            {% endfor %}
        </div>
    </div>
    {{ form.csrf_token }}
    <div class="box-footer">
        {{ form.submit }}
    </div>
</form>




<!--播放页面-->
<script src="{{ url_for('static',filename='jwplayer/jwplayer.js') }}"></script>
<script type="text/javascript">
    jwplayer.key = "P9VTqT/X6TSP4gi/hy1wy23BivBhjdzVjMeOaQ==";

</script>
<script type="text/javascript">
    jwplayer("moviecontainer").setup({
        flashplayer: "{{ url_for('static',filename='jwplayer/jwplayer.flash.swf') }}",
        playlist: [{
            file: "{{ url_for('static',filename="media/"+ movie.url) }}",
            title: "{{ movie.title }}"
        }],
        modes: [{
            type: "html5"
        }, {
            type: "flash",
            src: "{{ url_for('static',filename='jwplayer/jwplayer.flash.swf') }}"
        }, {
            type: "download"
        }],
        skin: {
            name: "vapor"
        },
        "playlist.position": "left",
        "playlist.size": 200,
        height: 250,
        width: 387,
    });

</script>
<script>
    $(document).ready(function () {
        $('#input_release_time').datepicker({
            autoclose: true,
            format: 'yyyy-mm-dd',
            language: 'zh-CN',
        });
    });

</script>

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

<a class="label label-success" href="{{ url_for('admin.movie_update', update_id=movie.id) }}">编辑</a>

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

image.png image.png

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

上一篇下一篇

猜你喜欢

热点阅读