您现在的位置是: 网站首页 >Flask >Flask搭建微电影视频网站 Flask

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

admin2018年11月11日 20:08 Flask | Html | Python 551人已围观

Flask搭建微电影视频网站简介 利用Flask搭建微电影视频网站 Github地址:https://github.com/xyliurui/FlaskMovie

## 电影管理 ### 电影添加 #### 创建电影添加表单 **app/admin/forms.py** ```python 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电影添加视图 `url`和`logo`不能通过`form.data`直接获取,后面再增加 ```python @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`提示信息 ```html <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> ``` ![BLOG_20181111_200946_30](/media/blog/images/2018/11/BLOG_20181111_200946_30.png "博客图集BLOG_20181111_200946_30.png") #### 进行表单文件的上传保存操作 **上传表单、文件上传**: http://www.pythondoc.com/flask-wtf/form.html `url`和`logo`如何获取? 定义文件上传保存的路径。 修改**app/\_\_init\_\_.py**增加文件保存路径 ```python # 定义文件上传保存的路径,在__init__.py文件所在目录创建media文件夹,用于保存上传的文件 app.config['UP_DIR'] = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'static/media/') ``` 上传文件重命名,以时间字符串+随机字符串+文件后缀的名称进行重命名 ```python 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 ``` 上传文件保存的逻辑操作 ```python @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) ``` ![BLOG_20181111_200956_78](/media/blog/images/2018/11/BLOG_20181111_200956_78.png "博客图集BLOG_20181111_200956_78.png") 将会在`static/media`文件夹下保存电信的视频和封面内容 ![BLOG_20181111_201017_43](/media/blog/images/2018/11/BLOG_20181111_201017_43.png "博客图集BLOG_20181111_201017_43.png") ![BLOG_20181111_201024_49](/media/blog/images/2018/11/BLOG_20181111_201024_49.png "博客图集BLOG_20181111_201024_49.png") ### 电影列表 #### 修改movie_list视图增加查询和分页 ```python @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参数**,如果不增加,后面使用会报错 ```html <a href="{{ url_for('admin.movie_list', page=1) }}"> <i class="fa fa-circle-o"></i> 电影列表 </a> ``` 直接使用之前创建的分页模块,获取电影的标签名称,通过`movie.tag.name`电影外键关系关联获取 ```html <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') }} ``` ![BLOG_20181111_201050_88](/media/blog/images/2018/11/BLOG_20181111_201050_88.png "博客图集BLOG_20181111_201050_88.png") ### 电影删除 #### 增加movie_delete删除电影视图 从数据库中查询到该电影,然后进行删除,同事需要从磁盘删除电影文件和封面文件 ```python @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删除电影链接和提示 ```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> ``` ![BLOG_20181111_201059_49](/media/blog/images/2018/11/BLOG_20181111_201059_49.png "博客图集BLOG_20181111_201059_49.png") ### 编辑电影 #### 创建movie_update电影编辑试图 - 初始化表单,并增加除了文件外的初始值 - 因为在`MovieForm`中要求`url`和`logo`不能为空,所以在修改视图中,需要允许上传文件为空,直接定义`required`为`False`没这样在前端表单中不会要求上传文件 - 提交表单,检查片名是否存在,如果不存在才添加数据库 - 保存提交的修改 - 如果电影文件和封面文件存在,先删除旧文件,然后保存新文件到数据库 ```python @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="">`来显示封面图 ```html 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`增加编辑按钮的链接 ```html <a class="label label-success" href="{{ url_for('admin.movie_update', update_id=movie.id) }}">编辑</a> ``` 当输入一个已存在的片名就会提示已存在。 ![BLOG_20181111_201132_74](/media/blog/images/2018/11/BLOG_20181111_201132_74.png "博客图集BLOG_20181111_201132_74.png") ![BLOG_20181111_201138_34](/media/blog/images/2018/11/BLOG_20181111_201138_34.png "博客图集BLOG_20181111_201138_34.png") 如果有上传电影文件或者是封面图片,那么将会删除旧文件,并保存新的文件到数据库,可以查看`/static/media/`下的文件变动。

很赞哦! (0)

文章交流

  • emoji
1人参与,1条评论
CC 2019年11月5日 14:27
Movie模型和Tag模型建立了relationship之后在展示电影列表的查询语句不需要用到join(Tag)

当前用户

未登录,点击   登录

站点信息

  • 建站时间:网站已运行683天
  • 系统信息:Linux
  • 后台程序:Python: 3.6.6
  • 网站框架:Django: 2.2.12
  • 文章统计:235 篇
  • 文章评论:40 条
  • 腾讯分析网站概况-腾讯分析
  • 百度统计网站概况-百度统计
  • 公众号:微信扫描二维码,关注我们
  • QQ群:QQ加群,下载网站的学习源码
返回
顶部
标题 换行 登录
网站