Flask

1.浅谈FLask

“微”的含义

“微”并不代表整个应用只能塞在一个Python文件内,当然塞在单一文件内也没有问题。
“微”也不代表Flask功能不强。微框架中的“微”字表示Flask的目标是保持核心简单而又可扩展。
Flask不会替你做出许多决定,比如选用何种数据库。类似的决定,如使用何种模板引擎,是非常容易改变的。
Flask可以变成你任何想要的东西,一切恰到好处,由你做主。

FLask完整版 <= Django
# 本博客开始从web开发讲解FLask,先将前后端不分离的项目,后将vue +flask,后从运维开发讲解FLask从源码分析flask为什么这么牛逼                                                                                                                                                                                                                                                                 
# html,网络请看我其他博客                                                                                                                                                                                                                                                                                                                             

2.初识Flask

1.安装Flask

pip install flask

启动最小的hello, word

from flask import Flask

app = Flask(__name__)


@app.route('/')
def hello_world():
    return "<p>Hello, World!</p>"'


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

1.首先我们导入了Flask类。该类的实例将会成为我们的 WSGI 应用。
2.接着我们创建一个该类的实例。第一个参数是应用模块或者包的名称。 __name__是一个适用于大多数情况的快捷方式。有了这个参数, Flask
才能知道在哪里可以找到模板和静态文件等东西。
3.然后我们使用 route() 装饰器来告诉 Flask 触发函数 的 URL 。
4.函数返回需要在用户浏览器中显示的信息。默认的内容类型是HTML ,因此字符串中的HTML会被浏览器渲染。

2.路由

url_for() 函数用于构建指定函数的 URL。它把函数名称作为第一个 参数。它可以接受任意个关键字参数,每个关键字参数对应 URL 中的变量。未知变量 将添加到 URL 中作为查询参数。

from flask import Flask, url_for

app = Flask(__name__)

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

@app.route('/zz/<int:post_id>')
def hello2(name='zzz'):
    return f'Hello World!{name}'

@app.route('/test')
def test_url_for():
    # 下面是一些调用示例(请在命令行窗口查看输出的 URL):                                                                                                                                                                                                                                                                                                              
    print(url_for('hello1'))  # 输出:/                                                                                                                                                                                                                                                                                                            
    print(url_for('hello2',name='213'))  # /zz/123                                                                                                                                                                                                                                                                                             
    print(url_for('test_url_for',num=2))  # 输出:/test?num=2                                                                                                                                                                                                                                                                                     
    return 'Test page'

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

string (缺省值) 接受任何不包含斜杠的文本
int 接受正整数
float 接受正浮点数
path 类似 string ,但可以包含斜杠
uuid 接受UUID字符串

3.redict重定向

from flask import Flask, redirect

app = Flask(__name__)


@app.route('/hello/')
def hello():
    return redirect("/index")


    # return redirect(url_for('index')) 同上面的结果是一样的                                                                                                                                                                                                                                                                                               

@app.route("/index")
def index():
    return "123"


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

hello 尾部有一个斜杠,看起来就如同一个文件夹。访问一个没有斜杠结尾的
URL时,Flask会自动进行重定向,帮您在尾部加上一个斜杠

index 的URL没有尾部斜杠,因此其行为表现与一个文件类似。如果访问这
个URL时添加了尾部斜杠(就会得到一个404 “未找到” 错误。这样可以保持URL唯一,并有助于搜索引擎重复索引同一页面。

4.Jinja2

** 举例:**

< h1 > {{username}}的个人主页 < / h1 >
{ % if bio %}

{ % else %}

{ % endif %} {
# 大部分 Jinja 语句都需要声明关闭 #}                                                                                                                                                                                                                                                                                                       

Jinja2的语法和Python大致相同,你在后面会陆续接触到一些常见的用法。在模板里,你需要添加特定的定界符将
Jinja2语句和变量标记出来,下面是三种常用的

定界符:

{{...}}    用来标记变量。

{ % ... %} 用来标记语句,比如 if 语句,for 语句等。

{  # ... #} 用来写注释。                                                                                                                                                                                                                                                                                                                            

编写个人主页

模拟数据库数据 data ,创建后端程序

from flask import Flask, render_template

app = Flask(__name__)

name = "zbb"
data =[{'name': "zbb"},{'name': "zbb"},{'name': "zxy"},{'name': "zxy"},]

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


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

templates / index.html

< !DOCTYPE html >
< html lang = "en" >
< head > 
    < meta charset = "UTF-8" >
    < title > {{name}} 的主页 < / title >
< / head >
< body >
    < h2 > {{name}} 的主页 < / h2 >< p > {{datalength}}Titles < / p >
< ul >
    { % for z in data %} {  # 迭代 data 变量 #}                                                                                                                                                                                                                                                                                                        
        < li > {{z.name}}-{{z.age}} </li> {  # 迭代 data 变量 #}                                                                                                                                                                                                                                                                                          
    {% endfor %} {  # 使用 endfor 标签结束 for 语句 #}                                                                                                                                                                                                                                                                                                    
< / ul >
< / body >
< / html >

更多模板用法请看我的这篇博文

https://www.cnblogs.com/zdqc/p/11638144.html

前端知识请看

https://www.cnblogs.com/zdqc/category/1428916.html

5.static

存放静态文件的目录

我们在我们的网页中加入图片

<link rel="icon" href="{{ url_for('static', filename='123.png') }}">

6.数据库

Flask和Django 数据库上的操作的区别就是ORM

Django内置ORM,ORM是什么?

1.MVC或者MVC框架中包括一个重要的部分,就是ORM,它实现了数据模型与数据库的解耦,即数据模型的设计不需要依赖于特定的数据库,通过简单的配置就可以轻松更换数据库,这极大的减轻了开发人员的工作量,不需要面对因数据库变更而导致的无效劳动
2.ORM是“ ** 对象 - 关系 - 映射 **”的简称。
3.类对象 - -->sql - -->pymysql - -->mysql服务端 - -->磁盘,orm其实就是将类对象的语法翻译成sql语句的一个引擎

ORM特点

符合python思想,一切接对象,只会ORM就可以操作基本上所有的关系型数据库,如mysql, Postgres oracle等,
书写及其简单,但是要代码转换sql,性能较低,也很难写出高性能的sql语句

想学的ORM的同学可以看看我的这篇文章

https://www.cnblogs.com/zdqc/p/11656606.html
# flask也可以使用ORM                                                                                                                                                                                                                                                                                                                              
# pip install flask-sqlalchemy                                                                                                                                                                                                                                                                                                              

** 这里使用原生sql,封装pymysql **

pip install pymysql

pmysql.py

import pymysql


class MysqlC:


    def __init__(self, host, user, password, database):
        self.con = None


        self.arguments = {
            "host": host,
            "user": user,
            "password": password,
            "database": database,
            "charset": 'utf8'
            }


    def post(self, sql):
        with self:
            self.cursor = self.con.cursor(pymysql.cursors.DictCursor)


            try:
                data = self.cursor.execute(sql)
                self.con.commit()

                return data
            except Exception as e:
                self.con.rollback()
                return e


    def get(self, sql, one=None):
        with self:
            self.cursor = self.con.cursor(pymysql.cursors.DictCursor)


            try:
                self.cursor.execute(sql)
                if one:
                    return self.cursor.fetchone()
                else:
                    return self.cursor.fetchall()
            except Exception as e:
                  return e

    def __enter__(self):
        if self.con is None:
            try:
                self.con = pymysql.connect(**self.arguments)

            except Exception:
                return "数据库连接失败!"

            return self.con


    def __exit__(self, exc_type, exc_val, exc_tb):
        self.cursor.close()
        self.con.close()
        self.con = None

** app.py **

from flask import Flask, render_template
from mysql.pmysql import MysqlC

app = Flask(__name__)
pmysql = MysqlC("121.3", "root", "我好想你啊", "zzb")
name = "zbb"


@app.route('/')
def hello():
    data = pmysql.get("select name,age from t")


    # [{'name': 'zbb', 'age': 83}, {'name': 'zbb', 'age': 83}, {'name': 'zbb', 'age': 83}]                                                                                                                                                                                                                                                      
    print(data)
    return render_template('index.html', name=name, data=data)

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

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <link rel="icon" href="{{ url_for('static', filename='123.png') }}">
    <title>{{ name }}的主页</title>
</head>
<body>
    <h2>{{ name }}的主页</h2>
    <p>{{ data|length }} Titles</p>
    <ul>
        {% for z in data %} {# 迭代 data 变量 #}
        <li>{{ z.name }}-{{ z.age }} </li>{# 迭代 data 变量 #}
        {% endfor %} {# 使用 endfor 标签结束 for 语句 #}
    </ul>
</body>
</html>

目录结构

7.Form表单

创建一个login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<form action="" method="post" enctype="multipart/form-data">
    用户名:<input type="text" name="user">
    密码:<input type="password" name="pwd">
    <input type="submit" value="提交">
</form>

<script></script>
</body>
</html>

resquest可以拿到请求中的数据

from flask import Flask, render_template, request
import os

app = Flask(__name__)
app.debug = True

@app.route("/login", methods=["POST", "GET"])
def login():
    if request.method == "GET":
        # 获取URL中的参数,例如传入参数:http://127.0.0.1:5000/login?id=1
        print(request.args.get("id"))
        # 获取URL中的参数 转换成 字典
        print(request.args.to_dict())
        # 获取请求原始信息
        print(request.environ)
        # 路由地址 /login
        print(request.path)
        # 获取访问路径
        print(request.url)  # http://127.0.0.1:5000/login?id=1
        # 获取URL头,不包含参数 /login
        print(request.base_url)  # http://127.0.0.1:5000/login

        return render_template("login.html")

    if request.method == "POST":
        # 请求头中的数据
        print(request.headers)

        print(request.json)  # 请求头中 Content-type:application/json 数据序列化 request.json
        print(request.data)  # 请求头中 Content-type 不包含 Form or data

        # Formdata 和 Args查询参数 中的数据
        print(request.values)

        username = request.form.get("user")
        password = request.form.get("pwd")
        print(username, password)

        return "200 OK"

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

8.文件上传

用Flask处理文件上传很容易,只要确保不要忘记在您的HTML
表单中设置enctype = "multipart/form-data" 属性就可以了。否则浏览器将不会传送您的文件。

from flask import request

@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        f = request.files['the_file']
        f.save('/var/www/uploads/uploaded_file.txt')
    ...

如果想要知道文件上传之前其在客户端系统中的名称,可以使用filename 属性。但是请牢记这个值是 可以伪造的,
永远不要信任这个值。如果想要把客户端的文件名作为服务器上的文件名, 可以通过 Werkzeug 提供的 secure_filename()函数:

from werkzeug.utils import secure_filename

@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        file = request.files['the_file']
        file.save(f"/var/www/uploads/{secure_filename(file.filename)}")
    ...

8.Session

flask会将你的SessionID存放在客户端的Cookie中

基本用法,具体用法请看练习题

使用在app.py中引入

from flask import session
.......
app = Flask(__name__)
app.secret_key = "i am session"   #这里只需要随机指定一个字符串即可
.......


session["username"] = USER["username"] #设置session                                                                                                                                                                                                                                                                                    

验证session

session.get("username")   #获取session
session.pop('username')   #删除session
                                                                                                                                                                                                                                                                                                       

9.配置文件

from flask import Flask,Response,render_template

app = Flask(__name__,template_folder="templates",static_folder="static")
app.config.from_object("config.settings.DevelopmentConfig") 
@app.route('/login')
def login():
    return "ok"


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

config下的settings.py


class Config:
    DEBUG = False
    TESTING = False
    DATABASE_URI = 'sqlite://:memory:'
 
 
class ProductionConfig(Config):
    DATABASE_URI = 'mysql://user@localhost/foo'
 
 
class DevelopmentConfig(Config):
    DEBUG = True

调用

app.config.get("参数")

默认配置

{
    'DEBUG':                                False,  是否开启Debug模式
    'TESTING':                              False,                          是否开启测试模式
    'PROPAGATE_EXCEPTIONS':                 None,                         
    'PRESERVE_CONTEXT_ON_EXCEPTION':        None,
    'SECRET_KEY':                           None,
    'PERMANENT_SESSION_LIFETIME':           timedelta(days=31),
    'USE_X_SENDFILE':                       False,
    'LOGGER_NAME':                          None,
    'LOGGER_HANDLER_POLICY':               'always',
    'SERVER_NAME':                          None,
    'APPLICATION_ROOT':                     None,
    'SESSION_COOKIE_NAME':                  'session',
    'SESSION_COOKIE_DOMAIN':                None,
    'SESSION_COOKIE_PATH':                  None,
    'SESSION_COOKIE_HTTPONLY':              True,
    'SESSION_COOKIE_SECURE':                False,
    'SESSION_REFRESH_EACH_REQUEST':         True,
    'MAX_CONTENT_LENGTH':                   None,
    'SEND_FILE_MAX_AGE_DEFAULT':            timedelta(hours=12),
    'TRAP_BAD_REQUEST_ERRORS':              False,
    'TRAP_HTTP_EXCEPTIONS':                 False,
    'EXPLAIN_TEMPLATE_LOADING':             False,
    'PREFERRED_URL_SCHEME':                 'http',
    'JSON_AS_ASCII':                        True,
    'JSON_SORT_KEYS':                       True,
    'JSONIFY_PRETTYPRINT_REGULAR':          True,
    'JSONIFY_MIMETYPE':                     'application/json',
    'TEMPLATES_AUTO_RELOAD':                None,
}

10.练习

将今天所学全部用上,写一个用户注册,登录验证模块

创建login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <link rel="icon" href="{{ url_for('static', filename='123.png') }}">
</head>
<body>
{{ mes }}
<form action="" method="post" enctype="multipart/form-data">
    用户名:<input type="text" name="username">
    密码:<input type="password" name="pwd">
    <input type="submit" value="提交">
</form>
</body>
</html>

reg.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <link rel="icon" href="{{ url_for('static', filename='123.png') }}">
</head>
<body>
{{ mes }}
<form action="" method="post" enctype="multipart/form-data">
    用户名:<input type="text" name="username">
    密码:<input type="password" name="pwd">
    <input type="submit" value="注册">
</form>
</body>
</html>

app.py

from flask import Flask, redirect, url_for, session, request, render_template
from mysql.pmysql import MysqlC
from werkzeug.security import generate_password_hash, check_password_hash  #hash加密密码和解密密码

app = Flask(__name__)
app.secret_key = "qwerty"  # 设置密钥使用session
app.config.from_object("conf.settings.DevelopmentConfig")  # 获取配置文件中的mysql链接
mysql_con = app.config.get("MYSQL")
pmysql = MysqlC(**mysql_con)  # 调取封装好的pymysq


@app.route('/', methods=["get", "post"])
def login():
    # 获取用户提交的数据
    if request.method == "GET":
        return render_template("login.html")

    if request.method == "POST":
        username = request.form.get("username")
        pwd = request.form.get("pwd")
        if not username or not pwd:
            return render_template("login.html", mes="请输入用户名密码")
        password = pmysql.get(f"select pwd from user where username='{username}'", one=True)
        if password:
            if check_password_hash(password["pwd"], pwd):
                # 将用户名加到session中用于判断用户是否登录了
                session["username"] = username
                return redirect('/index')
            else:
                return render_template("login.html", mes="密码输入错误")
        else:
            return render_template("login.html", mes="用户名输入错误")


@app.route('/reg', methods=["get", "post"])
def reg():
    if request.method == "GET":
        return render_template("reg.html")

    if request.method == "POST":
        username = request.form.get("username")
        pwd = request.form.get("pwd")
        if not username or not pwd:
            return render_template("reg.html", mes="请输入用户名密码")
        #进行密码加密
        password = generate_password_hash(pwd)
        try:
            pmysql.post(f"insert user(username,pwd) values('{username}','{password}')")
            return redirect(url_for('login'))
        except Exception as e:
            return render_template("reg.html", mes="注册失败")


@app.route("/index")
def index():
    # 如果用户登录成功
    if session.get("username"):
        return "用户登陆成功!"
    # 未登录跳转到登陆页面
    else:
        # 未登录跳转到登陆页面
        return redirect('/')

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

封装好的pmysql.sql

import pymysql


class MysqlC:

    def __init__(self, host, user, password, database):
        self.con = None
        self.arguments = {
            "host": host,
            "user": user,
            "password": password,
            "database": database,
            "charset": 'utf8'
        }

    def post(self, sql):
        with self:
            self.cursor = self.con.cursor(pymysql.cursors.DictCursor)
            try:
                data = self.cursor.execute(sql)
                self.con.commit()

                return data
            except Exception as e:
                self.con.rollback()
                return e

    def get(self, sql, one=None):
        with self:
            self.cursor = self.con.cursor(pymysql.cursors.DictCursor)
            try:
                self.cursor.execute(sql)
                if one:
                    return self.cursor.fetchone()
                else:
                    return self.cursor.fetchall()
            except Exception as e:
                return e

    def __enter__(self):
        if self.con is None:
            try:
                self.con = pymysql.connect(**self.arguments)
            except Exception:
                return "数据库连接失败!"

            return self.con

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.cursor.close()
        self.con.close()
        self.con = None

settings.py



class DevelopmentConfig:
    MYSQL = {
        "host": "121.423",
        "user": "root",
        "password": "zbb521",
        "database": "zzb"
    }


目录结构

11用户认证

12组织代码

13蓝图

14自定义错误

import json
from flask import request
from werkzeug.exceptions import HTTPException
from config.error_code import *


class APIException(HTTPException):
    """
    为了使代码简洁, 首先定义一个最基本的类, 供其它类继承, 这个自定义的APIException继承HTTPException.
    1. 为了返回特定的body信息, 需要重写get_body;
    2. 为了指定返回类型, 需要重写get_headers.
    3. 为了接收自定义的参数, 重写了__init__;
    4. 同时定义了类变量作为几个默认参数.(code500和error_code:9999 均表示未知错误, error_code表示自定义异常code)
    """

    # 自定义需要返回的信息,在初始化完成并交给父类
    def __init__(self, error_code=9999, code=200):
        self.code = code
        self.error_code = error_code
        if error_code in error_codes:
            # errcode.py中可以自定义一些错误码{20000:"请求成功"}
            self.msg = error_codes[error_code]
        else:
            self.msg = error_codes[9999]
        super(APIException, self).__init__(self.msg, None)

    def get_body(self, environ=None):
        body = dict(
            error_code=self.error_code,
            message=self.msg,
            # request=request.method + ' ' + self.get_url_no_parm(),
        )
        # sort_keys 取消排序规则,ensure_ascii 中文显示
        text = json.dumps(body, sort_keys=False, ensure_ascii=False)
        return text

    def get_headers(self, environ=None):
        return [('Content-Type', 'application/json')]

    @staticmethod
    def get_url_no_parm():
        full_path = str(request.path)
        return full_path

返回自定义的错误

# 主动抛出错误
raise APIException(20005)

自定义的错误码

errcode.py

error_codes = {
    9999: "发生未知错误",
    10000: "token过期或失效",
    20000: "请求成功",
    20001: "连接单片机出错(COM口冲突)",
    20002: "最多只能操作80个灯",
    20003: "操作行为不存在",
    20004: "用户id不能为空或者负数",
    20005: "未能获取到单片机的数据",
    20006: "数据不能为空",
    20007: "血压检测失败,请重新检测",
}

项目启动文件

# 异常错误判断
@app.errorhandler(Exception)
def handle_error(e):
    # 判断异常是不是APIException
    if isinstance(e, APIException):
        return e
    else:
        logger.error("{}".format(traceback.format_exc()))
        return APIException()
posted @ 2021-10-26 11:59  河图s  阅读(199)  评论(0)    收藏  举报