基于Flask的电影信息管理界面

一、简介

        使用Flask实现了一个简单的电影信息管理系统,用户可以通过网页界面进行电影信息的添加、编辑和删除操作。对应官网教程的第七章。

  Flask 入门教程 (helloflask.com)

二、文件组成

        在文章顶部下载相关资源

        

三、代码运行

        先按照官网上的教程将环境配置好

        在watchlist文件夹下运行Git Bash

输入    . env/Scripts/activate    进入虚拟环境

(注意在Git Bash中,Shift+Insert  是粘贴 Ctrl + C 是退出)

输入 flask run 启动程序

在浏览器中访问 localhost:5000 进入界面

测试一下加入新的数据

 

可以发现加入到了里面

四、代码解读 

3.1 app.py 代码解读

首先引入一些相关的库

# -*- coding: utf-8 -*- 声明了该文件的编码方式为 UTF-8
import os # 提供了与操作系统进行交互的功能,如文件和目录操作
import sys # 提供了与 Python 解释器相关的功能,如访问命令行参数和解释器状态

import click # 一个用于创建命令行接口的 Python 包
from flask import Flask
from flask import render_template # 用于渲染 HTML 模板
from flask import request # 用于访问请求数据(如表单数据)
from flask import url_for # 用于生成 URL
from flask import redirect # 用于重定向用户到指定的 URL
from flask import flash # 用于在请求之间传递一次性消息
from flask_sqlalchemy import SQLAlchemy # 使得我们可以使用 Python 类来表示数据库表,并使用 SQLAlchemy 提供的高级 API 来操作数据库

SQLite 数据库 URI 的前缀在不同操作系统上有所不同,为了确保 SQLAlchemy 能够正确地连接到指定路径的 SQLite 数据库,需要进行设置

# # sys.platform 返回一个描述操作系统的字符串 例如 'win32' 或 'win64'
WIN = sys.platform.startswith('win') 
if WIN:
    prefix = 'sqlite:///' # 操作系统是 Windows,则 prefix 被设置为 'sqlite:///'
else:
    prefix = 'sqlite:' # 操作系统不是 Windows,则 prefix 被设置为 'sqlite:'

创建并配置一个 Flask 应用,并初始化了SQLAlchemy,用于与 SQLite 数据库进行交互

app = Flask(__name__) # 创建一个 Flask 应用实例
app.config['SECRET_KEY'] = 'dev' # 设置应用的密钥,这里设置为 'dev',表示这是一个开发环境的密钥,实际生产环境中应该设置为一个更复杂的随机值
app.config['SQLALCHEMY_DATABASE_URI'] = prefix + os.path.join(app.root_path, 'data.db') # 设置 SQLAlchemy 的数据库 URI
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False # 禁用 SQLAlchemy 的事件系统,该系统会跟踪对象的修改。默认情况下,这个功能是启用的,但是它会消耗额外的内存,不需要时可以禁用以提高性能
db = SQLAlchemy(app) # 初始化 SQLAlchemy

定义一个initdb 命令行命令用于初始化数据库,drop可以实现先删除再创建

仅创建数据库: flask initdb

删除现有表并重新创建: flask initdb --drop

@app.cli.command() # 是一个装饰器,用于将一个函数注册为 Flask 应用的命令行命令。在命令行中使用 flask initdb 命令可以调用这个函数
@click.option('--drop', is_flag=True, help='Create after drop.')
def initdb(drop): # drop 参数的值由 --drop 选项决定
    if drop:
        db.drop_all() # 如果 drop 为 True,则调用 db.drop_all() 删除所有数据库表
    db.create_all() # 调用 db.create_all() 创建所有数据库表
    click.echo('Initialized database.') # 表示数据库初始化完成

 定义两个 SQLAlchemy 模型类 UserMovie,分别对应数据库中的两个表。每个模型类都继承自 db.Model,并定义了相应的字段(列)

# 下面的forge会用到
class User(db.Model):
    id = db.Column(db.Integer, primary_key=True) # 整数类型,主键
    name = db.Column(db.String(20)) # 字符串类型的字段,最大长度为20个字符

class Movie(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(60))
    year = db.Column(db.String(4))

定义一个 forge 命令行命令,用于生成假数据并插入到数据库中

在命令行中运行该命令以生成假数据并插入数据库:flask forge

@app.cli.command()
def 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'},
    ] # 包含多个字典的列表 movies,每个字典表示一部电影,包含 title 和 year 两个键
    user = User(name=name) # 创建一个 User 实例 user,其 name 属性为 'Grey Li'
    db.session.add(user) # 将该 user 实例添加到数据库会话中
    for m in movies:
        movie = Movie(title=m['title'], year=m['year']) # 遍历 movies 列表,为每部电影创建一个 Movie 实例
        db.session.add(movie) # 将每个 Movie 实例添加到数据库会话中
    db.session.commit() # 提交数据库会话,将所有添加的记录写入数据库
    click.echo('Done.') # 使用 click.echo 在命令行中输出 'Done.',表示数据生成和插入完成

注册一个上下文处理器

@app.context_processor # 注册一个上下文处理器函数
# 在模板 index.html 中,可以直接使用 {{ user.name }} 来访问用户的名字
def inject_user():
    user = User.query.first() # 查询 User 表中的第一条记录,通常用于获取数据库中的第一个用户
    return dict(user=user) # 如果用户表中没有记录,则 user 变量将为 None

定义一个错误处理函数 page_not_found,用于处理 HTTP 404 错误。

主要是为了美化404界面

@app.errorhandler(404) # 是 Flask 提供的装饰器,用于注册一个处理 HTTP 404 错误的函数。美化404界面
def page_not_found(e): # 定义了一个名为 page_not_found 的函数,用于处理 404 错误
    return render_template('404.html'), 404 # 当用户访问的页面不存在时,会返回一个自定义的 404 页面

实现对根目录的 GET 和 POST 请求的处理。当用户访问网站首页时,会显示所有电影的列表;当用户提交电影信息时,会将新电影添加到数据库,并显示成功消息。

@app.route('/', methods=['GET', 'POST']) #  Flask 提供的路由装饰器,用于将函数绑定到指定的 URL 路径上,指定了允许的 HTTP 请求方法,即 GET 和 POST
def index(): # 定义了一个名为 index 的函数,用于处理对根目录的请求
    if request.method == 'POST': # 当请求方法为 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('index')) # 重定向到首页

        movie = Movie(title=title, year=year)
        db.session.add(movie)
        db.session.commit()
        flash('Item created.') # 输入合法,将电影对象添加到数据库后,显示一个成功的闪现消息
        return redirect(url_for('index'))

    movies = Movie.query.all() # 当请求方法为 GET 时,查询所有的电影记录,并将它们传递给模板
    return render_template('index.html', movies=movies) # 渲染名为 index.html 的模板,并将查询到的电影列表传递给模板

实现对电影编辑页面的 GET 和 POST 请求的处理。当用户访问电影编辑页面时,会显示当前电影的信息;当用户提交编辑后的信息时,会更新数据库中对应的电影信息,并显示成功消息。

@app.route('/movie/edit/<int:movie_id>', methods=['GET', 'POST']) # 指定了一个动态 URL 变量 movie_id,它是一个整数类型
def edit(movie_id):
    movie = Movie.query.get_or_404(movie_id) # 如果找不到对应的电影对象,则会返回 404 错误页面

    # 与上面的命令行命令类似
    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)

实现对电影删除页面的 POST 请求的处理。当用户在电影删除页面上确认删除时,会从数据库中删除对应的电影信息,并显示成功消息。

@app.route('/movie/delete/<int:movie_id>', methods=['POST'])
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'))

3.2 父模板 base.html 代码解读

<!DOCTYPE html> <!-- 声明文档类型为html5 -->
<html lang="en"> <!-- 定义了文档的语言为英语-->

<head>
    <!-- 这是一个 Jinja2 块,用于定义模板中的头部内容。在子模板中,可以通过重写这个块来自定义头部内容 -->
    {% block head %} 
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <!-- 假设在 Python 代码中已经定义了一个 user 对象,其具有一个名为 name 的属性,那么在模板中使用 {{ user.name }} 就会将用户的名字动态地显示在页面标题中 -->
    <title>{{ user.name }}'s Watchlist</title>

    <!-- 生成一个指向静态文件夹static中的 favicon.ico 文件的 URL 地址 -->
    <link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">

    <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}" type="text/css">
    {% endblock %}
</head>

<body>
    <!-- Jinja2 模板中的循环结构,用于遍历闪现消息列表,并将每条消息以 <div class="alert"> 的形式显示在页面上 -->
    {% for message in get_flashed_messages() %}
        <div class="alert">{{ message }}</div>
    {% endfor %}

    <h2>
        <img alt="Avatar" class="avatar" src="{{ url_for('static', filename='images/avatar.png') }}">
        {{ user.name }}'s Watchlist
    </h2>
      
    <nav>
        <ul>
            <!-- 指向首页链接 -->
            <li><a href="{{ url_for('index') }}">Home</a></li>
        </ul>
    </nav>

    <!-- 允许在模板的子类中重写 content 块,以提供特定页面的自定义内容 -->
    {% block content %}{% endblock %}

    <!-- 页面的页脚部分,包含了版权信息和一个链接到 HelloFlask 网站的小标签 -->
    <footer>
        <small>&copy; 2018 <a href="http://helloflask.com/tutorial">HelloFlask</a></small>
	</footer>
</body>
</html>

3.3 子模板 index.html 代码解读

<!-- 指示模板继承自名为 base.html 的父模板 -->
{% extends 'base.html' %}

<!-- 块覆盖了父模板中同名的块(在父模板中是空白),因此在渲染页面时,将会显示子模板中定义的内容 -->
{% block content %}

<!-- 使用了模板语法,表示对电影列表 movies 应用了过滤器 length,以获取列表的长度 -->
<p>{{ movies|length }} Titles</p>

<!-- 定义了一个表单,使用 POST 方法提交数据 -->
<form method="post">
    <!-- autocomplete 表示禁止浏览器自动填充 required 表示不能为空 -->
    Name <input type="text" name="title" autocomplete="off" required> 
    Year <input type="text" name="year" autocomplete="off" required>
    <input class="btn" type="submit" name="submit" value="Add">
</form>

<ul class="movie-list">
    {% for movie in movies %}
    <li>{{ movie.title }} - {{ movie.year }}
        <span class="float-right">

            <!-- 编辑按钮,链接到编辑页面。使用了 url_for 函数生成编辑页面的 URL 地址 -->
            <a class="btn" href="{{ url_for('edit', movie_id=movie.id) }}">Edit</a>

            <!-- 删除表单,用于提交删除电影的请求。使用了 url_for 函数生成删除电影的 URL 地址。该表单使用了 POST 方法提交数据 -->
            <form class="inline-form" method="post" action="{{ url_for('.delete', movie_id=movie.id) }}">
                <input class="btn" type="submit" name="delete" value="Delete" onclick="return confirm('Are you sure?')">
            </form>

            <!-- IMDb 链接,点击链接将在新标签页中打开 IMDb 并搜索该电影的标题 -->
            <a class="imdb" href="https://www.imdb.com/find?q={{ movie.title }}" target="_blank" title="Find this movie on IMDb">IMDb</a>
        </span>
    </li>
    {% endfor %}
</ul>

<!-- alt="Walking Totoro":指定了图片的替代文本,即在图片无法显示时显示的文本 -->
<img alt="Walking Totoro" class="totoro" src="{{ url_for('static', filename='images/totoro.gif') }}" title="to~to~ro~">
{% endblock %} <!-- 用于结束 content 块的定义 -->

3.4 edit.html 代码解读

与index差不多,很容易理解

{% extends 'base.html' %}

{% block content %}
<h3>Edit item</h3>
<form method="post">
    Name <input type="text" name="title" autocomplete="off" required value="{{ movie.title }}">
    Year <input type="text" name="year" autocomplete="off" required value="{{ movie.year }}">
    <input class="btn" type="submit" name="submit" value="Update">
</form>
{% endblock %}

3.5 404.html 代码解读 

{% extends 'base.html' %}

{% block content %}
<ul class="movie-list">
    <li>
        Page Not Found - 404
        <span class="float-right">
            <a href="{{ url_for('index') }}">Go Back</a>
        </span>
    </li>
</ul>
{% endblock %}

  • 13
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值