• Django中的ORM进阶操作


    Django中是通过ORM来操作数据库的,通过ORM可以很easy的实现与数据库的交互。但是仍然有几种操作是非常绕也特别容易混淆的。于是,针对这一块,来一个分类总结吧。

    对于ORM对数据库的基本操作前面model里已经有了介绍,这里专门针对ORM的一对多、多对多、正向、反向等操作来讲解用法和注意事项。

    铭记于心的两条:

    • 在联表操作过滤查找数据时用双下划线 "__"
    • 在取数据时用点 "."

    一、一对多

      首先来设计两张简单的表格,并在其中一张表中设置一个另外一张表的外键值  

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    # --*-- coding:utf-8 -*-
    from django.db import models
     
     
    class UserType(models.Model):
        caption = models.CharField(max_length=32)
     
     
    class UserInfo(models.Model):
        user_type = models.ForeignKey(UserType)
        username = models.CharField(max_length=32)
        age = models.IntegerField()

      

    1、添加数据:


      传id的方式:(字典的形式传参)

    1
    2
    3
    user_dict = {"username""chenchao""age""18""user_type_id"1}
     
    models.UserInfo.objects.create(**user_dict)

      

      传对象的方式:

    1
    2
    3
    4
    user_type_obj = models.UserType.objects.get(id=1)   #先获取外键表中的数据对象
    user_dict = {"username""chenchao""age""18""user_type": user_type_obj}        #将对象传入字典
     
    models.UserInfo.objects.create(**user_dict)

    讲解:在我们写的models类时,外键的字段是”user_type",而django在数据库中保存字段时默认会在后面加“_id”,所以可以直接通过传id的方式。

          而在表中的外键字段“user_type”又代表的是字典表中的一行数据对象。于是同样可以传对象的方式来传参。

    2、删除数据

    3、修改数据  (这两个操作与上面的添加用法基本一致)

    4、查找数据

       正向查找:(基于存在外键值表的查找为正向)

    1
    models.UserInfo.objects.filter(user_type__caption= "CEO")   #查找用户类型为CEO的所有用户, 双下划线”__“

      反向查找:(基于不存在外键值表的查找为反向查找,前提是两张表已经建立了关系)

    • 我们创建的外键表有两个字段,id、caption。但Django默认在外键的表里还会埋藏一个字段为userinfo。可以get一个表中不存在的值,通过返回的报错黄页里面可以查看到。
    • 通过models获取的值都是Qureyset。只要是这个类型就可以用.filter .all .count方法
    • 我们知道user_type_obj获取的是外键表中的一行数据对象。
      • user_type_obj.id  代表一个数据
      • user_type_obj.caption  代表一个数据
      • user_type_obj.userinfo_set  特殊,代表的是一种能力。这个能力就可以获取到这个用户类型下的所有用户或者某几个用户
      • request.user  代指的是django自带的auth表里的一行数据,与userinfo做了一个OneToOne,与正向查询是一样的。所以也可以用request.user.userinfo.filter....

    举例:获取某个人是什么用户类型?当前用户类型下有多少人?

    1
    2
    3
    user_type_obj = models.UserType.objects.get(userinfo__username= "chenchao")  #通过外键表来找到隐藏的userinfo字段下的username
    user_type_obj.caption  # 获取用户chenchao的用户类型
    user_type_obj.userinfo_set.all().count()  #获取此类型下的所有用户个数

    点赞的例子:

    首先设计一下数据库表结构,使点赞的一张表与用户和文章建立外键关系

     点赞表结构

    通过反向来操作点赞表,获取点赞的数量

     操作表

    二、多对多

    首先设计好多对多的表结构:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    class Host(models.Model):
        hostname = models.CharField(max_length=32)
        port = models.IntegerField()
     
     
    class HostAdmin(models.Model):
        username = models.CharField(max_length=32)
        email = models.CharField(max_length=32)
        host = models.ManyToManyField(Host)

    前两张表通过models就可以创建,而第三张表django自动帮我们创建完成。我们主要针对第三张表,对其操作增删改查。

    1、增加数据

       正向添加(基于存在外键值表的查找为正向): add

    1
    2
    3
    user_obj = models.HostAdmin.objects.get(username="chenchao")   # 获取某个用户的数据对象
    host_obj = models.Host.objects.filter(id__lt=3)   # 获取id小于3的主机数据对象
    user_obj.host.add(*host_obj)   # 通过用户对象下的ManyToMany的字段将主机与用户的对象添加到第三张表中

      反向添加:(基于不存在外键值表的查找为反向查找,前提是两张表已经建立了关系

    1
    2
    3
    host_obj = models.Host.objects.get(id=1)   # 1、获取主机的数据对象
    user_obj = models.HostAdmin.objects.filter(id__gt=1)   # 2、获取用户id大于1的数据对象
    host_obj.hostadmin_set.add(*user_obj)      # 3、通过隐藏的外键字段hostadmin将主机对象与用户对象添加到第三张表

    2、查找数据

      正向查找:(基于存在外键值的表

    1
    2
    user_obj = models.HostAdmin.objects.get(id=1)  # 获取用户的数据对象
    print user_obj.host.all().count()              # 基于用户对象,通过外键字段来查找第三张表中的个数

      反向查找:(基于不存在外键值的表

    1
    2
    host_obj = models.Host.objects.get(id=1)       # 获取主机的数据对象
    print host_obj.hostadmin_set.all().count()     # 基于主机对象,通过隐藏的hostadmin_set字段来查找第三张中的数据

    自定义Django的多对多的第三张表:

      django除了能自动创建多对多的第三张表,同样也可以自定义创建多对多的第三张表,而且操作和管理扩展等难易程度要比自动创建的好许多。所以,在之后的models表结构中,推荐使用自定义的方式。

    仅仅在创建时添加一个字段即可:through='HostRelation' 。   HostRelation是我们自定义的第三张表名。

    复制代码
    class Host1(models.Model):
        hostname = models.CharField(max_length=32)
        port = models.IntegerField()
    
    class HostAdmin1(models.Model): username = models.CharField(max_length=32) email = models.CharField(max_length=32) host = models.ManyToManyField(Host1, through='HostRelation') class HostRelation(models.Model): #自定义创建第三张表,其中的外键都为一对多的关系 c1 = models.ForeignKey(Host1) c2 = models.ForeignKey(HostAdmin1)
    复制代码

    1、创建数据

     操作自定义创建的多对多关系表的两种方式:

    复制代码
    def ManytoM(request):
    
        models.HostRelation.objects.create(    # 传对象的方式向第三张表中添加数据,笨法
            c1=models.Host1.objects.get(id=1),
            c2=models.HostAdmin1.objects.get(id=2)
        )
        models.HostRelation.objects.create(c1_id=2, c2_id=1,)  # 传id的方式向第三张表中添加数据,easy idea
    return HttpResponse("add_many to many OK")
    复制代码

     2、查找数据

    1
    2
    3
    4
    5
    6
    7
    relation_list = models.HostRelation.objects.all()  # 直接通过自定义的第三张表来查找数据
     
    for item in relation_list:
        print item.c1.hostname    # 通过点”.“ 来获取数据
        print item.c2.username
     
    print models.HostRelation.objects.filter(c2__username="chenchao")   # 通过双下划线”__“来查找数据

      

     三、select_related

    select_related:用来优化数据库查询的操作,可以没有,但优化的不够彻底。

    用于在foreignkey查询的时候使用。可以通过query来查看一下django执行的sql语句。

    1
    2
    3
    4
    ret1 = models.UserInfo.objects.all()
    ret2 = models.UserInfo.objects.all().select_related()
    print ret1.query           
    print ret2.query            # 查看django执行的sql语句
    复制代码
    ret1:
    
    SELECT 
        "App01_userinfo"."id", 
        "App01_userinfo"."user_type_id", 
        "App01_userinfo"."username", 
        "App01_userinfo"."age" 
    
    FROM "App01_userinfo"
    
    
    ret2:
    
    SELECT 
        "App01_userinfo"."id", 
        "App01_userinfo"."user_type_id", 
        "App01_userinfo"."username", 
        "App01_userinfo"."age", 
        "App01_usertype"."id", 
        "App01_usertype"."caption" 
    FROM 
        "App01_userinfo" INNER JOIN "App01_usertype" 
        
    ON 
        ("App01_userinfo"."user_type_id" = "App01_usertype"."id")
    复制代码

     通过sql语句我们可以清晰地看到select_related不仅把当前表的内容查找出来,而且还把外键的表里的数据也查了出来。如果我们按ret1的方式,需要在多执行一次sql的查找操作。而ret2只需要执行一次。

     四、Django神奇的F

    如果一张表中的数字列需要增加,那么F是最神奇的操作。

    例如我们需要把user_info表里的所有age加1:

    1
    2
    3
    from django.db.models import F     #先要导入F
     
    models.user_info.objects.all().update(age=F('age')+1)   #执行+1

    五、Django更神奇的Q

     当做复杂的搜索查找条件时,django的Q可以提供非常便利的方法。

    在设计搜索条件时,相同的字段为或操作(OR),不同的字段之间是且操作(AND)

    复制代码
    from django.db.models import Q    # 导入Q
    con = Q()   # 创建Q对象
    
    q1 = Q()    
    q1.connector = 'OR'      # q1的元素为OR或的关系
    q1.children.append(('id', 1))
    q1.children.append(('id', 10))
    q1.children.append(('id', 9))
    
    q2 = Q()
    q2.connector = 'OR'      # q2的元素为OR或的关系
    q2.children.append(('c1', 1))
    q2.children.append(('c1', 10))
    q2.children.append(('c1', 9))
    
    con.add(q1, 'AND')      # 将q1添加到con对象中,与其他的Q为且的关系
    con.add(q2, 'AND')
    
    models.Tb1.objects.filter(con)   #将总的Q对象添加到model的查找条件中
    复制代码

    提示:

    1、之前所有的方法 如__gt,__lt,__contains. 双下划线联表查询等等都可以继续使用
    2、append添加的是一个元组
    3、最外层是用AND连接

  • 相关阅读:
    kubernetes 【版本】
    【pod无法删除 总是处于terminate状态】强行删除pod
    prometheus数据格式
    【prometheus 抓取源】
    【PromQL】prometheus查询语言
    【grafana报错】Singlestat "Error: Multiple Series Error"
    【prometheus抓取间隔】scrape_interval
    【分布式一致性】etcd
    【kubectl 原理】kubectl 命令执行的时候究竟发生了什么(kubectl、apiserver、etcd)
    中国移动DNS IP地址大全(32个省)
  • 原文地址:https://www.cnblogs.com/ExMan/p/9443620.html
Copyright © 2020-2023  润新知