flask 开发的个人笔记

发布时间 2023-04-03 23:15:35作者: 淦丘比


本博客仅作为笔记,不做教学,教程可以看大佬这的flask教程

环境

虚拟环境

创建虚拟环境

Windows $ python -m venv env
Linux 或 macOS $ python3 -m venv env

激活虚拟环境

Windows $ env\Scripts\activate
如果在 Windows 中使用 Git Bash,则是 . env/Scripts/activate
Linux 或 macOS $ . env/bin/activate

退出虚拟环境

$ deactivate

flask

视图函数

# 导入Flask库
from flask import Flask

# 创建一个Flask应用实例
app = Flask(__name__)

# 定义一个路由,当用户访问应用的根URL(例如:http://localhost:5000/)时,将触发此函数
@app.route('/')
def hello():
    # 返回一个简单的字符串,将在浏览器中显示
    return 'Welcome to My Watchlist!'

启动程序

flask run

url_for()的使用

当我有这么一个视图函数时。

@app.route('/user/<username>')
def user_profile(username):
    return f"Hello, {username}!"

可以通过url_for()来定位到该函数的URL,然后执行视图函数。
url = url_for('user_profile', username='JohnDoe')

环境设置

  • 自动导入系统环境变量的 python-dotenv:(env) $ pip install python-dotenv
  • 在新创建的 .flaskenv 文件里,我们写入一行 FLASK_ENV=development,将环境变量 FLASK_ENV 的值设为 development,以便开启调试模式:
    # .flaskenv 文件
    FLASK_ENV=development
    

模板

模板的使用方法

通用模板示例

htmlCopy code<!-- h1标签显示用户的个人主页标题,其中的变量username将被替换为实际值 -->
<h1>{{ username }}的个人主页</h1>

<!-- 使用Jinja2的条件语句判断变量bio是否存在(非空),缩进只是为了可读性,不是必须的 -->
{% if bio %}
    <!-- 如果bio变量存在(非空),则在一个<p>标签中显示bio的内容 -->
    <p>{{ bio }}</p>
{% else %}
    <!-- 如果bio变量不存在(为空),则显示一条默认消息 -->
    <p>自我介绍为空。</p>
{% endif %}  <!-- 大部分Jinja2语句都需要声明关闭,使用{% endif %}关闭if语句 -->

在这个模板中:

  • 使用{{ username }}插入变量username的值。在渲染模板时,需要传递一个包含username键的字典,以提供实际的值。{{ }}语法用于在HTML中输出变量。
  • 使用{% if bio %}和{% endif %}创建一个条件语句,判断变量bio是否存在(非空)。{% %}语法用于包含控制结构,如条件语句、循环等。

过滤器

举例{{ 变量|过滤器 }}
具体看

渲染模板

from flask import Flask, render_template

# ...

@app.route('/')
def index():
    return render_template('index.html', name=name, movies=movies)

静态文件

url_for()指向静态文件

固定为以下格式
url_for('static', filename='images/avatar.png

    • url_for函数在这里用于生成一个指向静态文件(如图片、CSS、JavaScript等)的URL。
    • url_for函数的第一个参数'static'告诉Flask你要生成一个静态文件的URL。
    • 第二个关键字参数filename用于指定静态文件的路径。注意,这里的参数名应该是filename,而不是name。
  1. 将filename更改为name可能导致不正确的URL生成,从而导致图片无法显示。所以,当在模板中使用url_for()调用本地图片时,后续参数名不能随意更改。确保使用正确的参数名filename来指定静态文件的路径。

数据库

重新看大佬教程

模板优化

模板上下文处理函数

对于多个模板内都需要使用的变量,我们可以使用 app.context_processor 装饰器注册一个模板上下文处理函数,如下所示:

@app.context_processor
def inject_user():  # 函数名可以随意修改
    user = User.query.first()
    return dict(user=user)  # 需要返回字典,等同于 return {'user': user}

这个函数返回的变量(以字典键值对的形式)将会统一注入到每一个模板的上下文环境中,因此可以直接在模板中使用。

模板继承

{% extends 'base.html' %}

{% block content %}
<p>{{ movies|length }} Titles</p>
<ul class="movie-list">
    {% for movie in movies %}
    <li>{{ movie.title }} - {{ movie.year }}
        <span class="float-right">
            <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>
<img alt="Walking Totoro" class="totoro" src="{{ url_for('static', filename='images/totoro.gif') }}" title="to~to~ro~">
{% endblock %}

表单

  • 表单示例
<form method="post">  <!-- 指定提交方法为 POST -->
    <label for="name">名字</label>
    <input type="text" name="name" id="name"><br>  <!-- 文本输入框 -->
    <label for="occupation">职业</label>
    <input type="text" name="occupation" id="occupation"><br>  <!-- 文本输入框 -->
    <input type="submit" name="submit" value="登录">  <!-- 提交按钮 -->
</form>
  • 表单的注意事项
    • 在 <form> 标签里使用 method 属性将提交表单数据的 HTTP 请求方法指定为 POST。如果不指定,则会默认使用 GET 方法,这会将表单数据通过 URL 提交,容易导致数据泄露,而且不适用于包含大量数据的情况。
    • <input> 元素必须要指定 name 属性,否则无法提交数据,在服务器端,我们也需要通过这个 name 属性值来获取对应字段的数据。
  • 表单的运行逻辑
    1. 从几个<input>中获取数据
    2. 通过<input type="submit">提供的按钮,提交数据。
    3. 以 post 方式,同时携带着数据访问 URL ,此时就可以通过传来的数据进行数据处理了。
  • flash 消息的使用
    • 传入消息内容
      flash('Item Created.')
    • 在模板中获取提示消息并显示
    <!-- 插入到页面标题上方 -->
    {% for message in get_flashed_messages() %}
    	<div class="alert">{{ message }}</div>
    {% endfor %}
    <h2>...</h2>
    

用户认证

密码保护(散列值生成、验证)

from werkzeug.security import generate_password_hash, check_password_hash


class User(db.Model):
    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)  # 返回布尔值

认证保护

视图保护

对于不允许未登录用户访问的视图,只需要为视图函数附加一个 login_required 装饰器就可以将未登录用户拒之门外。比如:

from flask_login import LoginManager

login_manager = LoginManager(app)  # 实例化扩展类
login_manager.login_view = 'login'	# 用户认证保护的重定向视图函数

# ...


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

添加了这个装饰器后,如果未登录的用户访问对应的 URL,Flask-Login 会把用户重定向到登录页面。

模板内容保护

在模板渲染时,会先判断当前用户的登录状态(current_user.is_authenticated)。如果用户没有登录(current_user.is_authenticated 返回 False),就不会渲染表单部分的 HTML 代码,即上面代码块中 {% if ... %} 和 {% endif %} 之间的代码。比如:

<!-- 在模板中可以直接使用 current_user 变量 -->
{% if current_user.is_authenticated %}
<form method="post">
    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>
{% endif %}

测试

有亿点长,感觉我还没学到家,还是看大佬吧。