flask入门教程实践之开发第一个web程序

《flask入门教程》实践

学校安排了一个网上实习。
我三天打鱼,两天晒网。
于是乎,自己看书实践。
书籍: Flask 入门教程 使用 Python 和 Flask 开发你的第一个 Web 程序
本书源码:https://github.com/greyli/flask-tutorial
我的仓库:https://github.com/da1234cao/watchlist
下面内容为仓库的README内容,我搬运到这里。


零 摘要与总结

这本书,作为入门书籍,不断完善一个例子,还是挺好。

当年我的java web,稀烂。现在也是。web以后会是我绕不开的槛。

现在会了点flask,可以简单在互联网上发布一个网页,还是很舒畅。

路由:当我们在浏览器输入一个地址,敲下IP。对应的视图函数,处理get请求。如果是提交表单,或许要处理的是post请求。

表单:说到表单,不得不提增删该查。其中具有代表性的是表单的修改。①主页的修改按钮,是一个超链接,传递要修改的id到后端;②后端处理这个get请求,根据id查到对应的数据;③传入参数给一个渲染修改页面;④在呈现的页面修改内容,按下提交按钮;⑤后端处理这个post请求,修改可以写回数据库;再次查询,重定向回主页。

模板渲染与重定向:上面提到这两个概念,我也不是很清楚。我知道的是,当要展示一个页面出来的时候,用的是模板渲染,它的地址不变;而使用重定向,是跳转到另一个页面,它的地址改变。

用户认证:当前段的内容传递到后端来处理;后端的处理完,再在前段展示。除了这两点,还有用户认证。
不同的页面之间,一直保持登录状态;登录与不登录之间的页面显示不同;禁止直接访问也谢url;

测试:测试很有必要。测试不同路径,代码的执行情况。不断增加单元测试。

当开发测试都完成,是不是事情就干完了?



chapter01 准备工作

参考:要不我们还是用回 virtualenv/venv 和 pip 吧

#流程:

#克隆一个空的仓库
git clone git@github.com:da1234cao/watchlist.git

#charm 打开该仓库目录文件,发现默认的是python2.7
charm watchlist

#包安装
pip3 install virtualenv
sudo apt install python3-venv

#创建虚拟环境watchlist
python3 -m venv watchlist

#激活虚拟环境
source ./watchlist/bin/activate 

#安装flask
(watchlist) pip install flask

#提交仓库
vim .gitignore #创建一个.gitignore
git status; git add; git commit; git push;


chapter02 Hello,Flask

在学习语言的时候,验证我们环境的安装情况,我们,通常会输出 ”神奇“的 “hello world”。

此处也是。代码见上面的仓库。

环境变量

#运行flask run,当默认的程序名称不是app.py
export FLASK_APP=XXX

#FLASK_ENV默认为 production 。在开发时,我们需要开启调试模式( debug mode) 。 
export FLASK_ENV=development 

为了不用每次打开新的终端会话都要设置环境变量, 我们安装用来管理系统环境变量的 python-dotenv。


URL规则

绑定多个URL、url_for的使用。

url_for() 函数最简单的用法是以视图函数名作为参数,返回对应的url。

from flask import url_for

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

@app.route('/user/<name>')
def user_page(name):
	return 'User: %s' % name

@app.route('/test')
def test_url_for():
	# 下面是一些调用示例( 请在命令行窗口查看输出的 URL) :
	print(url_for('hello')) # 输出: /
	# 注意下面两个调用是如何生成包含 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

在这里插入图片描述



chapter03 模板

上一章,我们视图函数是这样定义的:

def hello():
	return '<h1>Hello Totoro!</h1><img src="http://helloflask.com/totoro.gif">'

但是,我们的html肯定不是一个字符串可以解决的。

所以我们得使用html文件。

我们把包含变量和运算逻辑的 HTML 或其他格式的文本叫做模板, 执行这些变量替换和逻辑计算工作的过程被称为渲染, 这个工作由我们这一章要学习使用的模板渲染引擎——Jinja2 来完成。

我们使用render_template() 函数把模板渲染出来。

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

在这里插入图片描述



chapter04 静态文件

上一章是渲染模板。静态文件( static files) 和我们的模板概念相反, 指的是内容不需要动态生成的文
件。 比如图片、 CSS 文件和 JavaScript 脚本等。

主要点:生成静态URL

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

其他内容是css的部分。我仅仅知道最基本的css,写出来的难看得很。我拷贝了书中的css.

在这里插入图片描述



chapter 05 数据库

这章,略微有点多。

安装数据库工具+设置数据库的URI+创建数据模型+注册命令+增删该查

安装依赖包、连接到数据库

下面用的是root用户登录。当然,最好可以用其他用户登录。

参考:mysql环境准备

#安装依赖包
pip install flask-sqlalchemy
pip install pymysql

#设置数据库URI
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:1*******@localhost:3306/userdb'

#创建数据库
mysql> create database watchlist

创建数据模型

# 用户表
class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)  # 主键
    name = db.Column(db.String(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))  # 电影年份

增删该查

#增加
>>> 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() # 提交数据库会话, 只需要在最后调用一次即可

#查
>>> Movie.query.all() # 获取 Movie 模型的所有记录, 返回包含多个模型类实例的列表
>>> Movie.query.filter(Movie.title=='Mahjong').first() # 等同于上面的查询, 但使用不同的过滤方法

#更新
>>> 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() # 提交改动

命令注册

参考:Python 命令行之旅:深入 click 之选项篇

@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.')

在这里插入图片描述



chapter06 模板的优化

自定义错误页面+模板的上下文处理函数+模板继承

自定义错误页面

# 定义自定义错误界面
@app.errorhandler(404)
def page_not_found(e):
    user = User.query.first()
    return render_template('404.html'), 404  # 这里是有两个返回值吗

模板的上下文处理函数

# 上下文处理函数
@app.context_processor
def inject_user():
    user = User.query.first()
    return dict(user=user)

模板继承

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

在这里插入图片描述



chapter 7 表单

表单的显现+表单的增删

其中最重要的是改。

显示

    user = User.query.first()  # 读取第一个用户信息 --》用于base.html
    movies = Movie.query.all()  # 读取所有电影
    return render_template('index.html', user=user, movies=movies)

删除

删除是最简单的操作。

<!-- html中增加一个删除按钮,传递变量movie.id -->
 <a class="btn" href="{{url_for('edit',  movie_id=movie.id)}}">修改</a>
# 查询删除
@app.route('/movie/delete/<int:movie_id>')
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'))  # 重定向回主页

增加

<!-- index.html中增加一个表格 -->   
<form method="post">
    Name <input type="text" name="title" autocomplete="on" required>
    Year <input type="text" name="year" autocomplete="on" required>
    <input class="btn" type="submit" name="submit" value="Add">
</form>
@app.route('/', methods=['GET', 'POST'])
@app.route('/index', methods=['GET', 'POST'])
def index():
    if request.method == "POST":
        title = request.form.get('title')
        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'))  # 重定向回主页,url改变,调用get方法
        # return render_template('index.html') #url不变,这时候渲染,没有获取到user,movie的值

    user = User.query.first()  # 读取第一个用户信息 --》用于base.html
    movies = Movie.query.all()  # 读取所有电影
    return render_template('index.html', user=user, movies=movies)

修改

修改是表格中一个难点。当时思考的时候,画了个图。懒得整理,就它吧。

在这里插入图片描述

在这里插入图片描述



chapter8 用户认证

使用 Flask-Login 实现用户认证

不知道对不对:login_user(user)将用户信息存入session —> load_user(user_id)根据用户信息的id,做相关的操作,最后将user对象赋给current_user这个全局对象 —> current_user.is_authenticated 很轻松判断用户是否认证 —》在html中根据是否认证**{% if current_user.is_authenticated %}**,给出不同的页面。

准备

login_manager = LoginManager(app) # 实例化扩展类

load_user返回的user对象赋给current_user这个全局对象。

继承UserMixin这个类会让 User 类拥有几个用于判断认证状态的属性和方法, 其中最常用的是 is_authenticated 属性: 如果当前用户已经登录, 那么current_user.is_authenticated 会返回 True , 否则返回 False 。 有了
current_user 变量和这几个验证方法和属性, 我们可以很轻松的判断当前用户的认证状态

from flask_login import LoginManager
login_manager = LoginManager(app) # 实例化扩展类

@login_manager.user_loader
def load_user(user_id): # 创建用户加载回调函数, 接受用户 ID 作为参数
	user = User.query.get(int(user_id)) # 用 ID 作为 User 模型的主键查询对应的用户
	return user # 返回用户对象

from flask_login import UserMixin
class User(db.Model, UserMixin):

登录

用户的用户名密码验证通过之后,我们可以使用login_user(user)来使用户登录。login_user()方法会帮助你操作用户Session,并且会在请求上下文中记录用户信息。

from flask_login import login_user

# 验证用户名和密码是否一致
if username == user.username and user.validate_password(assword):
    login_user(user) # 登入用户
    flash('Login success.')
	return redirect(url_for('index'))

登出

# 登出
@app.route('/logout')
def logout():
    logout_user()
    flash('Goodbye')
    return redirect(url_for('index'))

视图保护

@login_required # 登录保护
if not current_user.is_authenticated: # 如果当前用户未认证

模板保护

{% if current_user.is_authenticated %}
{% endif %}

在这里插入图片描述

在这里插入图片描述



chapter 9 测试

在此之前, 每次为程序添加了新功能, 我们都要手动在浏览器里访问程序进行测
试。 除了测试新添加的功能, 你还要确保旧的功能依然正常工作。 在功能复杂的大
型程序里, 如果每次修改代码或添加新功能后手动测试所有功能, 那会产生很大的
工作量。 另一方面, 手动测试并不可靠, 重复进行测试操作也很枯燥。
基于这些原因, 为程序编写自动化测试就变得非常重要

测试客户端+测试命令

app.test_client() 返回一个测试客户端对象, 可以用来模拟客户端( 浏览器) , 我们创建类属性 self.client 来保存它。 对它调用 get() 方法就相当于浏览器向服务器发送 GET 请求, 调用 post() 则相当于浏览器向服务器发送POST 请求, 以此类推。

除了测试程序的各个视图函数, 我们还需要测试自定义命令。 app.test_cli_runner() 方法返回一个命令运行器对象, 我们创建类属性self.runner 来保存它。 通过对它调用 invoke() 方法可以执行命令, 传入命令函数对象, 或是使用 args 关键字直接给出命令参数列表。 invoke() 方法返回的命令执行结果对象, 它的 output 属性返回命令的输出信息。

测试代码,我复制过来。

检测出3个错误。其他若干笔误。

在这里插入图片描述

在这里插入图片描述

如下所以,在html中,没有登录时候,不会有增加按钮等部分。

所以这行,没有必要测试。为了100%,我特地测试了这一行,这样覆盖率就100%,😄

在这里插入图片描述
在这里插入图片描述



chapter10 使用包组织代码

如果,代码再大些,将是函数再分。用蓝图。

在这里插入图片描述



chapter11 其他

小挑战

我简单的做了下,代码上传在“小挑战”目录中。

在这里插入图片描述



部署

参考:使用gunicorn部署flask项目

我的ip放在哪里,随便攻击。 服务器八月份到期。

pip3 install gunicorn
gunicorn app:app

在这里插入图片描述

附录

生成 GitHub README.md 目录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

da1234cao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值