超快速入门制作网站----Flask快速学习笔记

学习笔记

HelloFlask

from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
    return 'Welcome to My Watchlist!'

程序发现机制

默认是app.py,通过设置系统环境变量FLASK_APP设置启动程序,可以利用python-dotenv读取环境变量,.flaskenv用来存储Flask命令行系统相关的公开环境变量;而.env则用来存储敏感数据,不应该提交进Git仓库

恶意代码处理

用户输入的数据会包含恶意代码,所以不能直接作为响应返回,需要使用MarkupSafe(Flask的依赖之一)提供的escape()函数对name变量进行转义处理,比如把<转换成 <.这样在返回响应时浏览器就不会把它们当做代码执行.

from markupsafe import escape

@app.route('/user/<name>')
def user_page(name):
    return f'User: {escape(name)}'

修改视图函数名称

from flask import url_for
from markupsafe import escape

# ...

@app.route('/')
def hello():
    return 'Hello'

@app.route('/user/<name>')
def user_page(name):
    return f'User: {escape(name)}'

@app.route('/test')
def test_url_for():
    # 下面是一些调用示例(请访问 http://localhost:5000/test 后在命令行窗口查看输出的 URL):
    print(url_for('hello'))  # 生成 hello 视图函数对应的 URL,将会输出:/
    # 注意下面两个调用是如何生成包含 URL 变量的 URL 的
    print(url_for('user_page', name='greyli'))  # 输出:/user/greyli
    print(url_for('user_page', name='peter'))  # 输出:/user/peter
    print(url_for('test_url_for'))  # 输出:/test
    # 下面这个调用传入了多余的关键字参数,它们会被作为查询字符串附加到 URL 后面。
    print(url_for('test_url_for', num=2))  # 输出:/test?num=2
    return 'Test page'

模板

Flask会从程序实例所在模块同级目录的templates文件夹中寻找模板

Jinja2基本语法

  • {{ … }} 用来标记变量。
  • {% … %} 用来标记语句,比如 if 语句,for 语句等。
  • {# … #} 用来写注释。
变量

Jinja2还支持列表、字典和对象

{{ mydict['key'] }}
{{ mylist[3] }}
{{ mylist[myintvar] }}
{{ myobj.somemethod() }}

获取变量的属性有以下两种方式

{{ foo.bar }}
{{ foo['bar'] }}
Filter过滤器()

使用格式变量名|函数,将变量传给函数,再把函数值作为代码块的值,可以通过|调用多个函数
例子:

<!-- 带参数的 -->
{{变量 | 函数名(*args)}}

<!-- 不带参数可以省略括号 -->
{{变量 | 函数名}}

字符串操作:

safe:禁用转义
<p>{{ '<em>hello</em>' | safe }}</p>
capitalize:把变量值的首字母转成大写,其余字母转小写
<p>{{ 'hello' | capitalize }}</p>
lower:把值转成小写
<p>{{ 'HELLO' | lower }}</p>
upper:把值转成大写
<p>{{ 'hello' | upper }}</p>
title:把值中的每个单词的首字母都转成大写
<p>{{ 'hello' | title }}</p>
reverse:字符串反转
<p>{{ 'olleh' | reverse }}</p>
format:格式化输出
<p>{{ '%s is %d' | format('name',17) }}</p>
striptags:渲染之前把值中所有的HTML标签都删掉
<p>{{ '<em>hello</em>' | striptags }}</p>
truncate: 字符串截断
<p>{{ 'hello every one' | truncate(9)}}</p>

列表操作:

first:取第一个元素
<p>{{ [1,2,3,4,5,6] | first }}</p>
last:取最后一个元素
<p>{{ [1,2,3,4,5,6] | last }}</p>
length:获取列表长度
<p>{{ [1,2,3,4,5,6] | length }}</p>
sum:列表求和
<p>{{ [1,2,3,4,5,6] | sum }}</p>
sort:列表排序
<p>{{ [6,2,3,1,5,4] | sort }}</p>
测试判断
{% if user.age is equalto 42 %}
Ha, you are 42!
{% endif %}

如果要传入参数,可以在test后增加括号,也可以直接写在后面
常用的test

boolean,defined,equalto
escaped,none,sequence
string,number,reverse
replace
For(Else)循环

例子

<h1>Members</h1>
<ul>
{% for user in users %}
  <li>{{ user.username|e }}</li>
{% endfor %}
</ul>

<dl>
{% for key, value in my_dict.items() %}
    <dt>{{ key|e }}</dt>
    <dd>{{ value|e }}</dd>
{% endfor %}
</dl>

循环中的索引:

  • loop.index: 循环当前迭代(从1开始)。
  • loop.index0: 循环当前迭代(从0开始)。
  • loop.revindex: 循环迭代的数量(从1开始)。
  • loop.revindex0: 循环迭代的数量(从0开始)。
  • loop.first: 是否为迭代的第一步。
  • loop.last: 是否为迭代的最后一步。
  • loop.length: 序列中元素的数量。
if语句
{% if users %}
<ul>
{% for user in users %}
    <li>{{ user.username|e }}</li>
{% endfor %}
</ul>
{% endif %}

{% if kenny.sick %}
    Kenny is sick.
{% elif kenny.dead %}
    You killed Kenny!  You bastard!!!
{% else %}
    Kenny looks okay --- so far
{% endif %}

主页模板

在templates目录下创建一个index.html文件作为主页模板

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>{{ name }}'s Watchlist</title>
</head>
<body>
    <h2>{{ name }}'s Watchlist</h2>
    {# 使用 length 过滤器获取 movies 变量的长度 #}
    <p>{{ movies|length }} Titles</p>
    <ul>
        {% for movie in movies %}  {# 迭代 movies 变量 #}
        <li>{{ movie.title }} - {{ movie.year }}</li>  {# 等同于 movie['title'] #}
        {% endfor %}  {# 使用 endfor 标签结束 for 语句 #}
    </ul>
    <footer>
        <small>&copy; 2018 <a href="http://helloflask.com/book/3">HelloFlask</a></small>
    </footer>
</body>
</html>

然后准备虚拟数据并利用flask中的render_template()渲染模板

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'},
]
from flask import Flask, render_template
# ...
@app.route('/')
def index():
    return render_template('index.html', name=name, movies=movies)

静态文件

图片、css文件和JavaScript脚本文件存储在静态文件中,可以利用url_for文件获取文件的url,例如:

<img src="{{ url_for('static', filename='foo.jpg') }}">

在python脚本中,url_for()需要从flask包中导入,而在模板中可以直接使用,因为flask把一些常用的函数和对象添加到了模板的上下文中

添加Favcion

<head>
    ...
    <link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">
</head>

添加图片

<h2>
    <img alt="Avatar" src="{{ url_for('static', filename='images/avatar.png') }}">
    {{ name }}'s Watchlist
</h2>
...
<img alt="Walking Totoro" src="{{ url_for('static', filename='images/totoro.gif') }}">

添加ccs

static/style.css:定义页面样式

/* 页面整体 */
body {
    margin: auto;
    max-width: 580px;
    font-size: 14px;
    font-family: Helvetica, Arial, sans-serif;
}
/* 页脚 */
footer {
    color: #888;
    margin-top: 15px;
    text-align: center;
    padding: 10px;
}
/* 头像 */
.avatar {
    width: 40px;
}
/* 电影列表 */
.movie-list {
    list-style-type: none;
    padding: 0;
    margin-bottom: 10px;
    box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 10px 0 rgba(0, 0, 0, 0.12);
}
.movie-list li {
    padding: 12px 24px;
    border-bottom: 1px solid #ddd;
}
.movie-list li:last-child {
    border-bottom:none;
}
.movie-list li:hover {
    background-color: #f8f9fa;
}
/* 龙猫图片 */
.totoro {
    display: block;
    margin: 0 auto;
    height: 100px;
}

引用css文件

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

添加class属性

<h2>
    <img alt="Avatar" class="avatar" src="{{ url_for('static', filename='images/avatar.png') }}">
    {{ name }}'s Watchlist
</h2>
...
<ul class="movie-list">
    ...
</ul>
<img alt="Walking Totoro" class="totoro" src="{{ url_for('static', filename='images/totoro.gif') }}">

数据库

使用一个叫做Flask-SQLAlchemy的扩展来集成SQLAlchemy,利用pip3分别安装这两个包
例子:

import os
import sys

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

WIN = sys.platform.startswith('win')
if WIN:  # 如果是 Windows 系统,使用三个斜线
    prefix = 'sqlite:///'
else:  # 否则使用四个斜线
    prefix = 'sqlite:'

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = prefix + os.path.join(app.root_path, 'data.db')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False  # 关闭对模型修改的监控
# 在扩展类实例化前加载配置
db = SQLAlchemy(app)

编写数据库模型

class User(db.Model):  # 表名将会是 user(自动生成,小写处理)
    id = db.Column(db.Integer, primary_key=True)  # 主键
    name = db.Column(db.String(20))  # 名字

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))  # 电影年份

模型的编写需要有限制:

  • 模型类要声明继承 db.Model。
  • 每一个类属性(字段)要实例化 db.Column,传入的参数为字段的类型,下面的表格列出了常用的字段类。
  • 在 db.Column() 中添加额外的选项(参数)可以对字段进行设置。比如,primary_key 设置当前字段是否为主键。除此之外,常用的选项还有 nullable(布尔值,是否允许为空值)、index(布尔值,是否设置索引)、unique(布尔值,是否允许重复值)、default(设置默认值)等。
字段类说明
db.Integer整型
db.String (size)字符串,size 为最大长度
db.Text长文本
db.DateTime时间日期,Python datetime对象
db.Float浮点数
db.Boolean布尔值

创建数据库

接下来要在python shell中创建这些文件

(env) $ flask shell
>>> from app import db
>>> db.create_all()

>>> db.drop_all()
>>> db.create_all()

和 lask shell类似,我们可以编写一个自定义命令来自动执行创建数据库表操作:

import click

@app.cli.command()  # 注册为命令,可以传入 name 参数来自定义命令
@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.')  # 输出提示信息

之后便可用命令行重建数据库

(env) $ flask initdb
(env) $ flask initdb --drop
# 使用 --drop 选项可以删除表后重新创建

数据库操作

创建
>>> from app import User, Movie  # 导入模型类
>>> user = User(name='Grey Li')  # 创建一个 User 记录
>>> m1 = Movie(title='Leon', year='1994')  # 创建一个 Movie 记录
>>> m2 = Movie(title='Mahjong', year='1996')  # 再创建一个 Movie 记录
>>> db.session.add(user)  # 把新创建的记录添加到数据库会话
>>> db.session.add(m1)
>>> db.session.add(m2)
>>> db.session.commit()  # 提交数据库会话,只需要在最后调用一次即可
读取

通过对模型类query属性调用可选过滤方法和查询方法.
<模型类>.query.<过滤方法(可选)>.<查询方法>

常用的过滤方法:

过滤方法说明
filter()使用指定的规则过滤记录,返回新产生的查询对象
filter_by()使用指定规则过滤记录(以关键字表达式的形式),返回新产生的查询对象
order_by()根据指定条件对记录进行排序,返回新产生的查询对象
group_by()根据指定条件对记录进行分组,返回新产生的查询对象

常用查询的方法:

查询方法说明
all()返回包含所有查询记录的列表
first()返回查询的第一条记录,如果未找到,则返回 None
get(id)传入主键值作为参数,返回指定主键值的记录,如果未找到,则返回 None
count()返回查询结果的数量
first_or_404()返回查询的第一条记录,如果未找到,则返回 404 错误响应
get_or_404(id)传入主键值作为参数,返回指定主键值的记录,如果未找到,则返回 404 错误响应
paginate()返回一个 Pagination 对象,可以对记录进行分页处理

例子:

>>> from app import Movie  # 导入模型类
>>> movie = Movie.query.first()  # 获取 Movie 模型的第一个记录(返回模型类实例)
>>> movie.title  # 对返回的模型类实例调用属性即可获取记录的各字段数据
'Leon'
>>> movie.year
'1994'
>>> Movie.query.all()  # 获取 Movie 模型的所有记录,返回包含多个模型类实例的列表
[<Movie 1>, <Movie 2>]
>>> Movie.query.count()  # 获取 Movie 模型所有记录的数量
2
>>> Movie.query.get(1)  # 获取主键值为 1 的记录
<Movie 1>
>>> Movie.query.filter_by(title='Mahjong').first()  # 获取 title 字段值为 Mahjong 的记录
<Movie 2>
>>> Movie.query.filter(Movie.title=='Mahjong').first()  # 等同于上面的查询,但使用不同的过滤方法
<Movie 2>

提示:我们在说 Movie 模型的时候,实际指的是数据库中的 movie 表。表的实际名称是模型类的小写形式(自动生成),如果你想自己指定表名,可以定义 __tablename__ 属性。

更新
>>> movie = Movie.query.get(2)
>>> movie.title = 'WALL-E'  # 直接对实例属性赋予新的值即可
>>> movie.year = '2008'
>>> db.session.commit()  # 注意仍然需要调用这一行来提交改动
删除
>>> movie = Movie.query.get(1)
>>> db.session.delete(movie)  # 使用 db.session.delete() 方法删除记录,传入模型实例
>>> db.session.commit()  # 提交改动

在程序里操作数据库

@app.route('/')
def index():
    user = User.query.first()  # 读取用户记录
    movies = Movie.query.all()  # 读取所有电影记录
    return render_template('index.html', user=user, movies=movies)
#同时index.html中的两处name变量也要相应的更新为user.name属性

创建虚拟数据

import click


@app.cli.command()
def forge():
    """Generate fake data."""
    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.')

如此执行flask forge命令可以将虚拟数据添加到数据库里

模板优化

自定义错误界面

首先编写错误界面模板404.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>{{ user.name }}'s Watchlist</title>
    <link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">
    <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}" type="text/css">
</head>
<body>
    <h2>
        <img alt="Avatar" class="avatar" src="{{ url_for('static', filename='images/avatar.png') }}">
        {{ user.name }}'s Watchlist
    </h2>
    <ul class="movie-list">
        <li>
            Page Not Found - 404
            <span class="float-right">
                <a href="{{ url_for('index') }}">Go Back</a>
            </span>
        </li>
    </ul>
    <footer>
        <small>&copy; 2018 <a href="http://helloflask.com/book/3">HelloFlask</a></small>
    </footer>
</body>
</html>

接着使用app.errorhandler()装饰器注册错误处理函数

@app.errorhandler(404)  # 传入要处理的错误代码
def page_not_found(e):  # 接受异常对象作为参数
    user = User.query.first()
    return render_template('404.html', user=user), 404  # 返回模板和状态码

模板上下文处理函数

使用app.context_processor装饰器注册一个模板上下文处理函数,这个函数返回的变量(以字典键值对的形式)将会统一注入到每一个模板的上下文环境中,因此可以直接在模板中使用,如此便可简化上个404代码撰写

@app.context_processor
def inject_user():
    user = User.query.first()
    return dict(user=user)

@app.errorhandler(404)
def page_not_found(e):
    return render_template('404.html'), 404

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

使用模板继承组织模板

编写基础模板base.html

<!DOCTYPE html>
<html lang="en">
<head>
    {% block head %}
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{ user.name }}'s Watchlist</title>
    <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>
    <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>
    {% block content %}{% endblock %}
    <footer>
        <small>&copy; 2018 <a href="http://helloflask.com/book/3">HelloFlask</a></small>
    </footer>
</body>
</html>

导航栏对应的CSS代码如下所示:

nav ul {
    list-style-type: none;
    margin: 0;
    padding: 0;
    overflow: hidden;
    background-color: #333;
}

nav li {
    float: left;
}

nav li a {
    display: block;
    color: white;
    text-align: center;
    padding: 8px 12px;
    text-decoration: none;
}

nav li a:hover {
    background-color: #111;
}

如此,子模板的编写将会变的非常的简单,如下:

{% 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 %}
{% 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 %}

添加链接

<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>

对应css如下:

.float-right {
    float: right;
}

.imdb {
    font-size: 12px;
    font-weight: bold;
    color: black;
    text-decoration: none;
    background: #F5C518;
    border-radius: 5px;
    padding: 3px 5px;
}

创建表单

在html里,用户编写表单获取用户输入,一个典型的表单如下所示:

<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>
  • 在 标签里使用 method 属性将提交表单数据的 HTTP 请求方法指定为 POST。如果不指定,则会默认使用 GET 方法,这会将表单数据通过 URL 提交,容易导致数据泄露,而且不适用于包含大量数据的情况。
  • 元素必须要指定 name 属性,否则无法提交数据,在服务器端,我们也需要通过这个 name 属性值来获取对应字段的数据。

创建新条目

<p>{{ movies|length }} Titles</p>
<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>

css如下:

/* 覆盖某些浏览器对 input 元素定义的字体 */
input[type=submit] {
    font-family: inherit;
}

input[type=text] {
    border: 1px solid #ddd;
}

input[name=year] {
    width: 50px;
}

.btn {
    font-size: 12px;
    padding: 3px 5px;
    text-decoration: none;
    cursor: pointer;
    background-color: white;
    color: black;
    border: 1px solid #555555;
    border-radius: 5px;
}

.btn:hover {
    text-decoration: none;
    background-color: black;
    color: white;
    border: 1px solid black;
}

处理表单数据

在 HTTP 中,GET 和 POST 是两种最常见的请求方法,其中 GET 请求用来获取资源,而 POST 则用来创建 / 更新资源。我们访问一个链接时会发送 GET 请求,而提交表单通常会发送 POST 请求.为了能够处理 POST 请求,我们需要修改一下视图函数:

from flask import request, url_for, redirect, flash

# ...

@app.route('/', methods=['GET', 'POST'])
def index():
    if request.method == 'POST':  # 判断是否是 POST 请求
        # 获取表单数据
        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('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()
    return render_template('index.html', movies=movies)

@app.route('/movie/edit/<int:movie_id>', methods=['GET', 'POST'])
def edit(movie_id):
    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 请求
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'))  # 重定向回主页

下面在基模板(base.html)里使用 get_flashed_messages() 函数获取提示消息并显示:

<!-- 插入到页面标题上方 -->
{% for message in get_flashed_messages() %}
    <div class="alert">{{ message }}</div>
{% endfor %}
<h2>...</h2>

emplates/edit.html:编辑页面模板

{% 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 %}

index.html

<span class="float-right">
    <a class="btn" href="{{ url_for('edit', movie_id=movie.id) }}">Edit</a>
    ...
</span>

<span class="float-right">
    ...
    <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>
    ...
</span>

样式如下:

.alert {
    position: relative;
    padding: 7px;
    margin: 7px 0;
    border: 1px solid transparent;
    color: #004085;
    background-color: #cce5ff;
    border-color: #b8daff;
    border-radius: 5px;
}
.inline-form {
    display: inline;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Flask-Security是一个基于 Flask 的安全扩展,可以帮助你快速实现常见的用户认证和授权功能。下面是一个快速入门指南: 1. 安装 Flask-Security 使用 pip 安装 Flask-Security: ``` pip install flask-security ``` 2. 初始化 Flask-Security 在 Flask 应用中初始化 Flask-Security: ```python from flask import Flask from flask_sqlalchemy import SQLAlchemy from flask_security import Security, SQLAlchemyUserDatastore app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///myapp.db' app.config['SECRET_KEY'] = 'super-secret' db = SQLAlchemy(app) from myapp.models import User, Role user_datastore = SQLAlchemyUserDatastore(db, User, Role) security = Security(app, user_datastore) ``` 在这个例子中,我们使用 SQLAlchemyUserDatastore 将 Flask-Security 和 SQLAlchemy 集成在一起,同时使用 Security 类来初始化 Flask-Security。 3. 创建用户模型 创建一个 User 模型来存储用户信息: ```python from flask_security import UserMixin, RoleMixin class User(db.Model, UserMixin): id = db.Column(db.Integer, primary_key=True) email = db.Column(db.String(255), unique=True) password = db.Column(db.String(255)) active = db.Column(db.Boolean()) roles = db.relationship('Role', secondary='user_roles', backref=db.backref('users', lazy='dynamic')) class Role(db.Model, RoleMixin): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(80), unique=True) description = db.Column(db.String(255)) ``` 在这里,我们使用 UserMixin 和 RoleMixin 来添加一些常见的用户和角色属性。 4. 创建用户和角色关系模型 创建一个 user_roles 表来存储用户和角色之间的关系: ```python user_roles = db.Table('user_roles', db.Column('user_id', db.Integer(), db.ForeignKey('user.id')), db.Column('role_id', db.Integer(), db.ForeignKey('role.id'))) ``` 5. 配置 Flask-Security 在 Flask 应用中配置 Flask-Security: ```python app.config['SECURITY_PASSWORD_SALT'] = 'super-secret-salt' app.config['SECURITY_REGISTERABLE'] = True app.config['SECURITY_SEND_REGISTER_EMAIL'] = False app.config['SECURITY_USER_IDENTITY_ATTRIBUTES'] = ['email'] ``` 在这个例子中,我们为密码添加了一个盐值,启用了用户注册功能,并且关闭了注册时发送电子邮件的功能。 6. 添加用户认证和授权路由 添加用户认证和授权路由到 Flask 应用中: ```python from flask import render_template, redirect, url_for from flask_security import login_required, current_user @app.route('/') def home(): return render_template('home.html') @app.route('/dashboard') @login_required def dashboard(): return render_template('dashboard.html', user=current_user) ``` 在这里,我们使用 login_required 装饰器来保护 dashboard 路由,只有已登录用户才能访问该路由。 7. 创建模板 创建一个 home.html 模板来显示主页: ```html <h1>Welcome to My App</h1> {% if current_user.is_authenticated() %} <p>Hello {{ current_user.email }}!</p> <p><a href="{{ url_for('security.logout') }}">Logout</a></p> {% else %} <p><a href="{{ url_for('security.login') }}">Login</a></p> {% endif %} ``` 创建一个 dashboard.html 模板来显示仪表盘: ```html <h1>Dashboard</h1> <p>Hello {{ user.email }}!</p> <p><a href="{{ url_for('security.logout') }}">Logout</a></p> ``` 在这里,我们使用 current_user 对象来检查当前用户是否已经登录,并使用 url_for 方法来生成登出链接。 这就是 Flask-Security 的快速入门指南。有了这个基础,你可以探索 Flask-Security 的更多功能,例如密码重置、电子邮件确认、角色管理等。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值