django模型层详解
django模型层详解
内容概要
-
测试环境配置
-
ORM常用关键字(重要)
-
ORM执行SQL语句
-
神奇的双下线查询
-
ORM外键字段的创建
-
外键字段数据的增删改查
-
多表查询(基于对象的跨表查询、基于双下划线的跨表查询)
-
聚合查询
-
分组查询
-
F与Q查询
- 自带的sqlate3数据库对时间字段不敏感 有时候会展示错乱所以我们习惯切换成常见的数据库比如MYSQL
django orm并不会帮我们创建数据库 所以需要我们自己提前创建好数据库
配置好settings文件数据库
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'djg07',
'HOST': 'localhost',
'PORT': 3306,
'USER': 'root',
'PASSWORD': 'root',
'CHARTSET': 'utf8'
}
}
准备好表
class User(models.Model):
id = models.AutoField(primary_key=True, verbose_name='用户id')
name = models.CharField(max_length=32, verbose_name='姓名')
age = models.IntegerField(verbose_name='年龄')
register_time = models.DateTimeField(verbose_name='注册时间', auto_now_add=True)
# auto_now 是只要修改或添加就会把当前时间自动更新
# auto_now_add 是在增加第一次创建数据时自动把当前时间添加到里面
# models.DateTimeField年月日时分秒时间 models.DateField 年月日时间
def __str__(self):
# 在打印类产生的方法时触发,只能返回字符串数据
return f'对象:{self.name}'
django单独测试某个功能
-
单独测试django某个功能层
默认不允许单独测试某个py文件
-
如果想要测试某个py文件(主要models.py)
#需要把这个放在最顶上 import os import django os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django07.settings') django.setup() #测试的数据一定要放在他们下面,否则会跟上面报错一样无法正常测试 from user import models models.User.objects.create(name='张三', age=18)
-
django orm底层还是SQL语句我们是可以查看的
如果我们手上是一个QuerySet对象 那么可以直接点query查看SQL语句
但是有的orm提供的方法不是一个QuerySet对象,但是又想看SQL语句那么也可以在配置文件中添加日志记录的
settings.py
配置LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console':{ 'level':'DEBUG', 'class':'logging.StreamHandler', }, }, 'loggers': { 'django.db.backends': { 'handlers': ['console'], 'propagate': True, 'level':'DEBUG', }, } }
ORM常用关键字(方法)
-
创建数据create() 创建数据并直接获取当前创建的数据对象
res = models.User.objects.create(name='张三', age=18) print(res) res = models.User.objects.create(name='李四', age=28) print(res) res = models.User.objects.create(name='jason', age=18) print(res) res = models.User.objects.create(name='tom', age=38) print(res) res = models.User.objects.create(name='jerry', age=88) print(res) res = models.User.objects.create(name='ikun', age=58) print(res)
-
筛选数据filter() 根据条件筛选数据 结果是QuerySet[对象1,对象2,···]
res = models.User.objects.filter() # 不写默认就算查所有字段 print(res) res = models.User.objects.filter(name='jason') # 写了就根据字段名与字段值查找数据 print(res) res = models.User.objects.filter(name='jason',age=18) # 可以写多个筛选条件,默认是and连接 print(res)
-
first() last() QuerySet支持索引取值但是只能只正数 并且ORM不建议我们使用索引
res = models.User.objects.filter() print(res[0]) res = models.User.objects.filter(id=100) print(res[0])
# 无法执行 res = models.User.objects.filter() print(res[-1]) assert ((not isinstance(k, slice) and (k >= 0)) or AssertionError: Negative indexing is not supported.
使用first,与last去索引
res = models.User.objects.filter() print(res.first()) print(res.last()) res = models.User.objects.filter(id=1000) print(res.first()) # 数据不存在索引取值也不会报错 print(res.last())
-
更新数据update (批量更新数据)
models.User.objects.update() # 批量更新数据 res = models.User.objects.filter(id=1).update(name='托马斯') # 手动筛选数据,在更新达到指定更新的效果 print(res) # update返回的是影响行数
-
删除数据delete (批量删除)
# models.User.objects.delete() # 批量删除数据 res = models.User.objects.filter(id=1).delete() # 手动筛选数据,进行删,我们一般都不会进行删除数据只会修改数据字段已达到删除的目的 print(res)
-
查询所有数据all 结果是QuerySet[对象1,对象2]
res = models.User.objects.all() # 查询所有数据django默认查询全部的时候,查21条,需要了在继续查 print(res)
-
获取字段数据values 结果是QuerySet[{},{},{}]
res = models.User.objects.values() # 获取所有数据字段所有值 print(res) print(res.first().get('name')) # 这里又重新查了一般数据库 """ (0.000) SELECT `user_user`.`id`, `user_user`.`name`, `user_user`.`age`, `user_user`.`register_time` FROM `user_user` ORDER BY `user_user`.`id` ASC LIMIT 1; args=() """ res = models.User.objects.all().values('name') # 获取全部数据name字段 print(res) res = models.User.objects.filter(id=4).values('name') # 过滤数据后在获取name字段数据 print(res)
-
values_list 根据指定字段获取数据结果QuerySet[(),(),()]
res = models.User.objects.filter(id=3).values_list('name', 'age') print(res)
-
去重distinct 数据一模一样才可以去重如果有主键肯定不行
mysql数据库不支持,字段去重
此时可以使用distinct(),注意只有在PostgreSQL中支持按字段去重。res = models.User.objects.values('name', 'age').distinct() print(res) """ <QuerySet [{'name': '李四', 'age': 28}, {'name': 'jason', 'age': 18}, {'name': 'tom', 'age': 38}, {'name': 'jerry', 'age': 88}, {'name': 'ikun', 'age': 58}]> """ res = models.User.objects.values('name', 'age').distinct('age') # mysql数据库不支持,字段去重 print(res)
-
排序oredr_by()
res = models.User.objects.all().order_by('age','id') # 默认为升序 print(res) res = models.User.objects.all().order_by('-age','id') # 字段前面加-号就是降序 print(res)
-
get() 根据条件筛选数据并直接获取到对象一旦条件不存在会直接报错 不建议使用
res = models.User.objects.get(pk=2) # 有数据能查到,执行 print(res) res = models.User.objects.get(pk=1) # 没有数据查新报错 print(res)
-
取反操作exclude() 是除了当前指定筛选数据不查询,查询其他的
res = models.User.objects.exclude(pk=2) # 是除了当前指定筛选数据不查询,查询其他的 print(res) res = models.User.objects.all() print(res)
-
reverse() 颠倒顺序(被操作的对象必须是已经排过序的才可以)
res = models.User.objects.all().order_by('id').reverse() # 颠倒排序 print(res) res = models.User.objects.all().order_by('id') # 升序 print(res) res = models.User.objects.all().reverse() # 没有排序颠倒不生效 print(res)
-
count() 统计结果中数据的个数
res = models.User.objects.filter(age=18).count() # 返回值为统计个数 print(res)
-
exists() 判断结果集中是否含有数据 如果有则返回Ture 没有则返回False
res = models.User.objects.all().exists() print(res) res = models.User.objects.filter(pk=100).exists() print(res)
ORM执行SQL语句
有时候ORM操作效率可能片偏低我们可以自己编写SQL语句
方式1:raw()只能查数据
res = models.User.objects.raw('select * from user_user;')
print(res) # 返回一个RawQuerySet对象
print(list(res)) # 拿出结果对象
print(res[0]) # 索引取值
# raw()无法执行增删改操作
# res = models.User.objects.raw('insert into user_user(name,age) values("1",2);')
# res = models.User.objects.raw('delete from user_user where id=8')
# res = models.User.objects.raw('update user_user set name="哈哈哈" where id=8')
# print(res)
方式2
from django.db import connection
import time
ctime = time.strftime("%Y-%m-%d %X")
curosrs = connection.cursor()
curosrs.execute('select * from user_user')
print(curosrs.fetchall())
# curosrs.execute('insert into user_user(name,age,register_time) value ("李李",50,%s)', (ctime,))
curosrs.execute('delete from user_user where id=9')
curosrs.execute('select * from user_user')
print(curosrs.fetchall())
#添加修改添加以及删除数据无需二次确认
神奇的双下划线查询
只要还是Query Set对象就可以无限制的点Query Set对象的方法
返回是Query Set对象的ORM常用方法
filter() # 筛选数据
all() # 查询所有数据
reverse() # 颠倒,必须排序之后才能使用否则不生效
order_by() # 对Query Set里面对象按照指定字段排序
exclude() # 查询除了当前数据的其他数据
values() # 查询所有数据的指定字段的值,为列表套字典
values_list() # 查询所有数据的指定字段的值,为列表套元组
distinct() # 去重数据必须一模一样,如果带主键那么数据肯定不一样,无法去重
__gt
大于
# 查询年龄大于18的用户数据
res = models.User.objects.filter(age__gt=18) # gt大于
print(res)
__lt
小于
# 查询年龄小于38的用户数据
res = models.User.objects.filter(age__lt=38)
print(res)
__gte
大于等于,__lte
小于等于
# 查询年龄大于等于18的用户数据
# 查询年龄小于等于18的用户数据
res = models.User.objects.filter(age__gte=18)
print(res)
res= models.User.objects.filter(age__lte=38)
print(res)
__in
在这个数据集里面
# 查询年龄是18或者28或者38的数据
res = models.User.objects.filter(age__in=(18,28,38))
print(res)
__range
查询范围内的数据
res = models.User.objects.filter(age__range=(18,38))
print(res)
__contains
匹配字符区分大小写
__icontains
匹配字符不区分大小写
res = models.User.objects.filter(name__contains='j')
print(res) # 区分大小写
res = models.User.objects.filter(name__icontains='J')
print(res) # 不区分大小写
__year
,__month
··· 根据时间查
不修改失去,根据日期查询,无法查询到数据
res = models.User.objects.filter(register_time__year=2000)
print(res)
res = models.User.objects.filter(register_time__day=6)
print(res)
settings.py
配置文件修改
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_L10N = True
USE_TZ = False
修改完毕之后正常查询数据
res = models.User.objects.filter(register_time__year=2000)
print(res)
res = models.User.objects.filter(register_time__day=6)
print(res)
res = models.User.objects.filter(register_time__month=6)
print(res)
ORM外键字段的创建
复习MySql外键关系
一对多:外键建在多的一方
多对多:外键需要建在第三张关系表中
一对一:建在任何一方都可以,但是推荐外键建在查询频率较高的一方
外键关系的判断:使用换位思考原则
准备数据
-
创建基础表(书籍表,出版社表,作者表,作者详情表)
-
确定外键关系
一对多 ORM与MySql一致建在多的一方
多对多 ORM比MySQL有更多变化- 外键字段可以直接建在某张表中(查询频率较高的)
内部会自动帮我们创建第三张表 - 自己创建第三表关系并创建外键字段
暂时忽略
一对一 ORM与MySQL一致 外键建在查询频率较高的一方
- 外键字段可以直接建在某张表中(查询频率较高的)
-
在djngo1.x版中默认是级联更新级联删除的,而django2.x以上版默认不是级联更新级联删除的,而如果我们不加的话默认是会报错的
在多对多外键字段中不能添加级联更新级联删除
-
ORM创建
针对一对多和一对一与同步到表中之后会字段会自动加_id的后缀。一对多 # 书籍表与出版社表外键关系 # 一本书只能对应一个出版社,而一个出版社可以对应很多本书书, # 所以书籍表对出版社表是多对一的关系,建在多的一方,也就是书籍表 publish = models.ForeignKey(to='Publsh', on_delete=models.CASCADE) # 在djngo1.x版中默认是级联更新级联删除的, # 而django2.x以上版默认不是级联更新级联删除的,而如果我们不加的话默认是会报错的 一对一 # 作者表与作者详情表外键关系 # 一个作者只能有一个详细信息,而一个详细信息只能有个作者所以是一对一的关系 # 又因为作者表查询频率比较高,所以外键字段建在查询频率较高的一方也就是作者表 authorDetail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE)
针对多对对 不会在表中有展示 而是创建第三张表
多对多 # 书籍表与作者表外键关系 # 一本书可以有多个作者,而一个作者可以写多本书 # 所以书籍表与作者表的关系就是多对多, # 然后,数据表查询频率比作者表查询频率高,所以在ORM中外键字段建在书籍表中 author = models.ManyToManyField(to='Author')
外键字段相关操作
在没有外键表中插入数据
插入出版社数据
odels.Publsh.objects.create(name='北方出版社',address='北方')
models.Publsh.objects.create(name='南方出版社',address='南方')
models.Publsh.objects.create(name='东方出版社',address='东方')
models.Publsh.objects.create(name='西方出版社',address='西方')
插入作者详情表数据
models.AuthorDetail.objects.create(phone=18888888888, address='北京')
models.AuthorDetail.objects.create(phone=17777777777, address='南京')
models.AuthorDetail.objects.create(phone=16666666666, address='上海')
models.AuthorDetail.objects.create(phone=18899999999, address='厦门')
针对一对多 插入数据可以直接填写表中的实际字段
# 针对一对多 插入数据可以直接填写表中的实际字段
book_obj = models.Book.objects.create(title='水浒传', price=888.11, publish_id=1)
# 直接添加数据需要指定表中的字段,外键字段会自动加_id所以在添加的时候字段名字为publish_id=1
print(book_obj)
book_obj = models.Book.objects.create(title='三国演义', price=666.88, publish_id=1)
print(book_obj)
book_obj = models.Book.objects.create(title='斗罗大陆', price=8888.88, publish_id=2)
print(book_obj)
book_obj = models.Book.objects.create(title='斗破苍穹', price=6666.66, publish_id=3)
print(book_obj)
book_obj = models.Book.objects.create(title='三体', price=555.89, publish_id=4)
print(book_obj)
#
# 针对一对多 插入数据也可以填写表中的类中字段名
publish_obj = models.Publsh.objects.filter(pk=1).first()
models.Book.objects.create(title='金瓶', price=99999.99, publish=publish_obj)
## 使用对象进行数据插入要使用类中的字段名进行添加
# 书籍对象:水浒传
# 书籍对象:三国演义
# 书籍对象:斗罗大陆
# 书籍对象:斗破苍穹
# 书籍对象:三体
# 书籍对象:金瓶
针对一对一与一对多一致,既可以传数字也可以传对象
models.Author.objects.create(name='jason', age=18, authorDetail_id=1)
# 直接添加数据需要指定表中的字段,外键字段会自动加_id所以在添加的时候字段名字为authorDetail_id=1
models.Author.objects.create(name='tom', age=28, authorDetail_id=2)
models.Author.objects.create(name='张三', age=58, authorDetail_id=4)
authordetail_obj = models.AuthorDetail.objects.filter(pk=3).first()
models.Author.objects.create(name='kunkun', age=38, authorDetail=authordetail_obj)
## 使用对象进行数据插入要使用类中的字段名authorDetail进行添加
## 一对一数据修改
author_qeury.update(name=name,age=age)
aud = author_qeury.first().authorDetail
aud.addr=addr
aud.phone=phone
aud.email=email
aud.save()
针对多对多关系绑定
# 增加数据
book_obj = models.Book.objects.filter(pk=1).first()
book_obj.authors.add(1)
book_obj.authors.add(4,3)
book_obj = models.Book.objects.filter(pk=2).first()
author_obj1 = models.Author.objects.filter(pk=1).first()
author_obj2 = models.Author.objects.filter(pk=3).first()
book_obj.authors.add(author_obj1, author_obj2)
# 修改数据
book_obj = models.Book.objects.filter(pk=4).first()
book_obj.authors.set((1, 3))
book_obj = models.Book.objects.filter(pk=4).first()
author_obj = models.Author.objects.filter(pk=3).first()
book_obj.authors.set([author_obj, ])
# 删除数据
book_obj = models.Book.objects.filter(pk=3).first()
# book_obj.authors.remove(3)
# 与下面同理
author_obj = models.Author.objects.filter(pk=3).first()
book_obj.authors.remove(author_obj)
# 清空当前数据对象的关系
bokk_obj.authors.clear()
add()/remove() 多个位置参数(数字,对象)
set() 可迭代对象(元组 列表)数字 对象
clear() 清空当前数据对象的关系
ORM跨表查询
复习MySql 跨表查询的思路
子查询 分步操作:将一条Sql语句当作另一条SQL语句查询条件
连表操作:先整合多张表之后基于单表查询即可
inner join 内连接 基于两个表中共有的数据字段展示
left join 左连接 以左表为基准展示数据
right join 右连接 以右表为基准展示数据
正反向查询的概念(重要)
正向查询
由外键字段所在的表数据查询为关联表的表数据 正向
反向查询
没有外键字段的表数据查询关联的表数据 反向
正反向的核心就看外键字段在不在当前数据所在的表中
ORM跨表查询的口诀(重要)
正向查询按外键字段
反向查询按表名小写
基于对象的跨表查询
1.查询主键为1的书籍对应的出版社名称(正向查询)
book_obj = models.Book.objects.filter(pk=1).first()
print(book_obj.publish.name)
2.查询主键为4的书籍对应的作者姓名(正向查询)
book_obj = models.Book.objects.filter(pk=3).first()
print(book_obj.authors) # User.Author.None 是多对多字段,看见这个不要慌,里正确步骤就只差一步,
print(book_obj.authors.all()) #就把所以数据拿出来 <QuerySet [<Author: 作者对象:tom>]>
3.查询jason的电话号码(正向查询)
# 先查出jason对象。因为外键字段在当前表,所是正向查询
author_obj = models.Author.objects.filter(name='jason').first()
# 因为是一对一字段,用对象点外键字段可以直接获取数据
print(author_obj.authorDetail.phone)
4.查询北方出版社出版过的书籍(反向查询)
publish_obj = models.Publsh.objects.filter(name='北方出版社').first()
# 外键字段不在当前表,所是反向查询
# 反向查询是对象点类名小写,出版社是一对多关系所以用boot_set
print(publish_obj.book_set)
print(publish_obj.book_set.all())
5.查询jason写过的书籍(反向查询)
author_obj = models.Author.objects.filter(name='jason').first()
print(author_obj.book_set)
print(author_obj.book_set.all().values('title'))
6.查询电话号码是18888888888的作者姓名(反向查询)
authordetail = models.AuthorDetail.objects.filter(phone=18888888888).first()
# 一对一 结果是精准的类名不用加_set
print(authordetail.author.name)
总结
如果用结果对象,无论是正向还是反向,如果站在当前对象表对应跨到的查询表,当前对象可能有多个结果数据的情况下那么需要使用all()方法来取数据不能直接点类表中的字段拿数据,否则就可以使用点类表中的字段形式获取数据
基于双下划线的跨表查询
1.查询主键为1的书籍对应的出版社名称(正向查询)
# 1.查询主键为1的书籍对应的出版社名称
# 先查询到主键为1的书籍,正向查询,直接点values('外键字段__要查询的关联表中的字段','因为手中本来就是书籍表所以可以直接查询当前表中的字段')
res = models.Book.objects.filter(pk=1).values('publish__name','title')
print(res)
2.查询主键为1的书籍对应的作者姓名(正向查询)
res = models.Book.objects.filter(pk=1).values('authors__name','title')
print(res)
3.查询jason的电话号码(正向查询)
res = models.Author.objects.filter(name='jason').values('authorDetail__phone','name')
print(res)
4.查询北方出版社出版过的书籍(反向查询)
res = models.Publsh.objects.filter(name='北方出版社').values('book__title')
print(res)
5.查询jason写过的书籍(反向查询)
res = models.Author.objects.filter(name='jason').values('book__title')
print(res)
6.查询电话号码是18888888888的作者姓名(反向查询)
res = models.AuthorDetail.objects.filter(phone=18888888888).values('author__name')
print(res)
进阶查询
1.查询主键为1的书籍对应的出版社名称
# 用后面的表对前面条件进行过滤,然后再直接拿后面表要拿的数据
res = models.Publsh.objects.filter(book__id=1).values('name')
print(res)
2.查询主键为3的书籍对应的作者姓名
res= models.Author.objects.filter(book__pk=3).values('name')
print(res)
3.查询jason的电话号码
res= models.AuthorDetail.objects.filter(author__name='jason').values('phone')
print(res)
4.查询北方出版社出版过的书籍
res= models.Book.objects.filter(publish__name='北方出版社').values('title')
print(res)
5.查询jason写过的书籍
res = models.Book.objects.filter(authors__name='jason').values('title')
print(res)
6.查询电话号码是18888888888的作者姓名
res = models.Author.objects.filter(authorDetail__phone=18888888888).values('name')
print(res)
补充(各种姿势查)
查询主键为3的书籍对应的作者的电话号码(第一张表为开头)
res = models.Book.objects.filter(pk=3).values('authors__authorDetail__phone')
print(res)
查询主键为3的书籍对应的作者的电话号码(第二张表为开头)
res = models.Author.objects.filter(book__pk=3).values('authorDetail__phone')
print(res)
查询主键为3的书籍对应的作者的电话号码(第三张表为开头)
#从哪个表开始从哪跨表,必须连续跳,不能隔表跳
res = models.AuthorDetail.objects.filter(author__book__pk=3).values('phone')
print(res)
补充总结
从哪个表开始从哪跨表,必须连续跳,不能隔表跳
获取每个组的最新信息
再有外键的表先用外键跨到需要用到的表,然后再用表名跨回来有外键的表进行分组,条件查询,最后进行过滤
# 获取每个站点用户的最新发布的文章
user_query=models.Article.objects.annotate(max_pk=Max('site__article__pk')).filter(pk=F('max_pk'))
print(user_query)
print(len(user_query))
聚合查询
常见聚合函数
max
取最大值
min
取最小值
count
统计总数
sum
求和
avg
求平均值
再ORM中支持单独使用聚合函数 aggregate
# 要先到导入模块才能使用
from django.db.models import Max, Min, Sum, Count, Avg
res = models.Book.objects.aggregate(最大价格=Max('price'), 最小主键=Min('pk'), total_price=Sum('price'),
total_num=Count('pk'), 平均值=Avg('price'))
print(res)
分组查询
分组查询关键字annotate
from django.db.models import Max, Min, Sum, Count, Avg
res = models.Publsh.objects.annotate(Count('name'))
print(res)
修改数据的sql_mode把only-full-group_by
去掉
修改后就能正常查询了
统计每一本书的作者个数
# 首先以Book书为分组,外键再手中,是正向查询
# 然后再用Count统计外键字段authors人数
# 最后再用values拿出统计的人数
res = models.Book.objects.annotate(aut_num =Count('authors')).values('title','aut_num')
print(res)
统计出每个出版社卖的最便宜的书的价格
#只有对象才会查询才会用到_set
res = models.Publsh.objects.annotate(price_num=Min('book__price')).values('name','price_num')
print(res)
统计不止一个作者的图书
# 首先根据书籍分组,统计各个图书作者个数,
# 然后过滤作者大于1的图书
# 拿出图书名称
res = models.Book.objects.annotate(aut_num=Count('authors')).filter(aut_num__gt=1).values('title','aut_num')
print(res)
查询每个作者出的书的总价格
res = models.Author.objects.annotate(aut_num=Count('book__pk'), sum_num=Sum('book__price')).values('aut_num', 'sum_num','name')
print(res)
models.表名.objects.annotate() # 按照表分组
models.表名.objects.values('字段名').annotate() # 按照values括号内的指定的字段分组
#查询每个作者出的书的总价格按字段分组,查询
res = models.Author.objects.values('pk').annotate(aut_num=Count('book__pk'),sum_num=Sum('book__price')).values('aut_num','sum_num','name')
print(res)
F与Q查询
1.查询库存数大于卖出数的书籍
from django.db.models import F
res = models.Book.objects.filter(kucun__gt=F('sale'))
print(res)
2.将所有书的价格涨800
from django.db.models import F
res = models.Book.objects.update(price=F('price')+800)
print(res)
3.将所有书的名称后面追加爆款
from django.db.models import F, Value
from django.db.models.functions import Concat
res = models.Book.objects.update(title=Concat(F('title'), Value('爆款')))
print(res)
4.查询主键是1或者价格大于2000的书籍
# 查询主键是1或者价格大于2000的书籍
from django.db.models import F, Q, Value # 与或非条件的时使用Q查询
from django.db.models.functions import Concat
res = models.Book.objects.filter(Q(pk=1) | Q(price__gt=2000))
print(res)
# 查询主键不是1或者价格不大于2000的书籍 # ~是not &是and |是or
res = models.Book.objects.filter(~Q(pk=1) | ~Q(price__gt=2000))
print(res)
总结F查询与Q查询
F查询针对与,需要再原有基础上做数据更改而使用的
Q查询针对与,再过条件中需要使用& and 与
,| or 或
,~ not 非
使用的
Concat
是在F查询
做字符串拼接
的时候使用