The installation of Flask-SQLAlchemy uses one-to-many many-to-many join query

Flask-SQLAlchemy installation and setup

  • SQLALchemy is actually an abstraction of the database, so that developers do not need to deal directly with SQL statements, but operate the database through Python objects. While giving up some performance overhead, in exchange for a greater improvement in development efficiency
  • SQLAlchemy is a relational database framework that provides high-level ORM and underlying native database operations. flask-sqlalchemy is a flask extension that simplifies SQLAlchemy operations
    Document address: https://flask-sqlalchemy.palletsprojects.com/en/3.0.x/
    SQLAlchemy documentation: https://docs.sqlalchemy.org/en/20/

Install

  • Install flask-sqlalchemy
    pip install flask-sqlalchemy
  • If you are connecting to a mysql database, you need to installpymysql
    pip install pymysql

Database connection settings

  • In Flask-SQLAlchemy, the database is specified using a URL, and the database used by the program must be stored in the SQLALCHEMY_DATABASE_URI key of the Flask configuration object
    app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:[email protected]:3306/test'
  • other settings:
# 动态追踪修改设置,如未设置只会提示警告
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
#查询时会显示原始SQL语句
app.config['SQLALCHEMY_ECHO'] = True
  • After the configuration is complete, you need to create the database used by the project in MySQL
$ mysql -uroot -pmysql
$ create database test charset utf8;
  • other configuration
SQLALCHEMY_DATABASE_URI	用于连接的数据库 URI 。例如:sqlite:tmp/test.dbmysql://username:password@server/db
SQLALCHEMY_BINDS	一个映射 binds 到连接 URI 的字典。更多 binds 的信息见用 Binds 操作多个数据库。
SQLALCHEMY_ECHO	如果设置为Ture, SQLAlchemy 会记录所有 发给 stderr 的语句,这对调试有用。(打印sql语句)
SQLALCHEMY_RECORD_QUERIES	可以用于显式地禁用或启用查询记录。查询记录 在调试或测试模式自动启用。更多信息见get_debug_queries()。
SQLALCHEMY_NATIVE_UNICODE	可以用于显式禁用原生 unicode 支持。当使用 不合适的指定无编码的数据库默认值时,这对于 一些数据库适配器是必须的(比如 Ubuntu 上 某些版本的 PostgreSQL )。
SQLALCHEMY_POOL_SIZE	数据库连接池的大小。默认是引擎默认值(通常 是 5 )
SQLALCHEMY_POOL_TIMEOUT	设定连接池的连接超时时间。默认是 10 。
SQLALCHEMY_POOL_RECYCLE	多少秒后自动回收连接。这对 MySQL 是必要的, 它默认移除闲置多于 8 小时的连接。注意如果 使用了 MySQL , Flask-SQLALchemy 自动设定 这个值为 2 小时。

Connect to other databases

For a complete list of connection URIs, please jump to the SQLAlchemy documentation ( supported database ) below . Some common connection strings are given here.

  • Postgres:
    postgresql://scott:tiger@localhost/mydatabase
  • MySQL:
    mysql://scott:tiger@localhost/mydatabase
    or
    mysql+pymysql://scott:tiger@localhost/mydatabase
  • Oracle:
    oracle://scott:[email protected]:1521/sidname
  • SQLite (note the four slashes at the beginning):
    sqlite:absolute/path/to/foo.db

Commonly used SQLAlchemy field types

insert image description here

Commonly used SQLAl1chemy column options

insert image description here

Common SQLAlchemy relational options

insert image description here

Basic operation of database

  • In Flask-SQLAlchemy, insert, modify, and delete operations are managed by database sessions.
    • A session is represented by db.session. Before preparing to write data to the database, add data to the session and then call the commit() method to commit the session.
  • In Flask-SQLAlchemy, the query operation is to manipulate data through the query object.
    • The most basic query is to return all the data in the table, and more precise database queries can be performed through filters.

Define the model class in the view function

from flask import Flask
from flask_sqlalchemy import SQLAlchemy


app = Flask(__name__)

#设置连接数据库的URL
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:123456@localhost/test'

app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
#查询时会显示原始SQL语句
app.config['SQLALCHEMY_ECHO'] = True
db = SQLAlchemy(app)

class Role(db.Model):
    # 定义表名,数据库中的真实表名
    __tablename__ = 'roles'
    # 定义列对象,变量名是数据库字段名
    id = db.Column(db.Integer, primary_key=True)  # 整型主键会默认设置自增主键
    name = db.Column(db.String(64), unique=True)
    users = db.relationship('User', backref='role')

    #repr()方法显示一个可读字符串,使用print打印对象时,会输出这里定义的内容
    def __repr__(self):
        return 'Role:%s'% self.name

class User(db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True, index=True)
    email = db.Column(db.String(64),unique=True)
    password = db.Column(db.String(64))
    role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))

    def __repr__(self):
        return 'User:%s'%self.name

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

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

Create table:

db.create_all()

delete table

db.drop_all()

Insert a piece of data

ro1 = Role(name='admin')
db.session.add(ro1)
db.session.commit()

Insert a piece of data again

ro2 = Role(name='user')
db.session.add(ro2)
db.session.commit()

Insert multiple data at once

us1 = User(name='wang',email='[email protected]',password='123456',role_id=ro1.id)
us2 = User(name='zhang',email='[email protected]',password='201512',role_id=ro2.id)
us3 = User(name='chen',email='[email protected]',password='987654',role_id=ro2.id)
us4 = User(name='zhou',email='[email protected]',password='456789',role_id=ro1.id)
us5 = User(name='tang',email='[email protected]',password='158104',role_id=ro2.id)
us6 = User(name='wu',email='[email protected]',password='5623514',role_id=ro2.id)
us7 = User(name='qian',email='[email protected]',password='1543567',role_id=ro1.id)
us8 = User(name='liu',email='[email protected]',password='867322',role_id=ro1.id)
us9 = User(name='li',email='[email protected]',password='4526342',role_id=ro2.id)
us10 = User(name='sun',email='[email protected]',password='235523',role_id=ro2.id)
db.session.add_all([us1,us2,us3,us4,us5,us6,us7,us8,us9,us10])
db.session.commit()

Inquire

Commonly used SQLAlchemy query filters (for filtering)

filter()	# 把过滤器添加到原查询上,返回一个新查询
filter_by()	# 把等值过滤器添加到原查询上,返回一个新查询
limit	# 使用指定的值限定原查询返回的结果
offset()	# 偏移原查询返回的结果,返回一个新查询
order_by()	# 根据指定条件对原查询结果进行排序,返回一个新查询
group_by()	# 根据指定条件对原查询结果进行分组,返回一个新查询

The above filter conditions can be used in conjunction, such asUser.query.filter().offset().order_by().limit()

Commonly used SQLAlchemy query executors (for fetching results)

all()	# 以列表形式返回查询的所有结果
first()	# 返回查询的第一个结果,如果未查到,返回None
first_or_404()	# 返回查询的第一个结果,如果未查到,返回404
get()	# 返回指定主键对应的行,如不存在,返回None
get_or_404()	# 返回指定主键对应的行,如不存在,返回404
count()	# 返回查询结果的数量
paginate()	# 返回一个Paginate对象,它包含指定范围内的结果

Query: filter_by precise query

Returns all people whose first name is equal to wang

User.query.filter_by(name='wang').all()  # flask-sqlalchemy提供的写法,query无参数

user = db.session.query(User).filter_by(name='wang').all()  # sqlalchemy提供的写法,query有参数

Executing sql is the filter condition in where

SELECT users.id AS users_id, users.name AS users_name, users.email AS users_email, users.password AS users_password, users.role_id AS users_role_id 
FROM users 
WHERE users.name = %(name_1)s

first() returns the first object found in the query

User.query.first()

sql, you can see that it is limit

SELECT users.id AS users_id, users.name AS users_name, users.email AS users_email, users.password AS users_password, users.role_id AS users_role_id 
FROM users 
 LIMIT %(param_1)s

all() returns all objects found in the query

User.query.all()
SELECT users.id AS users_id, users.name AS users_name, users.email AS users_email, users.password AS users_password, users.role_id AS users_role_id 
FROM users

filter fuzzy query, returns all the data whose name ends with g

User.query.filter(User.name.endswith('g')).all()
SELECT users.id AS users_id, users.name AS users_name, users.email AS users_email, users.password AS users_password, users.role_id AS users_role_id 
FROM users 
WHERE (users.name LIKE concat('%%', %(name_1)s))

Note that the '%%' in concat is the formatting of python strings, such as:

a= "%%%s%%"%("abc")
实际表示的是
'%abc%'

It %(name_1)sis also formatted, such as:

print(“I’m %(name)s. I’m %(age)d” % {‘name’:‘Pythontab’, ‘age’:99})

That is, the final sql will be

SELECT users.id AS users_id, users.name AS users_name, users.email AS users_email, users.password AS users_password, users.role_id AS users_role_id 
FROM users 
WHERE (users.name LIKE concat('%', 'g'))  - 也就是以g结尾

get(): The parameter is the primary key, if the primary key does not exist, no content is returned

User.query.get(1)

Logical NOT, return all data whose name is not equal to wang

User.query.filter(User.name!='wang').all()
SELECT users.id AS users_id, users.name AS users_name, users.email AS users_email, users.password AS users_password, users.role_id AS users_role_id 
FROM users 
WHERE users.name != %(name_1)s

not_ is equivalent to negate

from sqlalchemy import not_
User.query.filter(not_(User.name=='chen')).all()
SELECT users.id AS users_id, users.name AS users_name, users.email AS users_email, users.password AS users_password, users.role_id AS users_role_id 
FROM users 
WHERE users.name != %(name_1)s

Essentially indistinguishable from logical NOT

Logical and, need to import and, return all data that meets the condition of and()

from sqlalchemy import and_
User.query.filter(and_(User.name!='wang',User.email.endswith('163.com'))).all()
SELECT users.id AS users_id, users.name AS users_name, users.email AS users_email, users.password AS users_password, users.role_id AS users_role_id 
FROM users 
WHERE users.name != %(name_1)s AND (users.email LIKE concat('%%', %(email_1)s))

Logical or, need to import or_

from sqlalchemy import or_
User.query.filter(or_(User.name!='wang',User.email.endswith('163.com'))).all()
SELECT users.id AS users_id, users.name AS users_name, users.email AS users_email, users.password AS users_password, users.role_id AS users_role_id 
FROM users 
WHERE users.name != %(name_1)s OR (users.email LIKE concat('%%', %(email_1)s))

Group statistics group_by

flask-sqlalchemy can't achieve

from sqlalchemy import func
# 统计每个角色的用户数,即用户表以role_id分组统计count即可
ss = db.session.query(User.role_id, func.count(User.role_id)).group_by(User.role_id).all()

If you need to filter the grouped data use having

ss = db.session.query(User.role_id, func.count(User.role_id)).group_by(User.role_id).having(func.count(User.role_id)>1).all()

Execute sql as follows:
SELECT users.role_id AS users_role_id, count(users.role_id) AS count_1 FROM users GROUP BY users.role_id HAVING count(users.role_id) > 1

Delete after querying data

user = User.query.first()
db.session.delete(user)
db.session.commit()
User.query.all()

update data

user = User.query.first()
user.name = 'dong'
db.session.commit()
User.query.first()

Links Between Models

one-to-many

In the Role and User defined above, the core code of the one-to-many relationship is as follows

class Role(db.Model):
    ...
    #关键代码
    users = db.relationship('User', backref='role')
    ...

class User(db.Model):
    ...
    # 外键,数据库中真实存在的字段
    role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))

It realtionshipdescribes the relationship between Roleand User, in the one-to-many relationship, it is defined on one side (in this example, one role corresponds to multiple users, so it is defined in the Role class).

The first parameter is the class "User" corresponding to the reference, input as a string. The
second parameter backrefis a new attribute created for the class User, so that when there is a user object, the object user.rolecorresponding to the user can be obtained Role; in addition, if there is a role Object, you can use it directly when you want to get all the corresponding users role.users.
There is also a third parameter lazythat determines when SQLALchemy loads data from the database (not set in the case)

  • If it is set to subquery mode (subquery), the object associated with it will be loaded immediately after the Role object is loaded, which will reduce the total number of queries, but if the number of returned items is large, it will be slower. Set to For subquery, role.users returns a list of all data
  • It can also be set to dynamic, so that the associated objects will be loaded when they are used, and filtered before returning. If the number of returned objects is large, or there will be many in the future, it is best to use this method Way. If it is set to dynamic, role.users returns the query object, and does not do real query, you can use the query object to do other logic, such as: first sort and then return the result

Cannot be omitted in the User class role_id. This field is set as a foreign key, which is associated with the id of the roles table. When used, user.rolethis field will be used to get all the attributes of the role object. Otherwise, only user.role_idthe id can be obtained, but not other attributes. .

many to many

Requirement example: Students choose courses online, each student can choose multiple courses, and each course can also be selected by multiple students, forming a many-to-many relationship. The only point in the description of the many-to-many relationship is that a separate table needs to be added to record the correspondence between the two tables . This table is a table that actually exists in the database.

# 中间表,注意中间表使用db.Table,而非继承自db.Model,该表未设置主键
tb_student_course = db.Table('tb_student_course',
                             db.Column('student_id', db.Integer, db.ForeignKey('students.id')),
                             db.Column('course_id', db.Integer, db.ForeignKey('courses.id'))
                             )

# 学生表
class Student(db.Model):
    __tablename__ = "students"
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)
    # 关系,secondary指定关联中间表。通过backref使Course类中有student属性
    courses = db.relationship('Course', secondary=tb_student_course,
                              backref='student',
                              lazy='dynamic')

# 课程表
class Course(db.Model):
    __tablename__ = "courses"
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)

Example relational query (one-to-many):

The relationship between roles and users is a one-to-many relationship. A role can have multiple users, and a user can only belong to one role.

Query all users with known roles

#查询roles表id为1的角色
ro1 = Role.query.get(1)
#查询该角色的所有用户
ro1.users.all()

Query the roles of known users

#查询users表id为3的用户
us1 = User.query.get(3)
#查询用户属于什么角色
us1.role
SELECT roles.id AS roles_id, roles.name AS roles_name 
FROM roles 
WHERE roles.id = %(pk_1)s

The above two are known role_idor user_id, so the join query is not involved

Example of relational query (many-to-many)

Query which courses a student has taken

    stu1 = Student.query.get(1)
    cos = stu1.courses  # Student中有属性courses定义了关系
    for co in cos:
        print(co.name)

execute sql
SELECT courses.id AS courses_id, courses.name AS courses_name FROM courses, tb_student_course WHERE 1 = tb_student_course.student_id AND courses.id = tb_student_course.course_id

Query which students are enrolled in a course

    co1 = Course.query.get(1)
    stu = co1.student  # [<Student 1>, <Student 2>, <Student 3>] 学生列表
    for s in stu:
        print(s.name)

execute sql
SELECT students.id AS students_id, students.name AS students_name FROM students, tb_student_course WHERE 1 = tb_student_course.course_id AND students.id = tb_student_course.student_id

join query

  • Inner join inner join
# 以下查询结果中只包含user表的字段,不含角色表字段,实践中用途不大。得到的是User对象列表
# 其中User.role_id==Role.id在设置的有外键时,可不指定
d = User.query.join(Role, User.role_id==Role.id).all()

# sql
SELECT users.id AS users_id, users.name AS users_name, users.email AS users_email, users.password AS users_password, users.role_id AS users_role_id 
FROM users INNER JOIN roles ON users.role_id = roles.id

To query specific fields below, use with_entitiesthe field information to be queried

# 查询用户角色不为空时,用户的角色名(本质是inner join)
s = User.query.join(Role, User.role_id==Role.id).with_entities(User.id, User.name, User.role_id, Role.name).all()

# 原生sqlalchemy的写法
db.session.query(User.id, User.name, User.role_id, Role.name).join(User, User.role_id==Role.id, isouter=True).all()

# 结果样例,列表嵌套元组
[(6, 'wu', 2, 'user'), (7, 'qian', 1, 'admin'), (8, 'liu', 1, 'admin'), (9, 'li', 2, 'user'), (10, 'sun', 2, 'user')]

# 执行sql如下
SELECT users.id AS users_id, users.name AS users_name, users.role_id AS users_role_id, roles.name AS roles_name 
FROM users INNER JOIN roles ON users.role_id = roles.id
  • left join
d = User.query.join(Role, User.role_id==Role.id, isouter=True).with_entities(User.id, User.name, User.role_id, Role.name).all()

# sql
SELECT users.id AS users_id, users.name AS users_name, users.role_id AS users_role_id, roles.name AS roles_name 
FROM users LEFT OUTER JOIN roles ON users.role_id = roles.id
  • Right join
    is the same as left join, the two tables can be reversed

In addition, there is User.query.outerjoinan interface that represents external connection, but its essence is still join. The source code is as follows:
insert image description here

Association query is not mandatory to set foreign keys in the model class, as long as it can be associated through a certain field, you can use join query, you only need to specify the association field in the join.

subquery

# 下边代码是在Role和User表各添加了deleted字段,表示是否删除,0未删除,1删除。
# 通过subquery设定子查询
        abc = db.session.query(User.id.label('id'), User.name.label('username'), Role.name.label('rolename'),
                           Role.deleted.label('deleted'), func.IF(Role.deleted==0,1,0).label('snapshot')).\
        join(Role, Role.user_id == User.id, isouter=True).subquery()
        xx = db.session.query(abc.c.id, abc.c.username, func.sum(abc.c.snapshot)).group_by(abc.c.id).all()

execute sql

select userid, username, sum(snp) from (
SELECT users.id userid, users.name username, roles.name rolename,roles.deleted, if(roles.deleted=0,1,0) as snp
 FROM users LEFT OUTER JOIN roles ON roles.user_id = users.id) s GROUP BY s.userid;

Common functions

Aggregation functions usually used in the database such as sum, count, if function, etc. are all sqlalchemy.funcin

from sqlalchemy import func
# 聚合函数
db.session.query(User.role_id, func.count(User.role_id)).group_by(User.role_id).all()

# if 函数
db.session.query(User.id, func.IF(User.id > 4, 1, 0).label('xx'))  # user id大于4,返回1,否则返回0

Where label is used to set the alias

about

Welcome to follow my blog

Guess you like

Origin blog.csdn.net/HYESC/article/details/129022912