flask+HTML实现电影观看清单web应用

这是一个使用Flask框架构建的电影观看清单Web应用的后端代码详解。应用包括用户登录、电影管理(添加、更新、删除)、数据库初始化、错误处理等功能。通过Python的Flask-Login和Flask-SQLAlchemy库实现用户认证和数据持久化。源码中详细介绍了每个功能的实现方法,包括用户登录、登出、权限保护、数据库操作等。
摘要由CSDN通过智能技术生成

引言

1.工程运行的依赖见requirements.txt,data.db是数据库文件。
2.工程运行方法,打开pycharm,选择该工程,选择pycharm底部的终端。
3.cd进入到工程文件夹MyTest,输入flask run,即可生成一个本地网址,进入该网址即可。
4.其中Full_version.py是所有后端flask代码集合的一个文件。实际运行中,我们把该文件内的代码分类保存在了watchlist文件夹中的五个.py文件中。
5.最原始的工程结构如下,把Full_version.py改名为app.py,将工程结构恢复如下,终端输入python app.py即可运行。
在这里插入图片描述
6.管理员登录账户:
账号名:cz
密码:123
7.新增管理员账户方法如下:
打开工程后,终端输入flask admin,依次输入新增用户名,密码。

功能简要说明

  • 登入登出管理员
  • 添加、更新、删除电影名
  • 登入后,修改管理员用户名
  • 未登录前,自动跳到登录界面

后端完整代码

Full_version.py

# 该文件夹是包含了本工程所有的后端flask函数代码
from flask import Flask
from werkzeug.security import generate_password_hash, check_password_hash
from flask import request, url_for, redirect, flash
import os
import click
import sys
from flask_login import login_required, logout_user
from flask_login import login_user,current_user,UserMixin
from flask_login import LoginManager
from flask_sqlalchemy import SQLAlchemy
from flask import Flask, render_template
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(app.root_path, 'data.db')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False  # 关闭对模型修改的监控
db = SQLAlchemy(app)  # 初始化扩展,传入程序实例 app
# flash() 函数在内部会把消息存储到 Flask 提供的 session 对象里。
# session 用来在请求间存储数据,它会把数据签名后存储到浏览器的 Cookie 中,所以我们需要设置签名所需的密钥:
app.config['SECRET_KEY'] = 'dev'  # 等同于 app.secret_key = 'dev'
# 在扩展类实例化前加载配置
login_manager = LoginManager(app)  # 实例化扩展类
# 未登录的用户访问对应的 URL,Flask-Login 会把用户重定向到登录页面,并显示一个错误提示。
# 为了让这个重定向操作正确执行,我们还需要把 login_manager.login_view 的值设为我们程序的登录视图端点(函数名)
login_manager.login_view = 'login'

# 存储用户信息的类
# 添加 username 字段和 password_hash 字段,分别用来存储登录所需的用户名和密码散列值
# 继承 Flask-Login 提供的 UserMixin 类,拥有几个用于判断认证状态的属性和方法,
# 其中最常用的是 is_authenticated 属性:如果当前用户已经登录,那么 current_user.is_authenticated 会返回 True
class User(db.Model,UserMixin):  # 表名将会是 user(自动生成,小写处理)
    id = db.Column(db.Integer, primary_key=True)  # 主键
    name = db.Column(db.String(20))  # 名字
    # 同时添加两个方法来实现设置密码和验证密码的功能
    username = db.Column(db.String(20))  # 用户名
    password_hash = db.Column(db.String(128))  # 密码散列值
    def set_password(self, password):  # 用来设置密码的方法,接受密码作为参数
        self.password_hash = generate_password_hash(password)  # 将生成的密码保持到对应字段
    def validate_password(self, password):  # 用于验证密码的方法,接受密码作为参数
        return check_password_hash(self.password_hash, password)  # 返回布尔值

class Movie(db.Model):  # 表名将会是 movie
    id = db.Column(db.Integer, primary_key=True)  # 主键
    title = db.Column(db.String(60))  # 电影标题
    year = db.Column(db.String(4))  # 电影年份

# 自定义命令初始化数据库
@app.cli.command()  # 注册为命令
@click.option('--drop', is_flag=True, help='Create after drop.')  # 设置选项
def initdb(drop):
    """Initialize the database."""
    if drop:  # 判断是否输入了选项
        db.drop_all()
    db.create_all()
    click.echo('Initialized database.')  # 输出提示信息
# 初始化 Flask-Login,其包含实现用户认证需要的各类功能函数
@login_manager.user_loader
def load_user(user_id):  # 创建用户加载回调函数,接受用户 ID 作为参数
    user = User.query.get(int(user_id))  # 用 ID 作为 User 模型的主键查询对应的用户
    return user  # 返回用户对象
# 创建自定义命令forge
@app.cli.command()
def forge():
    """产生虚拟数据.执行flask forge就会把虚拟数据添加进数据库"""
    db.create_all()
    # 全局的两个变量移动到这个函数内
    name = 'Grey Li'
    movies = [
        {'title': 'My Neighbor Totoro', 'year': '1988'},
        {'title': 'Dead Poets Society', 'year': '1989'},
        {'title': 'A Perfect World', 'year': '1993'},
        {'title': 'Leon', 'year': '1994'},
        {'title': 'Mahjong', 'year': '1996'},
        {'title': 'Swallowtail Butterfly', 'year': '1996'},
        {'title': 'King of Comedy', 'year': '1999'},
        {'title': 'Devils on the Doorstep', 'year': '1999'},
        {'title': 'WALL-E', 'year': '2008'},
        {'title': 'The Pork of Music', 'year': '2012'},
    ]
    user = User(name=name)
    db.session.add(user)
    for m in movies:
        movie = Movie(title=m['title'], year=m['year'])
        db.session.add(movie)
    db.session.commit()
    click.echo('Done.')
# 生成管理员账户
@app.cli.command()
@click.option('--username', prompt=True, help='The username used to login.')
@click.option('--password', prompt=True, hide_input=False, confirmation_prompt=True, help='The password used to login.')
def admin(username, password):
    """Create user."""
    db.create_all()
    user = User.query.first()
    if user is not None:
        click.echo('Updating user...')
        user.username = username
        user.set_password(password)  # 设置密码
    else:
        click.echo('Creating user...')
        user = User(username=username, name='Admin')
        user.set_password(password)  # 设置密码
        db.session.add(user)
    db.session.commit()  # 提交数据库会话
    click.echo('Done.')
# 模板上下文处理函数
@app.context_processor
def inject_user():  # 函数名可以随意修改
    user = User.query.first()
    # 函数返回的变量(以字典键值对的形式)将会统一注入到每一个模板(首页模板,404错误页面模板)的上下文环境
    # 中,因此可以直接在模板中使用
    return dict(user=user)  # 需要返回字典,等同于 return {'user': user}
@app.route('/',methods=['GET', 'POST'])
# 其中 GET 请求用来获取资源,而 POST 则用来创建 / 更新资源。
def index():
    if request.method == 'POST':  # 判断是否是 POST 请求
        # 获取表单数据
        if not current_user.is_authenticated:  # 如果当前用户未认证
            return redirect(url_for('index'))  # 重定向到主页
        title = request.form.get('title')  # 传入表单对应输入字段的 name 值
        year = request.form.get('year')
        # 验证数据
        if not title or not year or len(year) > 4 or len(title) > 60:
            # flash页面上显示一个提示消息
            flash('Invalid input.')  # 显示错误提示
            return redirect(url_for('index'))  # 重定向回主页
        # 保存表单数据到数据库
        movie = Movie(title=title, year=year)  # 创建记录
        db.session.add(movie)  # 添加到数据库会话
        db.session.commit()  # 提交数据库会话
        flash('Item created.')  # 显示成功创建的提示
        return redirect(url_for('index'))  # 重定向回主页
    # user = User.query.first()  # 读取用户记录
    movies = Movie.query.all()  # 读取所有电影记录
    return render_template('index.html', movies=movies)
# 404 错误处理函数
@app.errorhandler(404)  # 传入要处理的错误代码
def page_not_found(e):  # 接受异常对象作为参数
    return render_template('404.html'), 404  # 返回模板和状态码

# 更新电影条目
@app.route('/movie/edit/<int:movie_id>', methods=['GET', 'POST'])
@login_required
def edit(movie_id):
    # movie_id 变量是电影条目记录在数据库中的主键值
    # get_or_404() 方法,它会返回对应主键的记录,如果没有找到,则返回 404 错误响应
    movie = Movie.query.get_or_404(movie_id)

    if request.method == 'POST':  # 处理编辑表单的提交请求
        title = request.form['title']
        year = request.form['year']

        if not title or not year or len(year) != 4 or len(title) > 60:
            flash('Invalid input.')
            return redirect(url_for('edit', movie_id=movie_id))  # 重定向回对应的编辑页面

        movie.title = title  # 更新标题
        movie.year = year  # 更新年份
        db.session.commit()  # 提交数据库会话
        flash('Item updated.')
        return redirect(url_for('index'))  # 重定向回主页

    return render_template('edit.html', movie=movie)  # 传入被编辑的电影记录
# 删除电影条目
@app.route('/movie/delete/<int:movie_id>', methods=['POST'])  # 限定只接受 POST 请求
#  对于不允许未登录用户访问的视图,只需要为视图函数附加一个 login_required 装饰器就可以将未登录用户拒之门外
@login_required  # 登录保护
def delete(movie_id):
    movie = Movie.query.get_or_404(movie_id)  # 获取电影记录
    db.session.delete(movie)  # 删除对应的记录
    db.session.commit()  # 提交数据库会话
    flash('Item deleted.')
    return redirect(url_for('index'))  # 重定向回主页
# 用户登录页面
@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        if not username or not password:
            flash('Invalid input.')
            return redirect(url_for('login'))
        user = User.query.first()
        # 验证用户名和密码是否一致
        if username == user.username and user.validate_password(password):
            login_user(user)  # 登入用户
            flash('Login success.')
            return redirect(url_for('index'))  # 重定向到主页
        flash('Invalid username or password.')  # 如果验证失败,显示错误消息
        return redirect(url_for('login'))  # 重定向回登录页面
    return render_template('login.html')
# 用户登出
@app.route('/logout')
@login_required  # 用于视图保护,后面会详细介绍
def logout():
    logout_user()  # 登出用户
    flash('Goodbye.')
    return redirect(url_for('index'))  # 重定向回首页
# 支持用户设计名字
@app.route('/settings', methods=['GET', 'POST'])
@login_required
def settings():
    if request.method == 'POST':
        name = request.form['name']
        if not name or len(name) > 20:
            flash('Invalid input.')
            return redirect(url_for('settings'))
        current_user.name = name
        # current_user 会返回当前登录用户的数据库记录对象
        # 等同于下面的用法
        # user = User.query.first()
        # user.name = name
        db.session.commit()
        flash('Settings updated.')
        return redirect(url_for('index'))
    return render_template('settings.html')
if __name__ == '__main__':
    app.run(debug=True)

完整源码链接

gitee链接,点此进入

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值