• 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后台管理页面
    
    




  • 相关阅读:
    java注解,通过反射解析注解,模仿hibernate,获取sql语句。
    Eclipse/Myeclipse中查看和调试JDK源代码的方法
    TCP为什么会出现 RST
    《浅谈F5健康检查常用的几种方式》—那些你应该知道的知识(二)
    负载均衡服务TCP端口健康检查成功,为什么在后端业务日志中出现网络连接异常信息?
    haproxy的丰富特性简介
    健康检查概述
    firewall防火墙常用操作
    gitlab修改默认端口
    vim脚本判断操作系统
  • 原文地址:https://www.cnblogs.com/hoyun/p/15572392.html
Copyright © 2020-2023  润新知