Flask框架学习记录

Flask项目简要

项目大致结构

flaskDemo1
├─static
├─templates
└─app.py

app.py

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

# 使用Flask类创建一个app对象
# __name__:代表当前app.py这个模块
# 1.以后出现bug,可以帮助快速定位
# 2.对于寻找模板文件,有一个相对路径
app = Flask(__name__)


# 创建一个路由和视图函数的映射
@app.route('/')
def hello_world():
    return 'https://blog.csdn.net/m0_61465701?type=blog'


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

运行结果:

FLASK_APP = app.py
FLASK_ENV = development
FLASK_DEBUG = 0
In folder E:/PyCharmProject/flaskDemo1
E:\SoftwareFile\anaconda\python.exe -m flask run 
 * Serving Flask app "app.py"
 * Environment: development
 * Debug mode: off
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

可以进入网站查看效果。

debug模式

开启debug模式后

  • 只要修改代码后保存,就会自动重新加载,不需要手动重启项目。
  • 在浏览器上就可以看到出错信息。

开启

点击Edit Configurations… , 再勾选Configuration下的FLASK_DEBUG选项,点击OK。

社区版:修改代码

app.run(debug=True)

修改代码后,ctrl+s保存后就会自动重新加载。

 * Detected change in 'E:\\PyCharmProject\\flaskDemo1\\app.py', reloading
 * Restarting with watchdog (windowsapi)
 * Debugger is active!
 * Debugger PIN: 296-639-520
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

修改host

主要作用:就是让其他电脑能访问到自己电脑上的flask项目

点击Edit Configurations… , 在Configuration下的Additional options中填写即可

--host=0.0.0.0  //可以写自己电脑的ip地址
// 0.0.0.0别人可以访问你电脑上的

修改port端口号

点击Edit Configurations… , 在Configuration下的Additional options中填写

--host=0.0.0.0 --port=8000

URL与视图的映射

url: http[80]/https[443]://www.xxx.com:443/path (冒号应为英文的:)

url与视图更准确来说是path与视图,只有path部分是需要自己现在编写的。

绝大部分网站都不可能只有首页一个页面,我们在制作网站的时候,需要定义许多不同的URL来满足需求,而URL总体上来讲又分为两种,第一种是无参数的,第二种是有参数的。

定义无参URL

无参URL是在URL定义的过程中,不需要定义参数。

可以使用以下代码实现。

@app.route('/2')
def stage2():
    return 'stage2'


@app.route('/3/abc')
def stage3():
    return 'stage3--abc'

注意:我们说的访问/path是不包含域名和端口号的,真正在浏览器中访问应该在前面加上域名和端口号,比如在本地开发应该为http://127.0.0.1:5000/path ,下文说的URL都是省略了域名和端口号。

定义带有参数的URL

很多时候,我们在访问某个URL的时候,需要携带一些参数过去。

比如获取博客详情,就需要把博客的id传过去,那么博客详情的URL可能为:/blog/13,其中13为博客的id。比如获取第10页的博客列表,那么博客列表的URL可能为: /blog/list/10,其中10为页码。

在Flask中,如果URL中携带了参数,那么视图函数也必须定义相应的形参来接收URL中的参数。

@app.route('/test/<int:num>')
def show(num):
    return "num=%s" % num

可以看到,URL中多了一对尖括号,并且尖括号中多了一个num,这个 num就是参数。然后在视图函数 show中,也相应定义了一个num的形参,当浏览器访问这个URL的时候,Flask接收到请求后,会自动解析URL中的参数 num,然后把他传给视图函数 show,在视图函数中,开发者就可以根据这个num,从数据库中查找到具体的数据,返回给浏览器。

参数类型可以不指定。

URL中的参数可以指定以下类型

参数类型 描述
string 字符串类型。可以接受除/以外的字符
int 整型。可以接受通过int()方法转换的字符
float 浮点类型。以接受通过float()方法转换的字符
path 路径。类似string,但是中间可以添加/。
uuid UUID类型。UUID是一组由32位数的16进制所构成。
any 备选值中的任何一个。

比较特殊的any举例:

@app.route('/<any(a,b,c):s>')
def show2(s):
    return "str=%s" % s

查询字符串的方式传参

from flask import Flask,request

...

@app.route('/num1')
def show3():
    # arguments:参数
    # request.args:类字典类型
    num = request.args.get("num",default=555,type=int)
    return f"num={
      
      num}"

在浏览器访问http://127.0.0.1:5000/num1 结果为

num=555

在浏览器访问http://127.0.0.1:5000/num1?num=123结果为

num=123

Jinja2模板

在Flask中,渲染HTML通常会交给模板引擎来做,而Flask中默认配套的模板引擎是Jinja2,Jinja2的作者也是Flask的作者,Jinja2可以独立于Flask使用,比如被Django使用。Jinja2是一个高效、可扩展的模板引擎。

模板渲染

在templates下新建一个html文件,index.html

<!--html 5的标签-->
<!DOCTYPE html>
<html lang="en">
<head>
    <!--页面的字符集-->
    <meta charset="UTF-8">
    <title>标题</title>
</head>
<body>

<h1>标题 h1</h1>
<hr>

<p>
    HTML 是一门语言,所有的网页都是有HTML这门语言编写出来的。<br>HTML 是一门语言,所有的网页都是有HTML这门语言编写出来的。
</p>

</body>
</html>

然后修改app.py中的代码

from flask import Flask,request,render_template


app = Flask(__name__)


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


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

渲染变量

html文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>博客详情</title>
</head>
<body>

<h1>用户名:{
   
   { username }}</h1>
<h1>访问的博客详情是:{
   
   { blog_id }}</h1>

</body>
</html>

修改app.py:

@app.route('/blog/<blog_id>')
def blog_detail(blog_id):
    return render_template("blog_detail.html",blog_id=blog_id,username='Xiaoming')

模板访问对象属性

index.html:

<!--html 5的标签-->
<!DOCTYPE html>
<html lang="en">
<head>
    <!--页面的字符集-->
    <meta charset="UTF-8">
    <title>标题</title>
</head>
<body>

<h1>用户名/邮箱</h1>

<h2>{
   
   { user.username }}/{
   
   { user.email }}</h2>

</body>
</html>

app.py:

app = Flask(__name__)


class User:
    def __init__(self,username,email):
        self.username = username
        self.email = email


@app.route('/test2')
def test2():
    user = User(username="Xiaoming",email="[email protected]")
    return render_template("index.html",user=user)

字典

index.html:

<h1>用户名/邮箱</h1>

<div>{
   
   { user.username }}/{
   
   { user.email }}</div>

<div>{
   
   { person['username'] }}/{
   
   { person.email }}</div>

app.py:

app = Flask(__name__)


class User:
    def __init__(self,username,email):
        self.username = username
        self.email = email


@app.route('/test2')
def test2():
    user = User(username="Xiaoming",email="[email protected]")
    person = {
    
    
        "username":"Wang Xiaoming",
        "email":"[email protected]"
    }
    return render_template("index.html",user=user,person=person)

过滤器的使用

在Python中,如果需要对某个变量进行处理。我们可以通过函数来实现。在模板中,我们则是通过过滤器来实现的。过滤器本质上也是函数。但是在模板中使用的方式是通过管道符号|来调用的。

例如有个字符串类型变赋 name。想要获取他的长度。则可以通过{ { name |length}}来获收。Jinja2会把name当傲第一个参数传给 length过滤器底层对应的函数。

新建filter.html文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>过滤器使用</title>
</head>
<body>

{
   
   { user.username }}-长度为:{
   
   { user.username|length }}
<br>
{
   
   { user.email }}-长度为:{
   
   { user.email|length }}

</body>
</html>

修改app.py

@app.route('/filter')
def filter_demo():
    user = User(username="Zhang",email="[email protected]")
    return render_template("filter.html",user=user)

一些过滤器

  1. abs(value):返回一个数值的绝对值,例如{ { -1|abs }}。如果给的参数类型不为数字,就会报错。

  2. default(value,default_value,boolean=False):如果value没有定义,则返回第二个参数default_value。如果想要让 value在被判断为False的情况下(传入'',[],None,{}这些值的时候)使用default_value,则应该将后面的boolean参数设置为True。

  3. escape(value):将一些特殊字符,比如:&,<,>,",'进行转义。因为Jinja2默认开启了全局转义,所以在大部分情况下无需手动使用这个过滤器去转义,只有在关闭转义的情况下会需要使用。

  4. first(value): 返回序列的第一个元素

    • 如果是一个字典,那么返回的是key的值
  5. last(value): 返回序列的最后一个元素

  6. format(value,*args,**kwargs): 格式化字符窜,和python中写法是一样的。

    {
         
         { "%s/%s"|format("username","email") }}
    
  7. join(value,d=’’): 将一个序列用d这个参数的值拼接成字符串

    num=[1,2,3,4]
    
    {
         
         { num|join('-') }}
    
    结果:
    1-2-3-4
    

自定义过滤器

如果内置过滤器不满足需求,我们还可以自定义过滤器。

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

def add_string(value, mystr):
    return value+mystr


app.add_template_filter(add_string,"addstring")

其中第一个参数是需要被处理的值,然后通过app.add_template_filter,将函数注册成了过滤器,并且这个过滤器的名字,叫做addstring。那么以后在模板文件中,就可以使用了:

{
   
   { user.username|addstring("hhhh") }}

如果app.add_template_filter没有传第二个参数,那么默认将使用函数的名称,来作为过滤器的名称。

app.add_template_filter(add_string)
{
   
   { user.username|add_string("hhhh") }}

控制语句

if语句

Jinja2中的if语句和Python中的if语句非常的类似。可以使用>、<、>=、<=、==、!=来进行判断,也可以通过and、or、not来进行逻辑操作。

control.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>控制语句</title>
</head>
<body>
{% if a > 20 %}
    <div>a的值大于20</div>
{% elif a == 20%}
    <div>a的值等于20</div>
{% else %}
    <div>a的值小于20</div>
{% endif %}
</body>
</html>

app.py

@app.route('/control')
def control_statement():
    a = 22
    return render_template("control.html",a = a)

for循环

Jinja2中的 for循环与Python中的 for 循环也是非常类似的,只是比 Python中的 for 循环多一个endfor 代码块。

不存在break语句。

control.html

{% for person in persons %}
    <div>name:{
   
   { person.name }},hobby:{
   
   { person.hobby }}</div>
{% endfor %}

app.py

@app.route('/control')
def control_statement():
    a = 22
    persons = [{
    
    
        "name":"Xiaoming",
        "hobby":"baseball"
    },{
    
    
        "name":"Xiaofang",
        "hobby":"basketball"
    }]
    return render_template("control.html",a = a,persons = persons)

模板继承

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

新建一个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>

<p>父模板的文字</p>
{% block body %}
{% endblock %}
<footer>底部标签</footer>
</body>
</html>

以上父模板中,编写好了网页的整体结构。然后针对子模板需要重写的地方,则定义成了block,比如以上定义了title、body这两个block,子模板在继承了父模板后,重写对应 block 的代码,即可完成子模板的渲染。

编写一个child1.html

{% extends "base.html" %}


{% block title %}
    child1标题
{% endblock %}


{% block body %}
    <p>子模版child1的部分</p>
{% endblock %}

app.py:

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

加载静态文件

一个网页中,除了HTML代码以外,还需要CSS、JavaScript 和图片文件才能更加美观和实用。静态文件默认是存放到当前项目的static文件夹中,如果想要修改静态文件存放路径,可以在创建Flask对象的时候,设置static_folder 参数。

app = Flask(__name__,static_folder='C:\\Users\\Xu\\Desktop\\static')

在模板文件中,可以通过url_for加载静态文件,示例代码如下。

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

第一个参数static是一个固定的,表示构建Flask 内置的static视图这个URL,第二个filename 是可以传递文件名或者文件路径,路径是相对于static或者static_folder参数自定义的路径。以上代码在被模板渲染后,会被解析成:

<link href="/static/style.css">

新建一个style.css文件

body{
    
    
    background: antiquewhite;
}

新建一个jstest.js文件

alert("it is js")

新建一个static.html文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>加载静态文件</title>
    <link rel="stylesheet" href="{
   
   { url_for('static',filename='css/style.css') }}">

</head>
<body>
<img src="{
   
   { url_for('static',filename='images/city.png') }}" alt="">
<script src="{
   
   { url_for('static',filename='js/jstest.js') }}">
</script>
</body>
</html>

修改app.py

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

数据库

数据库是一个动态网站必备的基础功能。通过使用数据库,数据可以被动态的展示、修改、删除等,极大的提高了数据管理能力,以及数据传递的效率。

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

  1. MySQL-python:也就是 MySQLdb。是对C语言操作 MySQL数据库的一个简单封装。遵循了Python DB·API v2。但是只支持Python2。

  2. mysqIclient:是 MySQL-python的另外一个分支。支持Python3并且修复了一些bug,是目前为止执行效率最高的驱动,但是安装的时候容易因为环境问题出错。

  3. pymysql:纯 Python实现的一个驱动。因为是纯 Python编写的,因此执行效率不如 mysqlclient。也正因为是纯 Python写的,因此可以和Python代码无缝衔接。

  4. mysql-connector-python: MySQL官方推出的纯 Python连接MySQL 的驱动,执行效率比pymysql 还慢。

这里我们用的是pymysql

通过以下命令安装。

pip install pymysql

在Flask中,我们很少会使用pymysql直接写原生SQL语句去操作数据库,更多的是通过SQLAlchemy提供的ORM技术,类似于操作普通Python对象一样实现数据库的增删改查操作,而 Flask-SQLAlchemy是对 SQLAIchemy的一个封装,使得在Flask中使用SQLAlchemy更加方便。

Flask-SQLAlchemy是需要单独安装,因为Flask-SQLAlchemy依赖SQLAlchemy,所以只要安装了Flask-SQLAlchemy,sQLAlchemy会自动安装。安装命令如下。

pip install flask-sqlalchemy

SQLAlchemy类似于Jinja2,是可以独立于Flask而被使用的,完全可以在任何Python程序被使用,SQLAlchemy的功能非常强大。

SQLAlchemy 的官方文档

Flask连接MySQL数据库

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

修改app.py

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)

# MySQL所在的主机名
HOSTNAME = "127.0.0.1"
# MySQL监听的端口号,默认3306
PORT = 3306
# 连接MySQL的用户名
USERNAME = "root"
# 连接MySQL的密码
PASSWORD = "031006"
# MySQL上创建的数据库名称
DATABASE = "xlr"

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


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

db = SQLAlchemy(app)

# 测试数据库是否连接成功
# 连接成功后结果为(1,)
with app.app_context():
    with db.engine.connect() as conn:
        rs = conn.execute("select 1")
        print(rs.fetchone())

@app.route('/')
def hello_world():  # put application's code here
    return 'Hello World!'


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

ORM模型与表的映射

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

  1. 开发效率高:几乎不需要写原生SQL语句,使用纯 Python的方式操作数据库,大大的提高了开发效率。
  2. 安全性高:ORM模型底层代码对一些常见的安全问题,比如SQL注入做了防护,比直接使用SQL语句更加安全。
  3. 灵活性强:Flask-SQLAlchemy底层支持SQLite、MySQL、Oracle、PostgreSQL等关系型数据库,但针对不同的数据库,ORM模型代码几乎一模一样,只需修改少量代码,即可完成底层数据库的更换。

修改app.py

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)

# MySQL所在的主机名
HOSTNAME = "127.0.0.1"
# MySQL监听的端口号,默认3306
PORT = 3306
# 连接MySQL的用户名
USERNAME = "root"
# 连接MySQL的密码
PASSWORD = "031006"
# MySQL上创建的数据库名称
DATABASE = "xlr"

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

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

db = SQLAlchemy(app)


# 测试数据库是否连接成功
# 连接成功后结果为(1,)
# with app.app_context():
#     with db.engine.connect() as conn:
#         rs = conn.execute("select 1")
#         print(rs.fetchone())


# User继承自db.Model
# db.Model中封装了与数据库底层交互相关的一些方法和属性
class User(db.Model):
    __tablename__ = 'user'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)  # 这样id才能成为表中的一个字段
    # varchar 最大长度为100 nullable=False字段不能为空
    username = db.Column(db.String(100), nullable=False)
    password = db.Column(db.String(100), nullable=False)


# user = User(username="2021",password="123456")
# sql: insert user(username, password) values('2021', '123456');

with app.app_context():
    db.create_all()


@app.route('/')
def hello_world():  # put application's code here
    return 'Hello World!'


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

首先我们创建一个类名叫 User,并使得他继承自db.Model,所有ORM 模型必须是db.Model的直接或者间接子类。

然后通过_tablename_属性,指定User模型映射到数据库中表的名称。

接着我们定义了三个db.Column类型的类属性,分别是id、username、password,只有使用db.Column定义的类属性,才会被映射到数据库表中成为字段。在这个User模型中,id是 db.Integer类型,在数据库中将表现为整形,并且传递primary_key=True参数来指定id作为主键,传递autoincrement=True来设置id为自增长。接下来的username和password,我们分别指定其类型为db.String类型,在数据库中将表现为varchar类型,并且指定其最大长度为100。

ORM模型的CRUD操作

增加操作

先使用ORM模型创建一个对象,然后添加到会话中,再进行commit 操作即可。

修改app.py

class User(db.Model):
    __tablename__ = 'user'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)  # 这样id才能成为表中的一个字段
    # varchar 最大长度为100 nullable=False字段不能为空
    username = db.Column(db.String(100), nullable=False)
    password = db.Column(db.String(100), nullable=False)

 
@app.route('/user/add')
def user_add():
    # 1. 创建ORM对象
    user = User(username="2021", password="123456")
    # 2. 将ORM对象添加到db.session中
    db.session.add(user)
    # 3. 将db.session中的改变同步到数据库中
    db.session.commit()
    return "Add success!"

查询操作

ORM模型都是继承自db.Model,db.Model内置的query属性上有许多方法,可以实现对ORM模型的查询操作。query 上的方法可以分为两大类,分别是提取方法以及过滤方法。

query常用的提取方法

方法名 描述
query.all() 获取查询结果集中的所有对象,是列表类型。
query.first() 获取结果集中的第一个对象。
query.one() 获取结果集中的第一个对象,如果结果集中对象数量不等于1,则会抛出异常。
query.one_or_none() 与one类似,结果不为1的时候,不是抛出异常,而是返回None。
query.get(pk) 根据主键获取当前ORM模型的第一条数据。
query.exists() 判断数据是否存在。
query.count() 获取结果集的个数。

query常用的过滤方法

方法名 描述
query.filter() 根据查询条件过滤
query.filter_by() 根据关键字参数过滤。
query.slice(start,stop) 对结果进行切片操作。
query.limit(limit) 对结果数量进行限制。
query.offset(offset) 在查询的时候跳过前面offset条数据。
query.order_by() 根据给定字段进行排序。
query.group_by() 根据给定字段进行分组。
@app.route('/user/query')
def user_query():
    # 1. get查找:根据主键查找,只查找一条数据
    # user = User.query.get(1)
    # print(f"id:{user.id},username:{user.username},password:{user.password}")
    # 2. filter_by查找
    users = User.query.filter_by(username='2021')
    # 是一个Query对象:类数组
    for user in users:
        print(f"id:{
      
      user.id},username:{
      
      user.username},password:{
      
      user.password}")
    return "Query Success"

更新操作

@app.route('/user/update')
def user_update():
    user = User.query.filter_by(username='2021').first()
    user.password = "555555"
    db.session.commit()
    return "Update success"

删除操作

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

ORM模型外键与表关系

关系型数据库一个强大的功能,就是多个表之间可以建立关系。

比如文章表中,通常需要保存作者数据,但是我们不需要直接把作者数据放到文章表中,而是通过外键引用用户表。这种强大的表关系,可以存储非常复杂的数据,并且可以让查询非常迅速。在 Flask-SQLAIchemy中,同样也支持表关系的建立。

表关系建立的前提,是通过数据库层面的外键实现的。表关系总体来讲可以分为三种,分别是:一对多(多对一)、一对一、多对多。

建立关系

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"))
    # 通过db.relationship与User模型建立联系
    author = db.relationship("User")

我们添加了一个author 属性,这个属性通过db.relationship 与User模型建立了联系,以后通过Article 的实例对象访问author 的时候,比如 article.author,那么Flask-SQLAlchemy会自动根据外键author_id 从 user表中寻找数据,并形成User模型实例对象。

建立双向关系

现在的Article模型可以通过author属性访问到对应的User实例对象。但是User实例对象无法访问到和他关联的所有Article 实例对象。因此为了实现双向关系绑定,我们还需要在User模型上添加一个db.relationship类型的articles属性,并且在User模型和Article模型双方的db.relationship 上,都需要添加一个back _populates参数,用于绑定对方访问自己的属性。

class User(db.Model):
    __tablename__ = 'user'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)  # 这样id才能成为表中的一个字段
    # varchar 最大长度为100 nullable=False字段不能为空
    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.Interger, db.ForeignKey("user.id"))
    # 通过db.relationship与User模型建立联系
    author = db.relationship("User", back_populates="articles")

以上User和Article模型中,我们通过在两边的db.relationship上,传递back_populates参数来实现双向绑定,这种方式有点啰嗦,我们还可以通过只在一个模型上定义db.relationship类型属性,并且传递 backref参数,来实现双向绑定。

class User(db.Model):
    __tablename__ = 'user'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)  # 这样id才能成为表中的一个字段
    # varchar 最大长度为100 nullable=False字段不能为空
    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.Interger, db.ForeignKey("user.id"))
    # backref:会自动的给User模型添加一个articles的属性,用来获取文章列表
    author = db.relationship("User", backref="articles")

增加和查询article:

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

    article2 = Article(title="Django学习", content="Django最全学习")
    article2.author = User.query.get(2)

    # 添加到session中
    db.session.add_all([article1, article2])
    # 同步session中的数据到数据库中
    db.session.commit()
    return "Articles Add Success!"


@app.route("/article/query")
def query_article():
    user = User.query.get(2)
    for article in user.articles:
        print(article.title)
    return "Article Query Success!"

flask-migrate迁移ORM模型

采用’db.create_all’在后期修改数据库表字段的时候,不会自动的映射到数据库中,必须删除表,然后重新运行’db.create_all’ 才会重新映射。

这样不符合我们的要求,因此flask-migrate就是为了解决这个问题。它可以在每次修改模型后,将修改的字段映射到数据库中。

安装

进入终端,输入

pip install flask-imgrate

使用

...
from flask_migrate import Migrate

...

db = SQLAlchemy(app)

migrate = Migrate(app, db)

...

ORM模型映射成表的三步

  1. flask db init:创建迁移环境 ,迁移环境只需要创建一次。这会在你的项目根目录下创建一个migrations文件夹 。

  2. flask db migrate:识别ORM模型的改变,生成迁移脚本

  3. flask db upgrade:运行迁移脚本,同步到数据库中

ORM模型映射成表的三步都是在终端中输入命令。

猜你喜欢

转载自blog.csdn.net/m0_61465701/article/details/129114833