django第三章 ORM 相关操作



ORM (数据库)

Django的ORM操作本质上会根据对接的数据库引擎,翻译成对应的sql语句;所有使用Django开发的项目无需关心程序底层使用的是MySQL、Oracle、sqlite等等;如果数据库迁移,只需要更换Django的数据库引擎即可。

ORM 默认使用的数据库:SQLlite

ORM核心功能:

​ 1.操作表(创建表、修改表(新增表字段、修改表字段)、删除表)

​ 2.操作表数据行(增删改查)

推荐博客:https://www.cnblogs.com/wupeiqi/articles/6216618.html



数据库配置操作步骤

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)   # <QuerySet [<UserInfo: UserInfo object (1)>, <UserInfo: UserInfo object (2)>]>
# 加 __str__ 情况:
obj02 = models.UserInfo.objects.all()
print(obj02)      # <QuerySet [<UserInfo: root>, <UserInfo: test1>]>



数据表相关操作

新增字段

如果对已创建的表想要新增字段时,只需要在原有的基础上直接添加即可,以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 Info(models.Model):  
    name = models.CharField(max_length=32)    
    
class Group(models.Model):
    title = models.CharField(max_length=32)
    
class Test02(models.Model):
    info = models.ForeignKey('Info')  
    group = models.ForeignKey('Group')
    # 联合唯一索引,约束
    class Meta:
        unique_together = [('info', 'group'),]
点击查看代码
# 给对应表插入数据
objs1 = [   models.Info(name='A1'),  models.Info(name='A2'),  models.Info(name='A3')]
models.Info.objects.bulk_create(objs1, 2)

objs2 = [models.Group(title='B1'),models.Group(title='B2'), models.Group(title='B3')]
models.Group.objects.bulk_create(objs2, 2)

models.Test02.objects.create(info_id=1, group_id=1)
models.Test02.objects.create(info_id=1, group_id=2)
models.Test02.objects.create(info_id=2, group_id=1)
models.Test02.objects.create(info_id=2, group_id=3)
点击查看代码
# 查询数据,通过Test02表进行跨表查询Info表中A1对应的Group表中对应的数据
tt = models.Test02.objects.filter(info_name='A1')
for it in tt: 
    print(it.group.title)

  • ManyToManyField
    另一种方式关联表: ManyToManyField 字段关联
点击查看代码
# app01\model.py    创建表
from django.db import models
class Info(models.Model):  
    name = models.CharField(max_length=32)    
    a = models.ManyToManyField('Group') # Django会自动创建新表,进行两个表关联 表名:info_a
class Group(models.Model):
    title = models.CharField(max_length=32)
    #a = models.ManyToManyField('Info')  # 相关联的表可以放到任意表中,对该表不会做额外操作

'''  对自动生成的表,添加数据操作  '''
obj=models.Info.objects.filter(name='A1').first()
obj.a.add(1)  
# 执行结果:info_a表中增加1条数据 name_id=1, title_id=1
obj.a.add(2,3)
# 执行结果:info_a表中增加2条数据 name_id=1, title_id=2, name_id=1, title_id=3
obj.a.add(*[4,])
# 执行结果:info_a表中增加1条数据 name_id=1, title_id=4

'''删除操作'''
obj.a.remove(1)
# 执行结果:info_a表中 title_id=1 的相关行数据
obj.a.remove(2,3)
# 执行结果:info_a表中 title_id=2, title_id=3 的相关行数据
obj.a.remove(*[4,])
# 执行结果:info_a表中 title_id=4 的相关行数据

'''清空数据'''
obj.a.clear()   # 将name='A1'关联Group表中的数据,全部清空

'''覆盖原数据'''
obj.a.set([1,2,3])   # info_a表中会将原数据全部删除,添加列表中的新数据,对应 title_id 字段

'''获取数据'''
q = obj.a.all()  # 这里取的是Group表的对象数据

'''通过Group表获取Info表的数据'''
obj = models.Group.objects.filte(title='B1').first()  # 获取Group表对象
v = obj.info_set.all()    # 获取Info表对象

  • ManyToManyField 进行自己关联
点击查看代码
class UserInfoTest(models.Model):
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=64)
    age = models.IntegerField(default=0)
    gender_choices = ((1,'男'),(2,'女'))
    genderss = models.IntegerField(choices=gender_choices)
    # 自己与自己关联
    m = models.ManyToManyField('UserInfo')

执行命令后,数据库自动创建数据表:

点击查看代码
def test(request):
    obj = models.UserInfoTest.objects.filter(id=2).first() 
    res = obj.m.all()    # 通过m字段去获取对应的数据,匹配的是‘from_userinfotest_id’字段列
    for it in res:    
        print(it.username)
    # 反向查询
    v = obj.userinfotest_set.all() # userinfotest_set=匹配的是‘to_userinfotest_id’字段列
    for it in v:
        print(it.username)
    return HttpResponse('......')

  • ForeignKey 自己关联
class UserInfoTT(models.Model):
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=64)
    age = models.IntegerField(default=0)
    gender_choices = ((1,'男'),(2,'女'))
    genderss = models.IntegerField(choices=gender_choices)
    # 自己与自己关联
    f = models.ForeignKey('UserInfo', on_delete=models.CASCADE, null=True, blank=True)

执行后数据库字段显示:



表数据操作

案例中的相关表结构:

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

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



  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)     # <QuerySet [<UserType: UserType object (1)>]>
    # 获取其中具体字段的数据
    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方式(下面详细介绍)



  • 其他筛选条件方式:
参数 说明
__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)    #> <QuerySet [{'username': 'root'}, {'username': 'test1'}]>
    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)    #> <QuerySet [('root',), ('test1',)]>
    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)        #> <QuerySet [('root', '开发部'), ('test1', '开发部')]>
    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方式(下面详细介绍)



删除数据

  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()
    )))



Meta 元数据

在每个模型类的里面我们还可以定义一个子类Meta,这个子类可以定义一些有关数据库或者数据表的相关信息,这些相关信息我们称之为元数据。

强调:每个模型都可以有自己的元数据类,每个元数据类也只对自己所在模型起作用。

字段介绍
# 让其他的类来继承这个基类
class BaseModel(models.Model):
    '''定义模型抽象基类'''
    create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
    uptate_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
    is_delete = models.BooleanField(default=False, verbose_name='删除标记')
    
    class Meta:        
        abstract = True  # 说明是一个抽象模型类
        
        # 其他字段说明:
        db_table = "table_name"  # 自定义数据库中生成的表名称        
        app_label='app_name'       # app_label声明属于哪个应用
        
        verbose_name = "name"   
        # 给这个模型取一个更简单、好读的名字,用于在各种打印、页面展示等场景,可以用中文。
        
        verbose_name_plural = verbose_name 
        # 这个就是模型对象的复数名,比如“apples”。因为我们中文通常不区分单复数,所以保持和verbose_name一致也可以。
        
        ordering = [“需要升序排列的字段名”] 
        # 注意:等号右边必须以元组或者列表的形式。这样的话也可以定义多个字段的排序了。
        
        index_together = [ ("字段1", "字段2"),]  # 联合索引
        
        unique_together = (("字段1", "字段2"),)  # 联合唯一索引 等同于数据库的联合约束!



Django使用多个数据库

一、定义数据库

使用Django的多个数据库的第一步是告诉Django将使用的数据库服务器。 这是使用DATABASES设置完成的。 此设置将数据库别名映射到该特定连接的设置字典,该数据库别名是一种在整个Django中引用特定数据库的方法。 内部词典中的设置在DATABASES文档中有完整描述。

数据库可以包含您选择的任何别名。 当没有选择其他数据库时,Django使用具有默认别名default的数据库。

比如需求:

  • 在进行django项目开发的时候,遇到了需要连接两个MySQL数据库的问题。同时使用django自带的admin进行后台数据管理。针对django项目中有多个app,app之间使用不同数据库的需求。

实现步骤:

  1. 设置路由,将app映射到相对应的数据库

    在settings.py文件同目录下,创建一个database_router.py文件(文件名自定义),添加内容如下:

点击查看代码
# -*- coding: utf-8 -*-
from django.conf import settings
DATABASE_MAPPING = settings.DATABASE_APPS_MAPPING
 
class DatabaseAppsRouter(object):
    """
    A router to control all database operations on models for different
    databases.
      
    In case an app is not set in settings.DATABASE_APPS_MAPPING, the router
    will fallback to the `default` database.
      
    Settings example:
      
    DATABASE_APPS_MAPPING = {'app1': 'db1', 'app2': 'db2'}
    """
  
    def db_for_read(self, model, **hints):
        """"
        应用于读取类型对象的数据库模型,如果数据库提供附加信息会在hints字典中提供,最后如果没有则返回None
        """
        if model._meta.app_label in DATABASE_MAPPING:
            return DATABASE_MAPPING[model._meta.app_label]
        return None
 
    def db_for_write(self, model, **hints):
        """
        应用于写入类型对象的数据库模型,hints字典提供附加信息,如果没有则返回None
        """
        if model._meta.app_label in DATABASE_MAPPING:
            return DATABASE_MAPPING[model._meta.app_label]
        return None
 
    def allow_relation(self, obj1, obj2, **hints):
        """
        外键操作,判断两个对象之间是否是应该允许关系,是返回True,否则返回False,如果路由允许返回None
        """
        db_obj1 = DATABASE_MAPPING.get(obj1._meta.app_label)
        db_obj2 = DATABASE_MAPPING.get(obj2._meta.app_label)
        if db_obj1 and db_obj2:
            if db_obj1 == db_obj2:
                return True
            else:
                return False
        return None
 
    # for Django 1.4 - Django 1.6
    def allow_syncdb(self, db, model):
        """
        Make sure that apps only appear in the related database.
        """
 
        if db in DATABASE_MAPPING.values():
            return DATABASE_MAPPING.get(model._meta.app_label) == db
        elif model._meta.app_label in DATABASE_MAPPING:
            return False
        return None
 
    # Django 1.7 - Django 1.11
    def allow_migrate(self, db, app_label, model_name=None, **hints):
        """
        db确定是否允许在具有别名的数据库上运行迁移操作,操作运行返回True,否则返回False,或者返回None,如果路由器没有意见。
        app_label:位置参数是正在迁移的应用程序的标签。
        model_name:多个迁移操作设置模型的值,如:model._meta.app_label
        """
        if db in DATABASE_MAPPING.values():
            return DATABASE_MAPPING.get(app_label) == db
        elif app_label in DATABASE_MAPPING:
            return False
        return None

  1. settings.py 中的DATABASES配置:
settings
DATABASES = {
     'default': {
         'ENGINE': 'django.db.backends.sqlite3',
         'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    },    
    'db02': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'app02',
        'USER': 'root',
        'PASSWORD': '',
        'HOST': '127.0.0.1',
        'PORT': 3306,
    },
    'db03': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'app03',
        'USER': 'root',
        'PASSWORD': '',
        'HOST': '127.0.0.1',
        'PORT': 3306,
    }
}

注意:其中default可以为空,但是不能删除,db02, db03 是需要配置的数据库连接信息。


settings.py 配置其他内容:

settings
# 数据库路由
DATABASE_ROUTERS = ['project_name.database_router.DatabaseAppsRouter']     # 路径
 
# 根据app名称路由指定的数据库
DATABASE_APPS_MAPPING = {
    # example:
    # 'app_name':'database_name',
    'app02': 'db02',
    'app03': 'db03',
}

  1. 为每个app的model分别指定所需要连接的数据库:
model
# app02/model.py
class UserInfoApp02(models.Model):
    username=models.CharField(max_length=32)
    password=models.CharField(max_length=64)
  
    class Meta:
        app_label = 'app02'     #定义该model的app_label

  1. 执行数据库迁移命令,可以通过--database指定迁移使用的数据库
  python manage.py makemigrations
  python manage.py migrate --database=db02

  1. 执行后数据库建表情况:



二、跨数据库操作

使用using()指定查询的数据库的别名:

    # 在查询的语句后面用 using(dbname) 来指定要操作的数据库即可
    task = User.objects.using('db02').filter(userId = 1)

    # 保存 或 删除
    user_obj.save(using='db02')
    user_obj.delete(using='db03')



三、数据导入导出

使用的时候和一个数据库的区别是:

如果不是defalut(默认数据库)要在命令后边加 --database=数据库对应的settings.py中的名称 如: --database=db1 或 --database=db2

  • 数据库同步(创建表)
  python manage.py migrate --database=db1

  • 数据导出
  python manage.py dumpdata app1 --database=db1 > app1_fixture.json
  python manage.py dumpdata app2 --database=db2 > app2_fixture.json
  python manage.py dumpdata auth > auth_fixture.json

  • 数据库导入
  python manage.py loaddata app1_fixture.json --database=db1
  python manage.py loaddata app2_fixture.json --database=db2



使用Django中的admin操作指定的数据库

在admin.py中通过下述代码告诉django在处理这些model的orm关系时,使用settings里配置的哪个数据库进行连接。然后再与正常一样进行使用。

admin
class MultiDBModelAdmin(admin.ModelAdmin):
    # A handy constant for the name of the alternate database.
    using = 'db02' #指定使用的数据库
 
    def save_model(self, request, obj, form, change):
        # Tell Django to save objects to the 'other' database.
        obj.save(using=self.using)
 
    def delete_model(self, request, obj):
        # Tell Django to delete objects from the 'other' database
        obj.delete(using=self.using)
 
    def get_queryset(self, request):
        # Tell Django to look for objects on the 'other' database.
        return super(MultiDBModelAdmin, self).get_queryset(request).using(self.using)
 
    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        # Tell Django to populate ForeignKey widgets using a query
        # on the 'other' database.
        return super(MultiDBModelAdmin, self).formfield_for_foreignkey(db_field, request, using=self.using, **kwargs)
 
    def formfield_for_manytomany(self, db_field, request, **kwargs):
        # Tell Django to populate ManyToMany widgets using a query
        # on the 'other' database.
        return super(MultiDBModelAdmin, self).formfield_for_manytomany(db_field, request, using=self.using, **kwargs)
 
 
class UserProfileAdmin(MultiDBModelAdmin):
    model = CraCrawl
 
 
admin.site.register(UserProfile, UserProfileAdmin)    #注册模型到admin后台管理页面




原文地址:https://www.cnblogs.com/hoyun/p/15572392.html