初识Flask

初识Flask

Flask简介

Flask是一个基于Python的web框架,由奥地利开发商Armin Ronacher于2010年4月1日发布。它以灵活、“微”框架著称,其本身并不具备太多的功能,但通过丰富的第三方插件,使其在保持重量轻和简单的同时仍然可以进行高度扩展,让开发者能轻松应对现实开发中复杂的需求。

Flask vs Django

  • Django功能大而全,Flask只包含基本的配置;
  • Flask 比 Django 扩展性更好。

安装Flask

打开cmd窗口,执行如下指令即可安装。

pip install flask -i https://pypi.tuna.tsinghua.edu.cn/simple

执行如下指令查看是否安装成功。

pip list

第一个Flask项目(使用Pycharm)

打开PyCharm,选择New Project新建一个项目,之后点击左侧Flask,再选择项目存放路径以及Python解释器路径,点击Create即可。

01

创建成功后,项目结构如下。

flask_01
│ app.py:项目入口文件。
├─static:存放静态文件(js、css、image等)的文件夹,初始为空。
└─templates:存Jinjia2模板的文件夹,初始为空。

02

app.py初始内容如下。

from flask import Flask  # 从flask包中导入Flask类

app = Flask(__name__)  # 实例化Flask类
# __name__:代表当前app.py这个模块,其作用为 1.以后出现bug,可以帮助我们快速定位;2.对于寻找模板文件,有一个相对路径


@app.route('/')  # 创建一个路由和视图函数的映射,/代表根路由
def hello_world():  # 当访问根路由时会执行此函数
    return 'Hello World!'


if __name__ == '__main__':
    app.run()  # 运行项目

项目配置

debug模式

开启debug模式后,只要修改代码后保存,项目就会重新加载,无需手动重启项目,且开发时如果遇到bug,可以在浏览器上看到出错信息。

设置方法

法一

首先点击Edit Configurations。

1675092550816

勾选FLASK_DEBUG后,点击OK即可。

04

法二

在app.run() 中添加debug=True。

app.run(debug=True)  # 运行项目

修改host

host即为监听的主机名,默认为127.0.0.1,修改host可以让其它电脑也能访问到本电脑的flask项目,修改步骤如下。

点击Edit Configurations。

03

在Additional options处填写预设的host值,然后点击OK即可。

05

修改port

port即为端口号,若要修改,同样在Additional options处修改即可。

06

URL与视图

URL格式

协议://域名:端口号/path,其中,path通过app.route()指定。

URL与视图的关系

在URL中,http协议默认端口为80,https协议默认端口为443,在实际使用中默认端口号可以不填写,下方两个URL均能正确访问到百度地址。

https://www.baidu.com

https://www.baidu.com:443

定义无参数的URL

敲写下方代码,程序运行后,可通过 127.0.0.1:8888/home 访问。

@app.route('/home')
def home():
    return 'home'

定义有参数的URL

flask里通过<>来给path指定参数。

参数类型

  • string:字符串类型,可以接受除/以外的字符。

  • int:整型,可以接受能通过int()方法转换的字符。

  • float:浮点型,可以接受能通过float()方法转换的字符。

  • path:路径,类似string,但是中间可以添加/。

  • uuid:UUID类型,UUID是一组32位数的16进制所构成。

  • any:备选值中的任何一个(理解为枚举)。

类型一

敲写下方代码,程序运行后,可通过 127.0.0.1:8888/home/任意数字 访问。

@app.route('/home/<int:user_id>')  # 类型的指定是可选的
def blog_detail(user_id):
    return '您是:%s' % user_id

07

类型二

敲写下方代码,程序运行后,可通过 127.0.0.1:8888/book/list?page=任意数字 访问。

from flask import Flask, request  # 从flask包中导入Flask类

# /book/list:返回第一页的数据
# /book/list?page=2:返回第二页的数据

@app.route('/book/list')
def book_list():
    # arguments: 参数
    # request.args: 类字典类型
    page = request.args.get("page", default=1, type=int)
    return f'您获取的是第{page}页的图书列表'

08

Jinjia2模板

Jinjia2是一个Python模板语言,安装Flask时,Jinjia2会自动安装,Jinjia2模板语言是不分缩进的。

模板渲染

无参数渲染

在templates创建文件index.html。

index.html内容如下。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
    <h1>这是首页。</h1>
</body>
</html>

app.py内容如下。

from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def hello_world():
    return render_template('index.html')


if __name__ == '__main__':
    app.run()  # 运行项目

有参数渲染

在templates创建文件user_detail.html。

user_detail.html内容如下,Jinjia2通过{}来获取变量的值。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户详情</title>
</head>
<body>
    <h1>用户id是{{ user_id }}</h1>
</body>
</html>

app.py内容如下。

from flask import Flask, render_template

app = Flask(__name__)

@app.route('/home/<user_id>')
def user_detail(user_id):
    return render_template('user_detail.html', user_id=user_id)


if __name__ == '__main__':
    app.run()  # 运行项目

模板访问对象属性

Jinjia2访问对象属性有两种方法,例如{{ user.user_name }}和{{ user['user_name'] }}。

index.html内容如下。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
<h1>{{ user.user_name }} + {{ user.user_email }}</h1>
</body>
</html>

app.py内容如下。

from flask import Flask, render_template

app = Flask(__name__)

class User:
    def __init__(self, user_name, user_email):
        self.user_name = user_name
        self.user_email = user_email

@app.route('/')
def hello_world():
    user = User("lili", '123456@qq.com')
    return render_template('index.html', user=user)


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

过滤器

在Python中,如果需要对某个变量进行处理,可以通过函数来实现;在模板中,则是通过过滤器来实现的。过滤器本质上也是函数,在模板中使用管道符号(|)来调用。例如有字符串类型变量name,想要获取它的长度,可以通过{{name | length}}来获取,length是Jinjia2内置的过滤器,Jinjia2会把name当做第一个参数传给length过滤器底层对应的函数。

内置过滤器

filter.html内容如下。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>filter_demo</title>
</head>
<body>
    名字:{{ user.user_name }}
    名字长度:{{ user.user_name|length }}
</body>
</html>

app.py内容如下。

from flask import Flask, render_template

app = Flask(__name__)

class User:
    def __init__(self, user_name, user_email):
        self.user_name = user_name
        self.user_email = user_email

@app.route('/filter')
def filte_page():
    user = User('lili', '123@qq.com')
    return render_template('filter.html', user=user)

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

自定义过滤器

过滤器本质上是Python的函数,它会把被过滤的值当做第一个参数传给这个函数,函数经过一些逻辑处理后,再返回新的值。在过滤器函数写好后,可以通过@app.template_filter装饰器或者app.add_template_filter函数来把函数注册成Jinjia2能用的过滤器。

下方代码定义了一个date_format函数,第一个参数是需要被处理的值,第二个参数是时间的格式,并且指定了一个默认值,通过app.add_template_filter,将date_format函数注册成了过滤器,并且过滤器的名字为d_format,如果app.add_template_filter没有传第二个参数,那么默认将使用函数的名称来作为过滤器的名称。

filter.html内容如下。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>filter_demo</title>
</head>
<body>
    时间:{{ my_time|d_format }}
</body>
</html>

app.py内容如下。

from flask import Flask, render_template
from datetime import datetime
app = Flask(__name__)

@app.route('/filter')
def filte_page():
    my_time = datetime.now()
    return render_template('filter.html', my_time=my_time)


def date_format(value, format="%Y-%m-%d %H:%M"):
    return value.strftime(format)


app.add_template_filter(date_format, 'd_format')

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

控制语句

if语句

filter.html内容如下。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>filter_demo</title>
</head>
<body>
    {% if age > 18 %}
        <div>您已满18岁</div>
    {% elif age == 18 %}
        <div>您刚满18岁</div>
    {% else %}
        <div>您未满18岁</div>
    {% endif %}
</body>
</html>

app.py内容如下。

from flask import Flask, render_template

app = Flask(__name__)

@app.route('/filter')
def filte_page():
    age = 17
    return render_template('filter.html', age=age)


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

for循环

Jinjia2中的for循环没有break语句。

filter.html内容如下。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>filter_demo</title>
</head>
<body>
    {% for student in students %}
        <div>学生姓名:{{ student.name }},学生年龄:{{ student.age }}</div>
    {% endfor %}
</body>
</html>

app.py内容如下。

from flask import Flask, render_template

app = Flask(__name__)

@app.route('/filter')
def filte_page():
    students = [
        {'name':'lili', 'age':18},
        {'name':'lucy', 'age':17},
        {'name':'lfiv', 'age':19}
    ]
    return render_template('filter.html', students=students)


if __name__ == '__main__':
    app.run()  # 运行项目

模板继承

一个网站中,大部分网页的模块是重复的,比如顶部的导航栏、底部的备案信息等,如果在每个页面中都重复地去写这些代码,会让项目变得臃肿,提高后期维护成本。此时,可以通过模板继承,把一些重复性的代码写在父模板里,子模板继承父模板后,再分别实现自己的代码。

父模板文件base.html内容如下。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}{% endblock %}</title>
</head>
<body>
<ul>
    <li><a href="#">首页</a></li>
    <li><a href="#">新闻</a></li>
</ul>
{% block body %}
{% endblock %}
<footer>底部标签</footer>
</body>
</html>

child1.html内容如下。

{% extends 'base.html' %}

{% block title %}
    我是子模板
{% endblock %}

{% block body %}
    我是子模板的文字
{% endblock %}

app.py内容如下。

from flask import Flask, render_template

app = Flask(__name__)

@app.route('/child1')
def child1():
    return render_template('child1.html')

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

加载静态文件

static.html内容如下。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>static_demo</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
    <script src="{{ url_for('static', filename='js/my.js') }}"></script>
</head>
<body>
    <img src="{{ url_for('static', filename='images/air.jpg') }}" alt="">
</body>
</html>

app.py内容如下。

from flask import Flask, render_template

app = Flask(__name__)

@app.route('/static')
def static_demo():
    return render_template('static.html')


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

操作MySQL

Python操作MySQL驱动

Flask要想操作数据库,必须要先安装Python操作MySQL的驱动,在Python中,目前有以下MySQL驱动包:

  • MySQL-python:也就是MySQLdb,是对C语言操作MySQL数据库的一个简单封装,但只支持Python2。
  • mysqlclient:是MySQL-python的一个分支,支持Python3并且修复了一些bug,是目前为止执行效率最高的驱动,但是安装的时候容易因为环境问题出错。
  • pymysql:纯Python实现的驱动,执行效率不如mysqlclient,可以和Python代码无缝衔接。
  • mysql-connector-python:MySQL官方推出的纯Python连接MySQL驱动,执行效率比pymysql慢。

安装pymysql

pip install pymysql -i https://pypi.tuna.tsinghua.edu.cn/simple

安装Flask-SQLAlchemy

在Flask中,很少会使用pymysql直接写原生SQL语句去操作数据库,更多的是通过SQLAlchemy提供的ORM技术,其类似于操作普通Python对象那样来实现对数据库的增删改查,而Flask-SQLAlchemy是对SQLAlchemy的一个封装,使得在Flask中使用SQLAlchemy更加方便。Flask-SQLAlchemy需要单独安装,因为Flask-SQLAlchemy依赖SQLAlchemy,所以只要安装了Flask-SQLAlchemy,SQLAlchemy会自动安装。

pip install flask-sqlalchemy -i https://pypi.tuna.tsinghua.edu.cn/simple

SQLAlchemy类似于Jinjia2,是可以独立于Flask而被使用的,完全可以在任何Python程序中被使用。SQLAlchemy官方链接

Flask-SQLAlchemy基本使用

连接MySQL

使用Flask-SQLAlchemy操作数据库之前,需要先创建一个由FLask-SQLAlchemy提供的SQLAlchemy类的对象,在创建这个类的时候,需要传入当前的app,然后要在app.config中设置SQLALCHEMY_DATABASE_URI,来配置数据库的连接。

app.py内容如下。

from flask import Flask, render_template
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import text
app = Flask(__name__)

HOSTNAME = '127.0.0.1'  # 主机名

PORT = 3306  # 端口号

USERNAME = 'root'  # 连接MySQL的用户名

PASSWORD = '123456'  # 连接MySQL的密码

DATABASE = 'db_flask_test'  # 在MySQL创建的数据库名称

app.config['SQLALCHEMY_DATABASE_URI'] = f'mysql+pymysql://{USERNAME}:  	  	 {PASSWORD}@{HOSTNAME}:{PORT}/{DATABASE}?charset=utf8mb4'

db = SQLAlchemy(app)

# 在app.config中设置好连接的数据库信息,然后使用SQLAlchemy(app)创建一个db对象,QLAlchemy会自动读取app.config中连接数据库的信息

# 测试是否连接成功
with app.app_context():  //应用上下文
    with db.engine.connect() as conn:
        rs = conn.execute(text("select 1"))
        print(rs.fetchone())  //若打印1,则说明连接成功

@app.route('/')
def hello_world():
    return render_template('index.html')


if __name__ == '__main__':
    app.run()  # 运行项目

注:测试时若报错,提示语句不是可执行的对象,可以使用sqlalchemy.text() 函数对语句进行封装。

ORM模型与表的映射

ORM模型

对象关系映射(Object Relationship Mapping),简称ORM,是一种可以用Python面向对象的方式来操作关系型数据库的技术,具有可以映射到数据库表能力的Python类我们称之为ORM模型。一个ORM模型与数据库中一个表相对应,ORM模型中的每个类属性分别对应表的每个字段,ORM模型的每个实例对象对应表中每条记录。ORM技术提供了面向对象的SQL交互的桥梁,让开发者用面向对象的方式操作数据库,使用ORM模型具有以下优势:

  • 开发效率高:几乎不需要写原生SQL语句,使用纯Python的方式操作数据库。
  • 安全性高:ORM模型底层代码对一些常见的安全问题,比如SQL注入做了防护,比直接使用SQL语句更加安全。
  • 灵活性强:Flask-SQLAlchemy底层支持SQLite、MySQL、Oracle、PostgreSQL等关系型数据库,但针对不同数据库,ORM模型代码几乎一模一样,只需要修改少量代码,即可完成底层数据库的更换。
映射方法
法一——通过db.create_all()映射。
with app.app_context():
    db.create_all()  # 将所有的表同步到数据库中
法二——通过flask-migrate映射。

db.create_all()只能识别新增了哪些模型,把新增的模型同步到数据库中,若是模型中字段值改变,它无法识别,这种情况下需要使用flask-migrate。

安装flask-migrate代码如下。

pip install flask-migrate -i https://pypi.tuna.tsinghua.edu.cn/simple

flask-migrate三部曲,在命令行中执行如下指令。

  • flask db init:只执行一次,执行后生成migrations文件夹。
  • flask db migrate:识别ORM模型的改变,生成迁移脚本。
  • flask db upgrade:运行迁移脚本,同步到数据库中。

MySQL的增删改查

使用ORM进行增删改查操作,需要先把操作添加到会话中,通过db.session可以获取到会话对象,会话对象只是在内存中,如果要把会话中的操作提交到数据库中,需要调用db.session.commit()操作,如果想要把会话中的操作回滚,则可以通过db.session.rollback()实现。

增加数据

使用ORM创建一条数据,首先需使用ORM模型创建一个对象,然后添加到会话中,再进行commit操作即可。创建对象时,必须要通过关键字参数给字段赋值。

class User(db.Model):
    __tablename__ = 'user'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    username = db.Column(db.String(100), nullable=False)
    password = db.Column(db.String(100), nullable=False)


@app.route("/user/add")
def add_user():
    # 1. 创建ORM对象
    user = User(username="张三", password="123456")  
    # sql: insert into user(username, password) values('张三', '123456')
    # 2. 将ORM对象添加到db.session中
    db.session.add(user)
    # 3. 将db.session中的改变同步到数据库中
    db.session.commit()
    return '用户添加成功'
查找数据
class User(db.Model):
    __tablename__ = 'user'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    username = db.Column(db.String(100), nullable=False)
    password = db.Column(db.String(100), nullable=False)

@app.route("/user/query")
def query_user():
    # 1. get查找:根据主键查找
    user = User.query.get(1)
    print(f'{user.id}-{user.username}-{user.password}')
    # 2. filter_by查找,返回Query数组
    users = User.query.filter_by(username='张三')
    for user in users:
        print(user.username)
    return '数据查找成功!'
修改数据
class User(db.Model):
    __tablename__ = 'user'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    username = db.Column(db.String(100), nullable=False)
    password = db.Column(db.String(100), nullable=False)

@app.route("/user/update")
def update_user():
    user = User.query.filter_by(username='张三').first()
    user.password = '654321'
    db.session.commit()
    return '数据修改成功'
删除数据
class User(db.Model):
    __tablename__ = 'user'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    username = db.Column(db.String(100), nullable=False)
    password = db.Column(db.String(100), nullable=False)

@app.route('/user/delete')
def delete_user():
    # 1. 查找
    user = User.query.get(1)
    # 2. 从db.session中删除
    db.session.delete(user)
    # 3. 将db.session中的修改同步到数据库中
    db.session.commit()
    return '数据删除成功'

ORM模型外键与表的关系

关系型数据库中,多个表之间可以建立关系,表关系总体上可以分成三种,分别是:一对一、一对多(多对一)、多对多,表关系的建立是通过数据库层面的外键来实现的,创建外键是通过db.ForeignKey实现的。

class User(db.Model):
    __tablename__ = 'user'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    username = db.Column(db.String(100), nullable=False)
    password = db.Column(db.String(100), nullable=False)

    articles = db.relationship('Article', back_populates='author')

class Article(db.Model):
    __tablename__ = 'article'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    title = db.Column(db.String(200), nullable=False)
    content = db.Column(db.Text, nullable=False)

    author_id = db.Column(db.Integer, db.ForeignKey("user.id"))
    author = db.relationship('User', back_populates='articles')
    # author = db.relationship('User', backref='articles'), backref会自动给User模型添加一个article属性,用来获取文章列表

@app.route("/article/add")
def article_add():
    article1 = Article(title='Flask学习', content="xxxxx")
    article1.author = User.query.get(2)

    article2 = Article(title='Django学习', content='xxxxx')
    article2.author = User.query.get(2)

    # 添加到session中
    db.session.add_all([article1, article2])
    # 同步session中的数据到数据库中
    db.session.commit()
    return '文章添加成功'

@app.route('/article/query')
def query_article():
    user = User.query.get(2)
    for article in user.articles:
        print(article.title)
    return '文章查找成功'

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:初识Flask - Python技术站

(0)
上一篇 2023年3月31日
下一篇 2023年3月31日

相关文章

合作推广
合作推广
分享本页
返回顶部