1. 需要的css和js以及fonts样式都从网上下载
2.创建home前台管理的蓝图
home模块包下__init__.py中创建
创建蓝图,到处所有的视图函数
from flask import Blueprint
home=Blueprint('home',__name__)
from app.home.views import *
app下的__init__.py添加蓝图注册,连接各模块
from app.home import home as home_blueprint
# 注册home蓝图, 不添加前缀
app.register_blueprint(home_blueprint)
3.forms.py中编写需要的表单,
from flask_wtf import FlaskForm
from wtforms import SubmitField, StringField, PasswordField, FileField, TextAreaField
from wtforms.validators import DataRequired,Length,EqualTo,Email
from flask_wtf.file import FileAllowed
class BaseForm(FlaskForm):
username = StringField(
label="用户名",
validators=[
DataRequired()
]
)
password = PasswordField(
label="密码",
validators=[
DataRequired(),
Length(6, 12, message="密码长度必须为6-12")
]
)
class LoginForm(BaseForm):
submit = SubmitField(
label="登录"
)
class RegisterForm(BaseForm):
repassword = PasswordField(
label="确认密码",
validators=[
EqualTo('password', message="两次密码不一致")
]
)
email = StringField(
label="邮箱",
validators=[
Email(message="邮箱格式不正确")
]
)
submit = SubmitField(
label="注册"
)
class EditUserForm(FlaskForm):
username = StringField(
label="用户名",
validators=[
DataRequired()
]
)
email = StringField(
label="邮箱",
# validators=[
# Email(message="邮箱格式不正确")
# ]
)
phone = StringField(
label="电话",
# validators=[
# Regexp(r'1\d{10}', message="电话号码格式不正确")
# ]
)
face = FileField(
label="用户头像",
validators=[
# *******限制用户上传文件的格式
FileAllowed(['png', 'jpg'], message="用户头像格式错误, 必须为png或者jpg")
]
)
info = TextAreaField(
label="用户简介",
)
submit = SubmitField(
label="更新信息"
)
class PwdForm(FlaskForm):
old_pwd = PasswordField(
label="旧密码",
validators=[
DataRequired()
],
# 设置key-values值
render_kw = {
'placeholder' : "请输入旧密码"
}
)
new_pwd = PasswordField(
label="新密码",
validators=[
DataRequired()
],
render_kw={
'placeholder': "请输入新密码"
}
)
submit = SubmitField(
label="修改密码"
)
4.在home包内添加需要用到的装饰器以及函数
from functools import wraps
from flask import session, flash, redirect, url_for
def is_login(f):
"""用来判断用户是否登录成功"""
@wraps(f)
def wrapper(*args,**kwargs):
# 判断session对象中是否有seesion['user'],
# 如果包含信息, 则登录成功, 可以访问主页;
# 如果不包含信息, 则未登录成功, 跳转到登录界面;;
if session.get('user', None):
return f(*args, **kwargs)
else:
flash("用户必须登录才能访问%s" % (f.__name__))
return redirect(url_for('home.login'))
return wrapper
def change_filename(filename):
"""在原有文件的基础上添加时间标签"""
from datetime import datetime
return datetime.now().strftime('%Y%m%d_%H%M%S') + filename
5.视图函数home包下的views.py和templates包内home中的html文件,实现与前台交互
5-1 需要导入的模块包
import os
from flask import render_template, flash, redirect, url_for, session, request
from werkzeug.security import generate_password_hash
from app import app, db
from app.home.forms import RegisterForm, LoginForm, EditUserForm, PwdForm
from app.home.utils import is_login, change_filename
from app.models import Movie, Preview, User, Comment, Userlog, MovieCollect
from . import home
继承用的base.html
{% extends 'bootstrap/base.html' %}
{% block styles %}
{# 先继承父类的css样式导入 #}
{{ super() }}
<link rel="stylesheet" href="{{ url_for('static', filename='css/main.css') }}">
{% endblock %}
{% block scripts %}
{{ super() }}
{# 实质使导入moment.js库的, 通过Flask-moment集成了起来#}
{{ moment.include_moment() }}
<script src="{{ url_for('static', filename='js/echarts.min.js') }}"></script>
{% endblock %}
{% block navbar %}
<nav class="navbar navbar-default">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">微电影网站</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav navbar-right">
{# 分类讨论:
1. 如果没有登录, 显示登录和注册按钮;
2. 如果登录成功, 显示用户名称和注销按钮
#}
<li><a href="#">电影</a></li>
{% if not session.user %}
<li><a href="{{ url_for('home.login') }}">登录</a></li>
<li><a href="{{ url_for('home.register') }}">注册</a></li>
{% else %}
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
aria-haspopup="true"
aria-expanded="false">当前用户:{{ session.user }} <span class="caret"></span></a>
<ul class="dropdown-menu">
<li role="separator" class="divider"></li>
<li><a href="{{ url_for('home.logout') }}">注销</a></li>
</ul>
</li>
{% endif %}
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
{#让每个页面都可以获取闪现信息闪现#}
{% for item in get_flashed_messages() %}
<div class="alert alert-warning alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span
aria-hidden="true">×</span></button>
{{ item }}
</div>
{% endfor %}
{% endblock %}
侧边栏的实现 usermneu.html
<div class="col-lg-2">
<div class="list-group">
<a href="{{ url_for('home.user') }}" class="list-group-item" id="md-1">
<span class="glyphicon glyphicon-user"></span> 会员中心
</a>
<a href="{{ url_for('home.pwd') }}" class="list-group-item" id="md-2">
<span class="glyphicon glyphicon-lock"></span> 修改密码
</a>
<a href="{{ url_for('home.comments') }}" class="list-group-item" id="md-3">
<span class="glyphicon glyphicon-comment"></span> 评论记录
</a>
<a href="{{ url_for('home.userlog') }}" class="list-group-item" id="md-4">
<span class="glyphicon glyphicon-calendar"></span> 登录日志
</a>
<a href="{{ url_for('home.moviecollect') }}" class="list-group-item" id="md-5">
<span class="glyphicon glyphicon-heart"></span> 收藏电影
</a>
</div>
</div>
6-2 主页面视图函数
@home.route('/')
@home.route('/<int:page>/')
def index(page=1):
# 预告信息
previews = Preview.query.all()
# 标签信息
tags = Tag.query.all()
# 星级信息[{'num':1, 'info':'1星'}, {'num':2, 'info':'2星'}.........]
star_list = [{'num': num + 1, 'info': '%s星' % (num + 1)} for num in range(5)]
# 上映时间信息(只显示近5年的)
now_year = int(time.localtime().tm_year)
year_list = [year for year in range(now_year, now_year - 5, -1)]
# 电影查询
page_movies = Movie.query
# 1). 判断用户是否选择标签; GET请求方式;如果为0代表未选择;
tag_id = int(request.args.get('tag_id', 0))
if tag_id:
page_movies = page_movies.filter_by(tag_id=tag_id)
# 2). 判断用户是否选择星级;GET请求方式;如果为0代表未选择;
star_num = int(request.args.get('star_num', 0))
if star_num:
page_movies = page_movies.filter_by(star=star_num)
# 3). 判断用户是否选择年份;GET请求方式;如果为1代表所有日期;
year = int(request.args.get('year', 0))
print(year)
if year:
# 筛选年份
page_movies = page_movies.filter(extract('year', Movie.release_time) == year)
print(str(extract('year', Movie.release_time)))
# 播放数为1----从高到低进行排序;
# 播放数为0----从低到高进行排序;
play_num = int(request.args.get('play_num', 1))
if play_num == 1:
# 降序
page_movies = page_movies.order_by(Movie.play_num.desc())
else:
# 升序
page_movies = page_movies.order_by(Movie.play_num.asc())
comment_num = int(request.args.get('comment_num', 1)) # 1为从高到低,0为从低到好
if int(comment_num) == 1:
page_movies = page_movies.order_by(Movie.comment_num.desc())
else:
page_movies = page_movies.order_by(Movie.comment_num.asc())
page_movies = page_movies.paginate(page, per_page=app.config['PER_PAGE'])
return render_template('home/index.html',
previews=previews,
tags=tags,
star_list=star_list,
year_list=year_list,
page_movies=page_movies,
app=app,
)
对应的页面实现的index.html内容
{% extends 'home/base.html' %}
{#
1. 轮播图:http://www.runoob.com/try/try.php?filename=bootstrap3-plugin-carousal-caption
2. 各种选择标签
3. 相关电影: https://v3.bootcss.com/components/#thumbnails-custom-content
#}
{% block title %}首页{% endblock %}
{% block content %}
<div class="container-fluid">
<div class="col-lg-10 col-lg-offset-1">
{# 轮播图 #}
<div id="myCarousel" class="carousel slide">
<!-- 轮播(Carousel)指标 -->
<ol class="carousel-indicators">
{% for preview in previews %}
{% if loop.first %}
<li data-target="#myCarousel" data-slide-to="0" class="active"></li>
{% else %}
<li data-target="#myCarousel" data-slide-to="{{ loop.index0 }}"></li>
{% endif %}
{% endfor %}
</ol>
<!-- 轮播(Carousel)项目 -->
<div class="carousel-inner">
{# #}
{% for preview in previews %}
{% if loop.first %}
<div class="item active">
<img src="{{ url_for('static', filename=app.config['PREVIEW_UP_DIR_LAST'] + preview.logo) }}"
alt="First slide">
<div class="carousel-caption">{{ preview.name }}</div>
</div>
{% else %}
<div class="item">
<img src="{{ url_for('static', filename=app.config['PREVIEW_UP_DIR_LAST'] + preview.logo) }}"
alt="Second slide">
<div class="carousel-caption">{{ preview.name }}</div>
</div>
{% endif %}
{% endfor %}
</div>
<!-- 轮播(Carousel)导航 -->
<a class="left carousel-control" href="#myCarousel" role="button" data-slide="prev">
<span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>
<span class="sr-only">Previous</span>
</a>
<a class="right carousel-control" href="#myCarousel" role="button" data-slide="next">
<span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
<span class="sr-only">Next</span>
</a>
</div>
{# 各种选择标签 #}
<table class="table text-left table-bordered" id="movietags">
<tr>
<td style="width:10%;">电影标签</td>
<td style="width:80%;">
{% for tag in tags %}
<a class="label label-info" href="{{ url_for('home.index',page=1) }}?tag_id={{ tag.id }}">
<span class="glyphicon glyphicon-tag"></span> {{ tag.name }}</a>
{% endfor %}
</tr>
<tr>
<td>电影星级</td>
<td>
{% for star in star_list %}
<a class="label label-warning"
href="{{ url_for('home.index',page=1) }}?star_num={{ star.num }}">
<span class="glyphicon glyphicon-star"></span> {{ star.info }}</a>
{% endfor %}
</td>
</tr>
<tr>
<td>上映时间</td>
<td>
{% for year in year_list %}
<a class="label label-default"
href="{{ url_for('home.index',page=1) }}?year={{ year }}">
<span class="glyphicon glyphicon-time"></span> {{ year }}</a>
{% endfor %}
</td>
</tr>
<tr>
<td>播放数量</td>
<td>
<a class="label label-success"
href="{{ url_for('home.index',page=1) }}?play_num=1"
><span
class="glyphicon glyphicon-arrow-down"></span> 从高到底</a>
<a class="label label-danger"
href="{{ url_for('home.index',page=1) }}?play_num=0"><span
class="glyphicon glyphicon-arrow-up"></span> 从低到高</a>
</td>
</tr>
<tr>
<td>评论数量</td>
<td>
<a class="label label-success"
href="{{ url_for('home.index',page=1) }}?comment_num=1"
><span
class="glyphicon glyphicon-arrow-down"></span> 从高到底</a>
<a class="label label-danger"
href="{{ url_for('home.index',page=1) }}?comment_num=0"
><span
class="glyphicon glyphicon-arrow-up"></span> 从低到高</a>
</td>
</tr>
</table>
{# 电影列表信息 #}
<div class="row">
{% for movie in page_movies.items %}
<div class="col-sm-3 col-md-3">
<div class="thumbnail">
<img src="{{ url_for('static', filename=app.config['MOVIE_UP_DIR_LAST'] + movie.logo) }}"
alt="..." style="width: 240px; height: 330px">
<div class="caption">
<h3>{{ movie.name }}</h3>
<p>
{{ movie.info }}
</p>
<div>
{% for star in range(movie.star) %}
<span class="glyphicon glyphicon-star" style="color:#FFD119"></span>
{% endfor %}
{% for emptystar in range(5-movie.star) %}
<span class="glyphicon glyphicon-star-empty" style="color:#FFD119"></span>
{% endfor %}
</div>
<a href="{{ url_for('home.play', id=movie.id) }}" class="btn btn-success btn-block"
target="_blank" role="button">
<span class="glyphicon glyphicon-play"></span> 播放</a>
</div>
</div>
</div>
{% endfor %}
</div>
{# 分页显示 #}
<div class="col-md-12 text-center">
<nav aria-label="Page navigation">
<ul class="pagination">
<li>
<a href="#" aria-label="First">
<span aria-hidden="true">首页</span>
</a>
</li>
<li>
<a href="#" aria-label="Previous">
<span aria-hidden="true">上一页</span>
</a>
</li>
<li><a href="#">1 / 10</a></li>
<li>
<a href="#" aria-label="Next">
<span aria-hidden="true">下一页</span>
</a>
</li>
<li>
<a href="#" aria-label="Last">
<span aria-hidden="true">尾页</span>
</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
{% endblock %}
6-3 注册页面的视图函数
@home.route('/register/',methods=['GET','POST'])
def register():
form=RegisterForm()
if form.validate_on_submit():
# 1. 从前端获取用户输入的值;
email = form.email.data
username = form.username.data
password = form.password.data
# 2. 判断用户是否已经存在? 如果返回位None,说明可以注册;
u = User.query.filter_by(name=username).first()
if u:
flash("用户%s已经存在" % (u.name))
return redirect(url_for('home.register'))
else:
u = User(name=username, email=email)
u.password = generate_password_hash(password) # 对于密码进行加密
db.session.add(u)
db.session.commit()
flash("注册用户%s成功" % (u.name))
return redirect(url_for('home.login'))
return render_template('home/register.html',
form=form)
对应的页面实现的register.html内容
{% extends 'home/base.html' %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block title %}
注册
{% endblock %}
{% block content %}
<div class="container">
<div class="col-lg-8 col-lg-offset-2">
<div class="panel panel-success">
<div class="panel-heading">
<h1 class="panel-title"><span class="glyphicon glyphicon-log-in">
</span> 注册</h1>
</div>
<div class="panel-body">
{{ wtf.quick_form(form) }}
</div>
</div>
</div>
</div>
{% endblock %}
6-4 登录页面的视图函数
@home.route('/login/', methods=['POST', 'GET'])
def login():
from app.models import User, Userlog
form = LoginForm()
if form.validate_on_submit():
username = form.username.data
password = form.password.data
user = User.query.filter_by(name=username).first()
# 使用user.verify_password方法验证密码,
# 因为相同密码每一次generate_password_hash加密后都不一样
if user and user.verify_password(password):
# session信息的保存
session['user_id'] = user.id
session['user'] = user.name
flash("用户%s登录成功" % (user.name))
remote_ip = request.remote_addr
# 将登录信息写到日志中;
userlog = Userlog(user_id=user.id,
ip=remote_ip,
area='内网IP')
db.session.add(userlog)
db.session.commit()
# 从index蓝图里面寻找index函数;
return redirect(url_for('home.user'))
else:
flash("用户登录失败")
return redirect(url_for('home.login'))
return render_template('home/login.html',
form=form)
实现页面显示的login.html
{% extends 'home/base.html' %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block title %}
登录
{% endblock %}
{% block content %}
<div class="container">
<div class="col-lg-8 col-lg-offset-2">
<div class="panel panel-success">
<div class="panel-heading">
<h1 class="panel-title"><span class="glyphicon glyphicon-log-in">
</span> 登录</h1>
</div>
<div class="panel-body">
{{ wtf.quick_form(form) }}
</div>
</div>
</div>
</div>
{% endblock %}