第三章--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. 修改数据库配置文件:

  1. 打开 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语句去数据库执行

以上命令执行成功后,查看对应的数据库会创建UserInfoUserGroup两个表。

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)

增加数据

  1. 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('......')

以上函数执行完后,数据库中就新增一条数据:

UserTypeUserInfo

  1. 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
  1. 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 
  1. update_or_create

如果存在,则更新,否则,创建

defaults 指定创建时或更新时的其他字段

obj, created = models.UserInfo.objects.update_or_create(username='root1', defaults={'password': '1234','ut_id': 3, 'age': 18})

查询数据

  1. 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('......')
  1. 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)+' )
  1. 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'}]
  1. 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',)]
  1. 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', '开发部')]
  1. first() 取第一行数据
# app01\views.py
from app01 import models
def text(request):
    obj = models.UserInfo.objects.first()  # 返回对象
    print(obj, obj.username)
  1. count() 计算总行数
# app01\views.py
from app01 import models
def text(request):
    number = models.UserInfo.objects.count()  # 返回 int 类型
    print(number)
  1. 索引方式[ : ]
# app01\views.py
from app01 import models
def text(request):
    obj = models.UserInfo.objects.all()[1:5]  # 返回 对象
    print(obj)
  1. in_bulk 根据主键进行查询
# 类似于 in条件查询
models.UserInfo.objects.filter(id__in=[1,2,3])
# 上下同理
models.UserInfo.objects.in_bulk([1,2,3])
  1. last 取最后一个数据
# app01\views.py
from app01 import models
def text(request):
    obj = models.UserInfo.objects.last()  # 返回对象
    print(obj, obj.username)

修改数据

  1. update() 更新指定数据(单表操作)
# app01\views.py
from app01 import models
def text(request):  
    ''' 更新数据 '''
    # 查询条件id=1的数据,更新指定字段
    models.UserType.objects.filter(id=1).update(title='分析部')
    return HttpResponse('......')
  1. [更新一列数据方式 F方式](#F 字段列自增数值)

删除数据

  1. 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 数据库中获取表数据
# 性能相关:表之间进行join连表操作,一次性获取关联的数据。
model.tb.objects.all().select_related()
model.tb.objects.all().select_related('外键字段')
model.tb.objects.all().select_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()
    )))