Flask 基础(一)

408 阅读11分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第24天,点击查看活动详情

Flask轻量级框架

Tornado 高并发处理框架

Django 企业级重量框架

Pycharm创建项目

快捷键

www.cnblogs.com/yeminglong/…

写代码,按Tab生成代码
Ctrl+/ 或 Ctrl+Shift+/注释(// 或者// )
Ctrl+Alt+L格式化代码
Ctrl+?生成注释
Alt加单击多选共同编辑

image-20201222210437280

1.自动生成虚拟环境(虚拟环境自动放到文件目录里)

2.使用提前生成的virtualenv,需要提前进到对应目录(推荐)

Windows terminal使用cmd不是powershell

blog.csdn.net/sinat_42194…

  1. win安装虚拟环境

    pip3 install virtualenvwrapper-win

  2. pip3 list检查是否安装成功

  3. 切换到对应目录,mkvirtualenv创建虚拟环境

    mkvirtualenv -p C:\Users\q2723\AppData\Local\Programs\Python\Python37-32\python3.exe flask

    workon

    image-20201223092414146

    使用mkvirtualenv命令来创建虚拟环境,环境目录路径为系统默认路径,C:\Users\q2723\Envs

  4. workon name 进入虚拟环境

  5. deactivate 退出虚拟环境

  6. 删除虚拟环境

    rmvirtualenv

pycharm使用已创建虚拟环境

image-20201223094717713

image-20201222211823006

有这些的原因:当初创建项目的时候已经指定了flask,所以pycharm在创建项目的时候自动执行了pip install flask

Flask结构

项目名:

​ static 静态 js、css

​ templates 模板

​ app.py 运行|启动

MVC:

​ model

​ view

​ controler

MTV:

​ model 与数据库交互

​ template 显示页面

​ view 起控制作用

B/S

browser/sever

C/S

client/sever

虚拟环境

win和linux虚拟环境区别

win:

​ 装完virtualenvwrapper可以直接使用,默认位置是c://user/q2723/Envs/

linux:

​ 装完需要配置~/.bashrc和~/.bash_profile文件

virtualenv命令

  • mkvirtualenv 虚拟环境名称

    创建

  • cdvirtualenv

    切换到当前虚拟环境目录下

  • lsvirtualenv

    显示所有虚拟环境==workon

  • rmvirtualenv 虚拟环境名称

    删除虚拟环境

WSGI

__name__表示当前文件名称

__init__创建对象时默认调用部分

WSGI

web服务器网管接口(python web sever gateway interface)

为python语言定义的web服务器和web应用程序或框架之间的一种简单通用的接口

image-20210109144022012

app.py

from flask import Flask

app = Flask(__name__)

@app.route('/')					#路由,必须以 / 开始否则报错
def hello_world():				#视图函数
    return 'Hello World!'		


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8080, debug=True)

Flask 默认拥有内置服务器

看 python 源码,ctrl 加单击

host 默认127.0.0.1只能本机访问,允许外网访问则需改成0.0.0.0

Debug 模式:开启后更改源码实时更新,适用于开发环境

app.config

<Config {'ENV': 'production', 'DEBUG': False, 'TESTING': False, 'PROPAGATE_EXCEPTIONS': None, 'PRESERVE_CONTEXT_ON_EXCEPTION': None, 'SECRET_KEY': None, 'PERMANENT_S
ESSION_LIFETIME': datetime.timedelta(days=31), 'USE_X_SENDFILE': False, 'SERVER_NAME': None, 'APPLICATION_ROOT': '/', 'SESSION_COOKIE_NAME': 'session', 'SESSION_COOK
IE_DOMAIN': None, 'SESSION_COOKIE_PATH': None, 'SESSION_COOKIE_HTTPONLY': True, 'SESSION_COOKIE_SECURE': False, 'SESSION_COOKIE_SAMESITE': None, 'SESSION_REFRESH_EAC
H_REQUEST': True, 'MAX_CONTENT_LENGTH': None, 'SEND_FILE_MAX_AGE_DEFAULT': datetime.timedelta(seconds=43200), 'TRAP_BAD_REQUEST_ERRORS': None, 'TRAP_HTTP_EXCEPTIONS'
: False, 'EXPLAIN_TEMPLATE_LOADING': False, 'PREFERRED_URL_SCHEME': 'http', 'JSON_AS_ASCII': True, 'JSON_SORT_KEYS': True, 'JSONIFY_PRETTYPRINT_REGULAR': False, 'JSO
NIFY_MIMETYPE': 'application/json', 'TEMPLATES_AUTO_RELOAD': None, 'MAX_COOKIE_SIZE': 4093}>

修改环境

app.py

app.config['ENV']='development'
app.config['DEBUG']=True

或将配置文件独立为

settings.py

ENC = 'development'
DEBUG = True

在此文件中port端口设置无效

在app.py导入settings

from flask import Flask
app = Flask(__name__) 

import settings

app.config.from_object(settings)
或者app.config.from_pyfile('settings.py')

@app.route('/')					#路由
def hello_world():				#视图函数
    return 'Hello World!'		

if __name__ == '__main__':
    app.run(port=8080)
    #端口在这改有效

@app.route()

源码:
def route(self, rule, **options):
def decorator(f):
            endpoint = options.pop("endpoint", None)
            self.add_url_rule(rule, endpoint, f, **options)
            return f
	return decorator


@app.route('/')
def index():
    return 'Index page'
就等效于
def index():
    return 'hello'
app.add_url_rule('/index',view_func=index)

路由变量规则:

from flask import Flask

app = Flask(__name__)

data = {'a': 'beijing', 'b': 'shanghai'}


@app.route('/')
def hello_world():
    return 'Hello World!'


@app.route('/getcity/<key>')  #key是一个变量名,默认是字符串类型数据
def get_city(key):
    print(type(key))
    return data.get(key)


@app.route(/getnum/<int:num>)
def calc(num)    #num和上行num保持一致
	result = num + 10
    return str(result)
#注意:上一行 return 返回值不能是 int 型,必须是 srting,dict,tuple,response instance,wsgi callable

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8080, debug=True)

app.route('*')

允许转换器的类型:

string(默认)接受任何不包含斜杠的文本
int接受正整数
float接受正浮点数
path类似于string,但是可以包含斜杠
uuid(通用唯一识别码),接受uuid字符串

python用于uuid的库

uuid有固定格式

特殊:

@app.route('/about')   #没有斜杠,行为表现类似于一个文件,访问加斜杠就会出现404错误,这样可以保证url唯一,避免重复索引同一界面
def hello_world():
    return 'Hello World!'

@app.route('/about/')   #尾部有/,行为表现类似于一个文件夹,访问没有斜杠结尾的url也能成功flask会自动重定向,帮你在结尾加上一个斜杠
def hello_world():
    return 'Hello World!'

路由自上而下加载

返回值类型

返回值不能是int型,必须是srting,dict,tuple,response instance,wsgi callable

data = {'a': 'beijing', 'b': 'shanghai'}


@app.route('/string')
def string():
    return '<h1>beijing</h1>'    
#content-Type:text/html;charset=utf-8
#return后面返回的字符串其实也是做了一个response对象的数据,最终返回结果还是response对象,会被当作HTML解析


@app.route('/dict')
def dict():
    return {'a': 'beijing', 'b': 'shanghai'}    
#content-Type:application/json;charset=utf-8


def tup():
    return 'aaa',200
a=tup()
print(a)
#输出信息为元组('aaa', 200)

@app.route('/tuple')
def tuple():
    return 'hello world',200 


@app.route('/response instance')
def response instance():
    return Response('<h1>beijing</h1>')
#content-Type:text/html;charset=utf-8
response对象

响应response

导入make_response

from flask import Flask,make_response

app = Flask(__name__)

@app.route('/index')
def index():
    content='''
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
<div>
    <h1>wolcome</h1>
    <ul>
        <li>
            hello
        </li>
    </ul>
</div>
</body>
</html>
    '''
    response = make_response(content) #返回值就是一个response对象
    response.headers['myheaders'] = '123abc'
    #定制响应头
    print(response.content_type)
    print(response.status)
    print(response.status_code)
    return response

if __name__ == '__main__':
    app.run(port='8080',debug=True)

image-20210110142702705

请求request

导入request

from flask import Flask, request
import settings

app = Flask(__name__)
app.config.from_object(settings)

@app.route('/index')
def index():
    print(request.headers)
    print(request.path)

    return 'ok'

if __name__ == '__main__':
    app.run()

request对象,对象访问属性,也可以调用方法

路由规则表

if __name__ == '__main__':
    print(app.url_map)
    app.run()

Map([<Rule '/index' (HEAD, OPTIONS, GET) -> index>,
 <Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>])
#以列表形式返回

app.py与模板的结合使用

from flask import Flask, request, render_template
import settings

app = Flask(__name__)
app.config.from_pyfile('settings.py')

@app.route('/register')
def register():
    r = render_template('register.html')    
    #默认去模板文件夹中找文件,通过jinja引擎转化为字符串即response对象
    return r

if __name__ == '__main__':
    print(app.url_map)
    app.run()

register.html要放在templates目录下

image-20210111091707583

为啥会默认在templates下

因为创建app时默认的

   app=Flask(__name__)
   def __init__(
        self,
        import_name,
        static_url_path=None,
        static_folder="static",
        static_host=None,
        host_matching=False,
        subdomain_matching=False,
        template_folder="templates",
        instance_path=None,
        instance_relative_config=False,
        root_path=None,
    ):

浏览器清空缓存

f12加右击刷新

image-20210111093036832

或:ctrl+shift+del

ctrl+?添加注释

get请求

register.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>注册</title>
    <style>
        div{
            height: 200px;
            width: 100%;
            border: 2px solid blue;
        }
    </style>
</head>
<body>
<div>
    <form action="register1" method="get">
        <p>
            <input type="text" name="username" placeholder="username">
        </p>
        <p>
             <input type="text" name="address" placeholder="address">
        </p>
        <p>
            <input type="submit" value="提交">
        </p>
    </form>
</div>
</body>
</html>

app.py

from flask import Flask, request, render_template
import settings

app = Flask(__name__)
app.config.from_pyfile('settings.py')

@app.route('/register')
def register():
    r = render_template('register.html')
    return r

@app.route('/register1')
def register1():
    print(request.full_path)
    #/register1?username=2019101763&address=aaa
    print(request.path)
    #/register1
    print(request.args)
    #ImmutableMultiDict([('username', '2019101763'), ('address', 'aaa')])  封装字典类型
    print(request.args.get('username'))
    #2019101763
    return 'ok'

if __name__ == '__main__':
    print(app.url_map)
    app.run()
    
    
输出
/register1?username=2019101763&address=aaa
/register1
ImmutableMultiDict([('username', '2019101763'), ('address', 'aaa')])
2019101763

get是在url中传参image-20210111102404119

post请求

register.html

<form action="register1" method="post">

app.py

@app.route('/register1',methods=['GET','POST'])
#post接收参数一定要添加methods
def register1():
    print(request.full_path)
    #/register1?
    print(request.path)
    #/register1
    print(request.args)
    #ImmutableMultiDict([])
    print(request.args.get('username'))
	#None

    print(request.form)
    #ImmutableMultiDict([('username', '2019101763'), ('address', 'aaa')])
    print(request.form.get('username'))
    #2019101763
    return 'ok'

此时request.args.get就取不到参数了

image-20210111103301469

因为url中没有内容,信息在请求主体内

路由规则表也会更新

Map([<Rule '/register1' (POST, HEAD, GET, OPTIONS) -> register1>,
 <Rule '/register' (HEAD, GET, OPTIONS) -> register>,
 <Rule '/static/<filename>' (HEAD, GET, OPTIONS) -> static>])

两个参数

错误写法

@app.route('/add/<int:a>/<int:b>')
def add(a,b):
    if a>0 and b>0:
        r=a+b
        return str(r)

image-20210111105916353

视图函数必须有返回值,否则报错

image-20210111105837186

正确

@app.route('/add/<int:a>/<int:b>')
def add(a,b):
    if a>0 and b>0:
        r=a+b
        return str(r)
    return 'number wrong'

request.method

重定向

redirect

源码就是在响应头里添加location头

response.headers['Location']=location addres

url_for

url=url_for('name') 路径反向解析

根据名找路径

路由加endpoint app.route('/',endpoint='index') 指定名字

from flask import Flask, url_for, redirect
import settings

app = Flask(__name__)
app.config.from_object('settings')


@app.route('/show', endpoint='index')  
#相当于给路由 /show ,起了一个别名叫 index
def show():
    name = 'ocean'
    gender = 'male'
    return name


@app.route('/redir')
def redir():
    return redirect(url_for('index'))
#使用 url_for 去反向解析别名 index 
#要注意的是 函数名 和redirect() 不能重名
#错误示范
#@app.route("/redirect")
#def redirect():
#    return redirect("https://www.baidu.com")

if __name__ == '__main__':
    app.run()

视图函数返回值:

response响应:

  1. str
  2. dict
  3. response对象
  4. make_response()
  5. redirect()
  6. reder_templates

模板(网页)

Jinja2 作为flask的默认模板引擎

在 HTML 中动态创建

模板语法

在flask中,jinja2默认配置如下:

  • 当使用render_template()时,扩展名为.html , .htm , .xml , .xhtml 的模板中开启自动转义
  • 当使用 render_template_string() 时,字符串开启自动转义
  • 在模板中可以使用 {%autoescape%} 来手动设置是否转义
  • Flask 在 Jinja2 环境中加入了一些全局函数和辅助对象,以增强模板的功能

简单使用{{}}

show.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>show</title>
</head>
<body>
<p>
    姓名:{{ name }}
    </hr>
    性别:{{ gender }}
</p>
</body>
</html>

app.py

from flask import Flask, request, render_template, url_for, redirect
import settings

app = Flask(__name__)
app.config.from_object('settings')

@app.route('/show', endpoint='index')
def show():
    name = 'ocean'
    gender = 'male'
    return render_template('show.html', name=name, gender='ooooo')
#前边的name是变量名,后边name是变量值,只是在上方提前赋值而已

if __name__ == '__main__':
    app.run()

image-20210111163717390

模板的语法:

  • 在模板中获取 view 中的传递的变量值:{{ 变量名key }}

    render_template( ' 模板文件名字 ' , key = value , key = value )

    在模板文件中:

    {{ list.0 }} 同 {{ list[0] }}

    {{ dict.key }} = {{ dict.get(key) }}

    {{ class.name }} = {{ 对象.属性 }}

    字符串,列表,元组,字典,对象等都可

  • 注释块

    {# #} 服务器可识别,浏览器不可识别

  • 控制块

    {% if(python语句) %}

for循环块

{% for girl in girls %}

{% endfor %}
#成对出现,搭配使用

例:show.html

<!DOCTYPE html>
<head>
    <meta charset="UTF-8">
    <title>show</title>
    <style>
        .a{
            font-weight: bolder;
            color: red;
        }      
    </style>
           <!--类选择器-->
</head>
<p>
{% for friend in friends %}
    {% if friend | length>6 %}
    <!--使用管道符而不是点-->
        <li class="a">
        {{ friend }}
        </li>
    {% else %}
        <li>
        {{ friend }}
        </li>
    {% endif %}
{% endfor %}
<!--成对出现,搭配使用-->
</p>

</body>
</html>

app.py

from flask import Flask, request, render_template, url_for, redirect
import settings

app = Flask(__name__)
app.config.from_object('settings')


@app.route('/show', endpoint='index')
def show():
    friends = ['xioaming', 'xiaohua', 'xiaoli']
    return render_template('show.html', friends=friends)


if __name__ == '__main__':
    app.run()

image-20210111185546273

loop变量

  • loop 循环赋值计数器
  • loop.index 序号从1开始
  • loop.index0 序号从0开始
  • loop.revindex 倒序
  • loop.revindex0
  • loop.first 判断是不是第一个 返回布尔值 真为 True 假为 False
  • loop.last 判断是不是最后一个 返回布尔值

例:如果是第一行就添加样式,否则没有样式

<!DOCTYPE html>
<head>
    <meta charset="UTF-8">
    <title>show</title>
    <style>
        .a{
            font-weight: bolder;
            color: blue;
        }
    </style>
</head>
<p>

{% for friend in friends %}
    {% if friend|length>6 %}
        <li {% if loop.first %} class="a" {% endif %} >
        #可以将jinja语句与html混编
        {{ friend }}
        {{ loop.index }}
        {{ loop.first }}
        </li>
    {% else %}
        <li>
        {{ friend }}
            {{ loop.index }}
        {{ loop.last }}
        </li>
    {% endif %}
{% endfor %}

</p>
</body>
</html>

image-20210111194440675

过滤器 |

Filters 过滤器的本质就是函数

语法: {{ 变量名 | 过滤器 (*args) }}

常见过滤器:

字符串过滤器

  • safe :

    如果渲染文本只有html标签jinja会自动进行实体转译,就自动防xss

    @app.route('/show', endpoint='index')
    def show():
        friends = ['xioaming', 'xiaohua', 'xiaoli']
        msg = '<h1>badapple</h1>'
        return render_template('show.html', friends=friends,msg=msg)
    
    <p>
        当前共有{{ friends|length }}人
        <br>
        {{ msg }}
    </p>
    

    image-20210111200740995

    image-20210111200919750

    加了safe就不会被转译,出现对应效果

  • capitalize

    单词首字母大写

  • lower ,upper

    转换大小写

  • title

    一句话中每个单词首字符大写

  • reverse

    反转

  • format

{{'my name  is %s ,i am %d years old '| format(friends.0 |title,17)}}
my name is Xioaming ,i am 17 years old
  • truncate()

    字符串按长度截断

列表过滤器

  • first ,last
  • length
  • sum ,sort 整型的计算和排序

字典

  • 可以使用 .items , .keys , .values

自定义过滤器

  • 通过 flask 模块中的 app.add_template_filter 方法

    定义函数,带有参数和返回值

    添加过滤器 app.add_template_filter(function,name=' ')

    在模板中使用 {{ 变量 | 自定义过滤器 }}

  • 使用装饰器完成

    定义函数,带有参数和返回值

    通过装饰器完成,@app.template_filter(' 过滤器名字 ') 装饰步骤1的函数

    在模板中使用 {{ 变量 | 自定义过滤器 }}

就像 app.route() 一样有两种方法

模板继承

需要模板继承的情况

  • 多个模板具有完全相同的顶部和底部
  • 多个模板具有相同的模板内容,但是内容中部分不一样
{% block name %}

{% endblock %}

有变化的地方挖坑

子模版继承父模板

类继承

class Person:
    def run(self):
        print('run...')
        
class Student(Person):
    pass

s=Student()
s.run()

模板继承与类继承略有不同

父模板最好预留css样式,js脚本的位置

例:index.html是子模版,base.html是父模板

<!--子模版index.html-->
{% extends 'base.html' %}
<!--继承父模板base-->

{% block title %}
首页
{% endblock %}
<!--对应父模板里的坑-->

{% block css %}
<style>
#head{
    background-color: cadetblue;
}
</style>
{% endblock %}
<!--css-->

{% block main %}
<div>
    <button id="btn">main-part</button>
    <!--在子里面填写会替换父里边-->
</div>
{% endblock %}
<!--页面替换内容部分-->

{% block js %}
<script>
    btn=document.getElementById('btn')
    btn.onclick=function (){
        alert('dont touch me!')
    }
</script>
<!--js脚本部分-->
{% endblock %}
<!--父模板base.html-->
<!DOCTYPE html>
<head>
    <meta charset="UTF-8">
    <title>
        {% block title %} title {% endblock %}
    </title>
    <style>
        #head{
            width: 100%;
            height: 50px;
            background-color: royalblue;
        }
        #head ul{
            list-style: none;
            height: 50px;
        }
        #head ul li{
            width: 25%;
            float: left;
            text-align: center;
            font-size: medium;
            line-height: 50px;
        }
        #foot{
            width:100%;
            height: 50px;
            background-color: aquamarine;
        }
        #main{
            width:100%;
            height: 500px;
            background-color: cadetblue;
        }
    </style>
        {% block css %}

        {% endblock %}
</head>

<body>
    <div id="head">
        <ul>
            <li>index</li>
            <li>first</li>
            <li>third</li>
            <li>second</li>
        </ul>

    </div>

    <div id="main">
        {% block main %}

        {% endblock%}
    </div>

    <div id="foot">
        <il>1</il>
        <il>2</il>
        <il>3</il>
        <il>4</il>
    </div>
    {% block js %}

    {% endblock %}

</body>
</html>
#app.py
@app.route('/index')
def index():
    return render_template('index.html')

image-20210112113536665

父模板:

  • 定义一个 base.html 的模板
  • 分析模板中那些是变化的 比如:{% block name%} 父模板 {% endblock %}

子模版

  • {% extends '父模板的名称' %} 将父模板继承过来
  • 找到对应 block 填充,每一个 block 都是有名字的

url_for加载静态文件

使用 url_for

if __name__ == '__main__':
    print(app.url_map)
    app.run()

Map([<Rule '/static/<filename>' (OPTIONS, HEAD, GET) -> static>])
#在路由里面默认就有 /static/<filename> 这条路由

加载静态文件,使用 url_for

例,加载 /static/images/1.jpg

......
{% block main %}
<div>
    <button id="btn">main-part</button>
    <img src="{{ url_for('static',filename='/images/1.jpg') }}" alt="">
</div>
{% endblock %}
......

image-20210112134142844

例,加载样式文件

#/static/css/style.css 
#head{
            width: 100%;
            height: 50px;
            background-color: #ff0023;
        }
<!--index.html-->
{% block css %}
    <link rel="stylesheet"
          ref="{{ url_for('static',filename='css/style.css') }}">
{% endblock %}

image-20210112135219627

include

一个模板包含另一个模板

在A,B,C页面都共同有的部分,但其它页面没有这部分

这个时候就可以使用 include

步骤:

  • 先定义一个公共的模板部分,如 xxx.html
  • 谁使用则 include 过来 ,{% include '文件夹/xxx.html' %}

include 会包含整个 xxx.html

{% include 'basic.html' %}

宏macro

macro 可以看作是 jinja2 的一个函数,这个函数可以返回一个 HTML 字符串

目的:代码可以复用,避免代码冗余

定义两种方式:

  1. 在模板中直接定义
  2. 将所有宏提取到一个模板中:marco.html
{% import 'macro.html' as f %}
{{ f.form('/welcome',value='注册')}}

定义宏方法1 单个文件中使用宏

macro1.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>user_macro</title>
</head>
<body>
{% macro form(action,value='login',method='post') %}
    <form action={{ action }} method={{ method }}>
        <p>
            <input type="text" name="username" placeholder="username">
        </p>
        <p>
             <input type="text" name="address" placeholder="address">
        </p>
        <p>
            <input type="submit" value={{ value }}>
        </p>
    </form>
{% endmacro %}

调用
{{ form('/','login')}}
{#定义调用方法与函数类似,默认参数,可选参数 }
</body>
</html>

app.py

@app.route('macro')
def def_macro():
    return render_template('macro/macro.html')
#调用文件时 render_template会默认以templates文件夹下开始

image-20210112195757242

定义宏方法2:多个宏,将宏存放在单独文件macro.html

macro.html

{% macro form(action,value='login',method='post') %}
    <form action={{ action }} method={{ method }}>
        <p>
            <input type="text" name="username" placeholder="username">
        </p>
        <p>
             <input type="text" name="address" placeholder="address">
        </p>
        <p>
            <input type="submit" value={{ value }}>
        </p>
    </form>
{% endmacro %}
{#可以写多个宏#}
<!--调用宏-->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>user_macro</title>
</head>
<body>
{% import 'macro/macro.html' as f %}
<!--先import宏,注意文件路径以templates文件夹开始,as f相当于起别名-->
{{ f.form(action='/',value='logout') }}
{{ f.form('/','logout') }}
    这两种都可以
<!--使用宏,使用macro.html文件中的名为form的宏-->
<!--注意:导入宏使用控制块{% %},使用宏用变量块{{ }}-->
</body>
</html>

app.py

@app.route('/macro')
def def_macro():
    return render_template('macro/macro1.html')