第三章--django--ORM 相关操作
- ORM (数据库)
- 数据库配置操作步骤
- 创建数据库表
- 数据表相关操作
- 新增字段
- 修改原字段
- 表和表进行关联
- 表数据操作
- 增加数据
- 查询数据
- 修改数据
- 删除数据
- 跨表读取字段方式
- 其他条件查询方式
- 条件 不等于(exclude() )
- 去重 distinct()
- 排序 order_by(字段)
- reverse 倒序
- 分组 annotate(name=规则)
- having 用法
- query 输出 sql 语句
- only 取指定字段
- F 字段列自增数值
- Q 组合条件查询方式
- extra 额外的查询条件
- 用原 SQL 直接进行查询
- raw 执行原生SQL
- using 指定数据库取数据
- select_related 一次性获取关联的数据
- prefetch_related
ORM (数据库)
Django的ORM操作本质上会根据对接的数据库引擎,翻译成对应的sql语句;所有使用Django开发的项目无需关心程序底层使用的是MySQL、Oracle、sqlite等等;如果数据库迁移,只需要更换Django的数据库引擎即可。
ORM 默认使用的数据库:SQLlite
ORM核心功能:
? 1.操作表(创建表、修改表(新增表字段、修改表字段)、删除表)
? 2.操作表数据行(增删改查)
推荐博客:
数据库配置操作步骤
a. 以MySQL数据库为案例介绍,本地或服务器上已成功安装MySQL并创建需要的数据库(djangot)。
b. 修改数据库配置文件:
- 打开
mysite/settings.py,找到DATABASES进行以下内容修改:
DATABASES = {
'default': {
# 修改连接MySQL
'ENGINE': 'django.db.backends.mysql', # 数据库类型
'NAME': 'djangot', # 要连接的数据库名称
'USER': 'root', # 登录账号
'PASSWORD': '', # 登录密码
'HOST': 'localhost', # 连接地址(IP)
'PORT': 3306, # 端口,默认3306
}
}
ENGINE可选值:? 'django.db.backends.sqlite3'
? 'django.db.backends.postgresql'
? 'django.db.backends.mysql'
? 'django.db.backends.oracle'
c. 在与工程同名的目录下[mysite\mysite\],在__init__.py文件添加以下内容:
将Django默认连接的MySQLdb,改为pymysql。
import pymysql # 导入MySQL模块,第三方库需提前安装
pymysql.install_as_MySQLdb()
创建数据库表
a. 在应用app01目录下的 model.py 文件中写入需要创建的相关表结构(不包含数据),比如以下示例:
注意:类必须继承 models.Model;在Django的ORM中“类”代表数据库中表,对象(object)代表数据表中的一行数据,对象中包含每个字段。
# app01\model.py
from django.db import models
# 类名==表名
class UserInfo(models.Model): # 必须继承 models.Model
# nid字段,可不写,django默认会自动添加字段(id)并加自增主键
nid = models.BigAutoField(primary_key=True) # primary_key==主键 自增
name = models.CharField(max_length=32) # max_length==最大长度
class UserGroup(models.Model):
title = models.CharField(max_length=32)
每个class被表示为 django.db.models.Model 类的子类。class名就等于数据库表名;每个class有一些类变量,它们都表示模型里的一个数据库字段。
b. 将上面写的表,在数据库中进行创建,需执行以下两个命令:
注意:需在工程目录下执行命令
老版本:
D:\mysite $ python manage.py syncdb
Django 1.7.1 及以上的版本需要用以下命令:
D:\mysite $ python manage.py makemigrations # 根据app下的[migrations]目录中的记录,检测当前[model]层代码是否发生变化
D:\mysite $ python manage.py migrate # 把ORM代码转换成SQL语句去数据库执行
以上命令执行成功后,查看对应的数据库会创建UserInfo和UserGroup两个表。
D:\mysite $ python manage.py migrate
Operations to perform:
Apply all migrations: admin, app01, auth, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying app01.0001_initial... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying auth.0009_alter_user_last_name_max_length... OK
Applying sessions.0001_initial... OK
显示以上信息表示执行成功,请查看对应数据库。
- models常用字段类型:
| 方法 | 说明 |
|---|---|
| BigAutoField | bigint自增列,必须填入参数 primary_key=True |
| AutoField | int自增列,必须填入参数 primary_key=True |
| CharField | 字符类型,必加参数:max_length |
| IntegerField | 整数列(有符号的) -2147483648 ~ 2147483647 |
| PositiveIntegerField | 正整数 0 ~ 2147483647 |
| BigIntegerField | 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807 |
| FloatField | 浮点型 |
| ForeignKey | 用于关联表 |
| TextField | 文本类型 |
| DateTimeField | 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] |
| DateField | 日期格式 YYYY-MM-DD |
| TimeField | 时间格式 HH:MM[:ss[.uuuuuu]] |
ForeignKey ( to, on_delete, **options )
有两个必选的参数 :
第一个参数:要关联的表(主表),在默认的情况下,外键储存的是主表的主键(Primary key)。
第二个参数:models.CASCADE,当主表的字段被删除时,和他有关的子表字段也会被删除,还有models.PROTECT(返回错误提示,阻止删除),models.SET_NULL(用null替代),models.SET_DEFAULT(用默认值替代),或者用models.SET()自定义。
可选参数,下面介绍较为常用的几个:
1、to_field: 设置关联到主表的字段,例如:models.ForeignKey('tablename', to_field=tablename.字段名)
注意:关联字段的内容必须是不重复的。在默认情况下,Django 关联到的字段是主表的主键。
2、related_name:自定义一个名称,用于反向查询
注意:当一张子表里,多个foreignkey指向同一个主表,related_name必须设置。
- 建表字段常用参数说明:
| 参数 | 说明 |
|---|---|
| primary_key | True表示设置主键,默认False |
| null | True表示可以为空,默认False |
| default | 设置默认值,NOT_PROVIDED(默认不提供) |
| db_column | 数据库中字段的列名 |
| db_index | 数据库中字段是否可以建立索引 |
| unique | 数据库中字段是否可以建立唯一索引 |
| unique_for_date | 数据库中字段【日期】部分是否可以建立唯一索引 |
| unique_for_month | 数据库中字段【月】部分是否可以建立唯一索引 |
| unique_for_year | 数据库中字段【年】部分是否可以建立唯一索引 |
c. 给表返回的对象加显示内容:
class UserInfo(models.Model):
nid = models.BigAutoField(primary_key=True)
name = models.CharField(max_length=32)
def __str__(self):
# 设置访问对象时,显示的字段内容
return self.name
# 不加 __str__ 情况:
obj02 = models.UserInfo.objects.all()
print(obj02) # , ]>
# 加 __str__ 情况:
obj02 = models.UserInfo.objects.all()
print(obj02) # , ]>
数据表相关操作
新增字段
如果对已创建的表想要新增字段时,只需要在原有的基础上直接添加即可,以UserGroup表为案例,新增一个name字段:
注意:因为原表已有字段是有数据的,所以新增的字段必须加默认值或为空设置。
# app01\model.py
class UserGroup(models.Model):
title = models.CharField(max_length=32)
name = models.CharField(max_length=64, null=True) # 新增部分
代码添加完成后执行以下两个命令:
$ python manage.py makemigrations
Migrations for 'app01':
app01\migrations\0002_UserGroup_name.py
- Add field name to UserGroup
$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, app01, auth, contenttypes, sessions
Running migrations:
Applying app01.0002_test01_name... OK
显示以上信息表示执行成功,请查看数据库对应字段已添加上。
修改原字段
如果对已创建的表中某个字段名想要修改时,以UserGroup表为案例,比如修改原字段name改为username,只需要在原有的基础上直接修改:
class UserGroup(models.Model):
title = models.CharField(max_length=32)
# name = models.CharField(max_length=64, null=True) # 原来的
username = models.CharField(max_length=64, null=True) # 修改的,只修改变量名别的不动
代码添加完成后执行以下两个命令:
$ python manage.py makemigrations
# 以下的意思是说明:是否将name重命名为username,输入 y 表示同意,N 表示退出
Did you rename UserGroup.name to UserGroup.username (a CharField)? [y/N] y
Migrations for 'app01':
app01\migrations\0003_auto_20211109_1703.py
- Rename field name on UserGroup to username
$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, app01, auth, contenttypes, sessions
Running migrations:
Applying app01.0003_auto_20211109_1703... OK
显示以上信息表示执行成功,请查看数据库对应字段已修改。
表和表进行关联
如果想让表与表之间进行关联,只需在其中一个表中新增一个ForeignKey类型的字段,以下案例详细说明。
- UserInfo表关联UserGroup表(A -> B)
注意:在原有表进行增加字段时,需要给ForeignKey设置为空(null=True)情况。数据库加关联字段时会自动给变量(ut)多加个”_id“,数据库表中字段为:ut_id
ut 代表关联表中的一行数据的对象,默认与主键关联;
关联方式类似于SQL的 JOIN ... on A.ut=B.id,比如UserInfo表中"ut"代表职位id,将于UserGroup表中对应的职位id对应关联。
# app01\model.py
from django.db import models
class UserInfo(models.Model):
name = models.CharField(max_length=32)
# 关联表:UserInfo -> UserGroup
ut = models.ForeignKey('UserGroup', on_delete=models.CASCADE, null=True)
class UserGroup(models.Model):
title = models.CharField(max_length=32)
查询跨表数据方式,注意 ut 在数据表中的区别
# app01\views.py
from app01 import models
def text(request):
userinfo = models.UserInfo.objects.all()
for obj in userinfo:
print(obj.username, obj.password, obj.ut_id, obj.ut.title)
# obj.ut_id 将获取UserGroup表中的 id 数据,obj.ut.title将获取UserGroup表中的 title 数据
- 正向关联
以跨表查询数据(A -> B -> C)案例
以下案例中,实现三个表进行关联
# app01\model.py
from django.db import models
class UserInfo(models.Model):
name = models.CharField(max_length=32)
ut = models.ForeignKey('UserGroup', on_delete=models.CASCADE, null=True)
class UserGroup(models.Model):
title = models.CharField(max_length=32)
ug = models.ForeignKey('Test01', on_delete=models.CASCADE, null=True)
class Test01(models.Model):
title = models.CharField(max_length=32)
name = models.CharField(max_length=64, null=True)
查询跨表数据方式,注意 ut 在数据表中的区别
# app01\views.py
from app01 import models
def text(request):
userinfo = models.UserInfo.objects.all()
for obj in userinfo:
print(obj.ut.ug.title)
# obj.ut.usergroup.ug 将获取Test01表中的 id 数据;
# 执行顺序是先通过ut跨UserGroup表,再通过UserGroup表中的ug跨到Test01表,获取Test01表中的字段数据
- 反向关联
被ForeignKey关联的表,该函数都会隐含 table(小写表名)_set 字段,通过隐含 table_set.all() 方法可以获取该表中的所有数据。table_set.all() 方法结果返回的是 QuerySet对象 list。
以跨表查询数据(A <- B)案例
通过 UserType 表中的隐含方法,来获取 UserInfo 表中的字段数据:
# app01\model.py
from django.db import models
class UserInfo(models.Model):
name = models.CharField(max_length=32)
ut = models.ForeignKey('UserGroup', on_delete=models.CASCADE, null=True)
class UserGroup(models.Model):
title = models.CharField(max_length=32)
# app01\views.py
from app01 import models
def text(request):
obj = models.UserGroup.objects.all().first() # 只取一行数据
for item in obj.userinfo_set.all():
print(item.name, item.id)
表数据操作
案例中的相关表结构:
# app01\model.py
from django.db import models
class UserInfo(models.Model):
username = models.CharField(max_length=32)
password = models.CharField(max_length=64)
ut = models.ForeignKey('UserGroup', on_delete=models.CASCADE, null=True)
class UserGroup(models.Model):
title = models.CharField(max_length=32)
增加数据
- create() 新增数据(单表操作)
# app01\views.py
from app01 import models
def text(request):
''' 增加数据 '''
models.UserType.objects.create(title='开发部') # 往UserType表增加一行数据
models.UserInfo.objects.create(username='root', password=123, ut_id=1) # UserInfo表增加一行数据
return HttpResponse('......')
以上函数执行完后,数据库中就新增一条数据:


- bulk_create 批量创建数据
# bulk_create(self, objs, batch_size=None)
obj = [ models.Test01(title='xx1'), # 创建的对象,还没有执行到数据库
models.Test01(title='xx2'), ]
models.Test01.objects.bulk_create(obj, 10) # obj=数据对象 10=每次操作条数,上限999
- get_or_create
如果存在,则获取,否则,创建
defaults 指定创建时,其他字段的值
# get_or_create(self, defaults=None, **kwargs)
obj, created = models.UserInfo.objects.get_or_create(username='root1', defaults={'password': '1234','ut_id': 3, 'age': 18})
或
obj, created = models.UserInfo.objects.get_or_create(
username='root1', password= '1234', # 支持多个
defaults={'ut_id': 3, 'age': 18})
# 先在UserInfo表中查username字段数据为root1的,如果存在,则返回数据;如果不存在,则新创建数据。
# obj==对象 created==True/False
- update_or_create
如果存在,则更新,否则,创建
defaults 指定创建时或更新时的其他字段
obj, created = models.UserInfo.objects.update_or_create(username='root1', defaults={'password': '1234','ut_id': 3, 'age': 18})
查询数据
- all() 查询表中所有数据(对象)(单表操作)
# app01\views.py
from app01 import models
def text(request):
''' 查询数据 '''
usertype_obj_list = models.UserType.objects.all() # 返回QuerySet类型,类似list->对象
print(usertype_obj_list) # ]>
# 获取其中具体字段的数据
for row in usertype_obj_list:
print(row.id, row.title) # 1 开发部
return HttpResponse('......')
- filter() 条件筛选(where),传入多条件表示:and(单表操作)
# app01\views.py
from app01 import models
def text(request):
# 只查询字段id=1的数据
obj_list01 = models.UserType.objects.filter(id=1)
# 多条件默认为and
obj_list02 = models.UserType.objects.filter(id=1, title='开发部')
# 只查询字段id>1的数据
obj_list03 = models.UserType.objects.filter(id__gt=1)
# 跨表方式
obj_list04 = models.UserType.objects.filter(ut__title='开发部')
print(obj_list04.ut.title) # 获取具体数据
return HttpResponse('......')
# 传多个条件,用字典方式:
com = {'id':1, 'title':"开发部"}
models.UserType.objects.filter(**com)
-
[组合条件查询 Q方式](#Q 组合条件查询方式)
-
其他筛选条件方式:
| 参数 | 说明 |
|---|---|
| __gt | 大于 eq: id__gt=1 |
| __lt | 小于 eq: id__lt=5 |
| __gte | 大于等于 eq: id__gte=1 |
| __lte | 小于等于 eq: id__lte=5 |
| __in | 在...范围内 eq: id__in = [1,2,3] |
| __range | 在...范围内 eq: id__range = [1,2,3] |
| __exact | 精确等于 like 'aaa' |
| __iexact | 精确等于 忽略大小写 ilike 'aaa' |
| __contains | 包含 like '%aaa%' |
| __icontains | 包含 忽略大小写 ilike '%aaa%',但是对于sqlite来说,contains的作用效果等同于icontains |
| __startswith | 以…开头 |
| __endswith | 以…结尾 |
| __istartswith | 以…开头 忽略大小写 |
| __iendswith | 以…结尾,忽略大小写 |
| __year | 日期字段的年份 |
| __month | 日期字段的月份 |
| __day | 日期字段的日 |
| __week_day | 日期字段的周 |
| __isnull | 是否为空 eq: __isnull=True/False |
| __regex | 正则匹配,Entry.objects.get(title__regex=r'^(An?The) +' ) |
| __iregex | 正则不区分大小写,Entry.objects.get(title__iregex=r'^(an?|the)+' ) |
- values() 取指定某字段列的数据,返回的是列表嵌套字典
# app01\views.py
from app01 import models
def text(request):
res = models.UserInfo.objects.values("username")
print(res) #>
print(list(res)) #> [{'username': 'root'}, {'username': 'test1'}]
- values_list() 取指定某字段列的数据,返回的是列表嵌套元组
# app01\views.py
from app01 import models
def text(request):
res = models.UserInfo.objects.values_list("username")
print(res) #>
print(list(res)) #> [('root',), ('test1',)]
- values()和values_list() 跨表查询数据方式
注意: 通过ForeignKey的变量加“双下划线”加跨表的字段(ut__title)方式,进行跨表查询
# app01\views.py
from app01 import models
def text(request):
res = models.UserInfo.objects.values("username", "ut__title")
obj = models.UserInfo.objects.values_list("username", "ut__title")
print(obj) #>
print(list(obj)) #> [('root', '开发部'), ('test1', '开发部')]
- first() 取第一行数据
# app01\views.py
from app01 import models
def text(request):
obj = models.UserInfo.objects.first() # 返回对象
print(obj, obj.username)
- count() 计算总行数
# app01\views.py
from app01 import models
def text(request):
number = models.UserInfo.objects.count() # 返回 int 类型
print(number)
- 索引方式[ : ]
# app01\views.py
from app01 import models
def text(request):
obj = models.UserInfo.objects.all()[1:5] # 返回 对象
print(obj)
- in_bulk 根据主键进行查询
# 类似于 in条件查询
models.UserInfo.objects.filter(id__in=[1,2,3])
# 上下同理
models.UserInfo.objects.in_bulk([1,2,3])
- last 取最后一个数据
# app01\views.py
from app01 import models
def text(request):
obj = models.UserInfo.objects.last() # 返回对象
print(obj, obj.username)
修改数据
- update() 更新指定数据(单表操作)
# app01\views.py
from app01 import models
def text(request):
''' 更新数据 '''
# 查询条件id=1的数据,更新指定字段
models.UserType.objects.filter(id=1).update(title='分析部')
return HttpResponse('......')
- [更新一列数据方式 F方式](#F 字段列自增数值)
删除数据
- delete() 删除指定数据(单表操作)
# app01\views.py
from app01 import models
def text(request):
''' 删除数据 '''
# 删除查询条件字段id=2的数据
models.UserType.objects.filter(id=2).delete()
return HttpResponse('......')
跨表读取字段方式
以 UserInfo表和 UserGroup表为案例,讲解读取跨表字段情况:
# app01\model.py
from django.db import models
class UserInfo(models.Model):
name = models.CharField(max_length=32)
ut = models.ForeignKey('UserGroup', on_delete=models.CASCADE, null=True)
class UserGroup(models.Model):
title = models.CharField(max_length=32)
- 正向获取表字段数据(UserInfo left join UserType)
# 1. 通过 ForeignKey 类型字段获取数据
obj01 = UserInfo.objects.first() # 返回对象
print(obj01.ut.title) # 返回具体字段的数据
# 2. 通过双下划线(__) 获取数据
obj02 = UserInfo.objects.values('ut_id', 'ut__title') # 返回对象
print(list(obj02)) # 返回[{},{}]
obj03 = UserInfo.objects.values_list('ut_id', 'ut__title') # 返回对象
print(list(obj03)) # 返回[(),()]
- 反向获取表字段数据(UserType left join UserInfo)
# 1. 通过 小写表名_set 取跨表的数据
obj01 = UserGroup.objects.first() # 返回对象
res = obj01.userinfo_set.all() # 返回 userinfo表的对象[userinfo对象,userinfo对象]
print(res[0].username) # 返回具体字段数据
# 2. 通过 小写表名 和 表名+双下划线(__) 方式获取跨表字段数据
obj02 = UserGroup.objects.values('userinfo', 'userinfo__name') # 返回对象
print(list(obj02)) # 返回[{},{}]
obj03 = UserGroup.objects.values_list('userinfo', 'userinfo__name') # 返回对象
print(list(obj03)) # 返回[(),()]
其他条件查询方式
条件 不等于(exclude() )
obj03 = models.UserInfo.objects.values('ut_id').exclude(id=1)
print(obj03.query)
# SELECT `app01_userinfo`.`ut_id` FROM `app01_userinfo` WHERE NOT (`app01_userinfo`.`id` = 1)
去重 distinct()
注意:只有PostgreSQL 数据库中,distinct才能传参数,进行去重操作。
连 mysql 或 sqlite 数据库中,distinct不能传参数。
obj = models.UserInfo.objects.values('ut_id').distinct()
print(obj.query)
# SELECT DISTINCT `app01_userinfo`.`ut_id` FROM `app01_userinfo`
排序 order_by(字段)
obj1 = obj01.userinfo_set.all().order_by('id') # 表示按 id 从小到大排序
# 加减号(-)表示方向排序
obj2 = obj01.userinfo_set.all().order_by('-id') # 表示按 id 从大到小排序
# 多字段排序
obj3 = obj01.userinfo_set.all().order_by('id', '-name') # 先按id排序,在按name排序
reverse 倒序
只能和 order by 一起使用才会生效。
如果存在order_by,reverse则为倒序,如果多个排序规则一一倒序
models.UserInfo.objects.all().order_by('-nid', 'age')
# 以上排序方式: 先nid从大到小,后age从小到大
models.UserInfo.objects.all().order_by('-nid', 'age').reverse()
# 以上排序方式: 先nid从小到大,后age从大到小
分组 annotate(name=规则)
from django.db.models import Count, Min, Max, Sum
obj02 = models.UserInfo.objects.values('ut_id').annotate(f=Count('id'))
print(obg.query)
# SELECT `app01_userinfo`.`ut_id`, COUNT(`app01_userinfo`.`id`) AS `f` FROM `app01_userinfo` GROUP BY `app01_userinfo`.`id` ORDER BY NULL
having 用法
obj02 = models.UserInfo.objects.values('ut_id').annotate(ff=Count('id')).filter(ff__lt=10)
print(obj02.query)
# SELECT `app01_userinfo`.`ut_id`, COUNT(`app01_userinfo`.`id`) AS `ff` FROM `app01_userinfo` GROUP BY `app01_userinfo`.`ut_id` HAVING COUNT(`app01_userinfo`.`id`) < 10 ORDER BY NULL
query 输出 sql 语句
obj = UserGroup.objects.all()
print(obg.query) # 返回生成的 SQL 语句
only 取指定字段
注意:主键写不写都会取数据
models.UserInfo.objects.only('username','id')
或
models.UserInfo.objects.filter(...).only('username','id')
或
models.UserInfo.objects.all().only('username').extra( select={'nid':1,} )
F 字段列自增数值
- F("字段名") 更新时取原来的数据
比如给 UserInfo 表中的 age 字段列所有数据自增 +1
from django.db.models import F
obj = models.UserInfo.objects.all().update(age=F("age")+1)
Q 组合条件查询方式
- Q() 多条件查询
用于filter()多个条件时的组合,还可以加其他条件
from django.db.models import Q
# 方式一:
models.UserInfo.objects.filter(Q(id=1)) # Q(id=1) 等价于 id=1, id为数据表中的字段
models.UserInfo.objects.filter(Q(id=1) | Q(id__gt=2)) # | 表示 or
models.UserInfo.objects.filter(Q(id=1) & Q(id=2)) # & 表示 and
# 方式二:
q1 = Q() # 创建对象
q1.connector = 'OR' # 字段与字段的条件规则 等价于 id=1 or id=9
q1.children.append(('id', 1)) # 字段添加到 q1 对象中, id为数据表中的字段
q1.children.append(('id__gt', 9))
q2 = Q()
q2.connector = 'OR' # 等价于 c1=1 or c1=10
q2.children.append(('c1', 1)) # c1为数据表中的字段
q2.children.append(('c1__gt', 10))
con = Q() # 将q1和q2合并为一个大的条件
con.add(q1, 'AND')
con.add(q2, 'AND') # con 等价于 (id=1 or id=9) and (c1=1 or c1=10)
models.UserInfo.objects.filter(con) # 传入条件
# 案例,比如格式: (id=1 or id=2 or (name='xx' and name='yy'))
a1 = Q()
a1.connector = 'OR'
a1.children.append(('id', 1))
a1.children.append(('id__gt', 2))
a2 = Q()
a2.connector = 'AND'
a2.children.append(('name', 'xx'))
a2.children.append(('name', 'yy'))
a1.add(a2, 'OR') # 将a2的条件添加到a1中
# 案例,比如格式: 自动将字典转换为条件格式
comon_dict = {
'a': [1,2,3],
'b': [4,5],
'c': [6]
}
con = Q()
for k,v in comon_dict.items():
q = Q()
q.connector = 'OR'
for i in v:
q.children.append((k, i)) # 这里的'k'对应数据表的字段
con.add(q, 'AND')
models.UserInfo.objects.filter(con)
extra 额外的查询条件
models.表.objects.extra(
select={}, select_params=[], # 映射
where=[], params=[], # 条件
order_by=[], # 排序
tables=[]) # 表
参数用法:
select={}, select_params=[]
# 作用于: select 此处 from 表
where=[], params=[]
# 作用于: select * from 表 where 此处
示例: where=["id=1 or id=2", "age>10"] # 参数之间是用 and 连接
tables=[] # 参数对应数据库中的表名
# 作用于: select * from 表, 此处
order_by=[],
# 作用于: select * from 表 order by 此处
# 示例:
obj = models.UserInfo.objects.extra(
select={'new1': 'select count(1) from app01_usertype where id > %s',
'new2': 'select count(1) from app01_test01 where id > %s', },
select_params=[1, 2],
where=['age>=%s'], params=[2],
order_by=['-age'],
tables=['app01_usertype'])
print(obj.query)
"""SELECT (select count(1) from app01_usertype where id > 1) AS `new1`,
(select count(1) from app01_test01 where id > 2) AS `new2`,
`app01_userinfo`.`id`, `app01_userinfo`.`username`, `app01_userinfo`.`password`, `app01_userinfo`.`age`, `app01_userinfo`.`ut_id` FROM `app01_userinfo` , `app01_usertype` WHERE (age>=2) ORDER BY `app01_userinfo`.`age` DESC
"""
用原 SQL 直接进行查询
将原SQL语句转换为models类型进行相关查询操作,导入方式: from django.db import connection, connections,内部会自动进行数据库连接。
在配置文件“setting.py”中的DATABASES配置多个数据库:
# setting.py 配置两个数据库
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db1.sqlite3'),
},
'db2': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db2.sqlite3'),
},
}
- connection 连接默认数据库(default)
from django.db import connection
cursor = connection.cursor() # connection = default中的数据库
- connections[] 连接指定数据库
from django.db import connections
cursor = connections['db2'].cursor()
- 查询数据方式
# 写SQL
cursor.execute(""" select * from 表 where id=%s""", [1])
# 获取查询数据
a = cursor.fetchone() # 获取一条数据 tuple
b = cursor.fetchall() # 获取所有数据 tuple嵌套tuple
raw 执行原生SQL
# raw(self, raw_query, params=None, translations=None, using=None)
result = models.UserInfo.objects.raw('select * from app01_usertype')
# 返回[obj(UserInfo),obj(UserInfo)]
# 如果SQL是其他表时,必须将名字设置为当前UserInfo对象的主键列名
models.UserInfo.objects.raw('select id as nid from 其他表')
# 为原生SQL设置参数
models.UserInfo.objects.raw('select id as nid from app01_userinfo where nid>%s', params=[12,])
# 将获取的到列名转换为指定列名
name_map = {'first': 'first_name', 'last': 'last_name', 'bd': 'birth_date', 'pk': 'id'}
Person.objects.raw('SELECT * FROM some_other_table', translations=name_map)
# 指定数据库
models.UserInfo.objects.raw('select * from userinfo', using="default")
using 指定数据库取数据
根据 setting.py 中配置的数据库, 默认取default 。
models.UserInfo.objects.all().useing('db2')
# 这就表示向 db2 数据库中获取表数据
select_related 一次性获取关联的数据
# 性能相关:表之间进行join连表操作,一次性获取关联的数据。
model.tb.objects.all().select_related()
model.tb.objects.all().select_related('外键字段')
model.tb.objects.all().select_related('外键字段__外键字段')
prefetch_related
# 性能相关:多表连表操作时速度会慢,使用其执行多次SQL查询在Python代码中实现连表操作。
# 获取所有用户表
# 获取用户类型表where id in (用户表中的查到的所有用户ID)
models.UserInfo.objects.prefetch_related('外键字段')
from django.db.models import Count, Case, When, IntegerField
Article.objects.annotate(
numviews=Count(Case(
When(readership__what_time__lt=treshold, then=1),
output_field=CharField(),
)))
students = Student.objects.all().annotate(num_excused_absences=models.Sum(
models.Case(
models.When(absence__type='Excused', then=1),
default=0,
output_field=models.IntegerField()
)))