• Django基础——Model篇(三)


    一 Django ORM中的概念

    ORM —— 关系对象映射,是Object Relational Mapping的简写,是用来简化数据库操作的框架

    Django ORM遵循Code Frist原则,即根据代码中定义的类来自动生成数据库表,对于ORM框架:
      (1)自定义的类表示待创建数据库的表
      (2)根据自定义类创建的对象obj表示数据库表中的一行数据
      (3)obj.字段1、obj.字段2、...、obj.字段n表示每一行数据中相应字段的值

    ORM中的一对多、一对一以及多对多的概念及应用场景,具体参见前期博客。
    ORM中的正向和反向操作,这是相对而言的,主要取决于一对多/一对一/多对多字段写在哪个类中。

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

       对于上面两张表,UserInfo表是关联UserType表的,即一对多字段(外键)在UserInfo表中。那么根据UserInfo表去操作数据库,就表示正向;反之根据UserType表去操作数据库,就表示反向。

    二 ORM中一对多的操作

      models.py中的表结构

    class UserType(models.Model):
        caption = models.CharField(max_length=32)
    
    class UserInfo(models.Model):
        # 此处user_type是models.ForeignKey类封装了UserType类生成的的对象
        # 即user_type是包含了id和caption字段的对象
        user_type = models.ForeignKey(UserType)
        username = models.CharField(max_length=32)
        age = models.IntegerField()

       views.py中生成三种用户类型的代码

    def user_type(request):
        # 添加user_type表的数据
        # dic = {'caption': 'CEO'}  对应id为1
        # dic = {'caption': 'CTO'}  对应id为2
        # dic = {'caption': 'COO'}  对应id为3
        # models.UserType.objects.create(**dic)
        print models.UserType.objects.all()
       return HttpResponse('ok')

      生成的表结构如下图所示:

      1 UserInfo表添加数据的两种方法

      (1)数据库级别

        上表是UserInfo数据库表结构,外键user_type字段默认会加上"_id"进行存储,那么从数据库级别添加数据的代码如下:

    def user_info(request):
        # 一对多:外键
        # 一对多中添加user_info表数据:数据库级别
    
        # 第1种方法(数据库级别):按外键在数据库中的存储方式
        dic = {'username':'xx','age':18,'user_type_id':1}
        models.UserInfo.objects.create(**dic)
        result = models.UserInfo.objects.all()
        for item in result:
            print item.username,item.age,item.user_type.caption

      (2)对象级别

    def user_info(request):
        # 一对多:外键
        # 一对多中添加user_info表数据:对象级别
    
        # 第2种方法(对象级别):UserInfo表中的user_type字段对应models中的UserType对象
    
        #先获取用户类型的对象
        type = models.UserType.objects.filter(id=2)
        #添加时直接添加对象
        models.UserInfo.objects.create(username='xxx',age=28,user_type=type)
        
        #或写成下面形式,省略中间步骤
        dic = {'username':'xxx','age':28,'user_type':models.UserType.objects.get(id=2)}
        models.UserInfo.objects.create(**dic)

     get方法是从数据库中取一个匹配的结果,返回一个对象,如果不存在,则会报错
     filter方法是从数据库中取匹配的结果,返回一个对象列表,如果不存在,则会返回[]

      2 查找数据

      (1)正向查

    def user_info(request):
        # 查找表中的所有数据
        result = models.UserInfo.objects.all()
    
        # 查找指定数据
        # 正向查找:查询当前表中数据
        result = models.UserInfo.objects.filter(username='xx')
    
        # 跨表查询(使用双线划线"__"),user_type是UserType类的对象,所以这里是查询UserType表中的caption字段,即是跨表查询
        result = models.UserInfo.objects.filter(user_type__caption='CTO')
        for item in result:
          # 与跨表查询数据不同的是,当查询完数据再取数据时,使用的是".",这里要注意
          print item.username,item.age,item.user_type.caption
        
        return HttpResponse('ok')

      (2)反向查

    def user_info(request):
        # 反向查找
        # 搜索条件:id/caption/userinfo(该字段是Django在UserType表中自动生成的,关联到UserInfo表,这个字段很重要,为反向查找提供了可能)
        line = models.UserType.objects.get(id=1)
        print line.id  #输出"1"
        print line.caption  #输出"CEO"
        #输出"[<UserInfo: UserInfo object>]",表示当前用户类型UserType(id=1)对应的用户对象
        '''
        注意下面的line.userinfo_set等价于models.UserInfo.objects.filter(user_type=line)
        好好理解这种等价关系
        '''
        print line.userinfo_set.all()
        print line.userinfo_set.filter(username='xx')
        for item in line.userinfo_set.all():
           # 输出"xx 18 UserType object"
            print item.username, item.age, item.user_type
           # 输出"xx 18 CEO",注意user_type是UserType的对象
            print item.username, item.age, item.user_type.caption
    
        # 1、查找某个人是哪种用户类型;
         user_type_obj = models.UserType.objects.get(userinfo__username='xx')
        print user_type_obj.caption

    # 2、查找指定用户类型下有哪些用户
    # 下面两种方法是等价的,注意在Django中,UserType表默认会增加一列表示与UserInfo表的关系 print user_type_obj.userinfo_set.count() # 下面方法是从UserInfo表出发查找的 print models.UserInfo.objects.filter(user_type=user_type_obj).count() return HttpResponse('ok')

      3 新闻点赞实例

      models.py中的表结构

    #用户表
    class
    MyUser(models.Model): username = models.CharField(max_length=32) password = models.CharField(max_length=64) def __unicode__(self): return self.username
    #新闻表
    class New(models.Model): title = models.CharField(max_length=32) content = models.CharField(max_length=32) def __unicode__(self): return self.title
    #点赞表,这里需要注意:1 哪个用户点的赞;2 给哪个新闻点的赞;3 不能重复点赞
    class Favor(models.Model): # 外键关联MyUser user_obj = models.ForeignKey(MyUser) # 外键关联New new_obj = models.ForeignKey(New) def __unicode__(self): return "%s -> %s" %(self.user_obj.username, self.new_obj.title)

       配置admin以及创建admin用户,并创建几篇文章

    from django.contrib import admin
    from app01 import models
    
    # Register your models here.
    admin.site.register(models.MyUser)
    admin.site.register(models.New)
    admin.site.register(models.Favor)
    admin.site.register(models.HostAdmin)
    admin.site.register(models.Host)

                                                 

     views.py中的相关代码,主要完成两个功能:1 查询某人点赞过的文章; 2 查询某篇文章被点赞过的次数

    # 新闻点赞实例(相关表数据已在Django的admin中添加完成)
    def favor_new(request):
        # 获取所有的新闻列表
        # new_list = models.New.objects.all()
    
        # 获取alex点赞过的新闻列表,注意这里的反向查询和跨表查询
        new_list = models.New.objects.filter(favor__user_obj__username='alex')
        for item in new_list:
           print '====================='
            print item.title
           print item.content
           # 打印每条新闻的点赞次数
            print item.favor_set.all().count()
        return HttpResponse('ok')

    三 ORM中多对多的操作

        Django中的多对多与一对多没有联系,它的实际操作比一对多简单,Django默认会为多对多生成第3张表。下面是对应数据库表的关系:

       models.py中的数据库结构

    #============多对多================
    #下面建立多对多关系的字段,写了其中任何一个即可,它们的区别只在于哪个是"正向查找",哪个是"反向查找",即查找时的参照类有区别。
    class Host(models.Model):
        hostname = models.CharField(max_length=32)
        port = models.IntegerField()
        # admin = models.ManyToManyField(HostAdmin)
    
    class HostAdmin(models.Model):
        username = models.CharField(max_length=32)
        email = models.CharField(max_length=32)
        host = models.ManyToManyField(Host)

       views.py中添加数据

    def many_to_many(request):
       #创建主机表
        models.Host.objects.create(hostname='c1',port=80)
        models.Host.objects.create(hostname='c2',port=80)
        models.Host.objects.create(hostname='c3',port=80)
        
       #创建用户表,注意这里只需要添加username和email字段,host字段不需要指定
        models.HostAdmin.objects.create(username='alex',email='alex@qq.com')
        models.HostAdmin.objects.create(username='eric',email='eric@qq.com')
        models.HostAdmin.objects.create(username='pony',email='pony@qq.com')
        models.HostAdmin.objects.create(username='rain',email='rain@qq.com')
      return HttpResponse('ok')

        生成的三张表如下:

       
      1 正向添加数据(从包含多对多字段的表操作数据库)

    def many_to_many(request):
       # 正向添加
        # 目的:给alex分配两个主机的管理权限
        # 1、获取指定的HostAdmin对象
        admin_obj = models.HostAdmin.objects.get(username='alex')
        # 2、获取指定的Host,条件为id小于3
        host_list = models.Host.objects.filter(id__lt=3)
        # 3、将用户alex和指定的两个主机添加到对应的第3张表中,注意host_list是列表,所有加*号
        admin_obj.host.add(*host_list)
      return HttpResponse('ok')

      Django自动生成的第3张表如下所示:

      2 反向添加数据(从不包含多对多字段的表操作数据库)

    def many_to_many(request):
        # 反向添加
        # 1、获取主机表
        # host_obj = models.Host.objects.get(id=3)
        # 2、获取用户表
        # admin_list = models.HostAdmin.objects.filter(id__gt=1)
        # 3、添加数据,注意hostadmin_set,这是在Host表中,Django自动生成的关联HostAdmin表的字段
        # host_obj.hostadmin_set.add(*admin_list)
      return HttpResponse('ok')
    无论是正向添加还是反向添加,其本质都是基于主机表或用户表的一行数据(对象)对应另一张表中的一行或多行数据(对象)。

    正向添加,对于ID=2的用户,添加多台主机(比如主机ID为1,2,3,4),那么Django自动生成的第3张表信息如下:
    # 用户ID  主机ID
        2       1
        2       2
        2       3
        2       4

    反向添加,对于ID=2的主机,添加多个用户(比如用户ID为1,2,3),那么Django自动生成的第3张表信息如下:
    # 用户ID  主机ID
        1       2
        2       2
        3       2

        3 自定义第3张表

        对于Django自动生成的第3张表,在使用的过程中不是很灵活,也不能增加字段。对于这种情况,Django允许自定义生成第3张表,不需要使用默认的表结构。分析下三张表之间的关系:

         models.py中自定义第3张表

    # 主机表
    class Host(models.Model):
        hostname = models.CharField(max_length=32)
        port = models.IntegerField()
        # admin = models.ManyToManyField(HostAdmin)
    
    # 用户表 class HostAdmin(models.Model): username = models.CharField(max_length=32) email = models.CharField(max_length=32) host = models.ManyToManyField(Host1,through='HostRelation') # 自定义的第3张表 class HostRelation(models.Model): # 外键关系 c1 = models.ForeignKey(Host) c2 = models.ForeignKey(HostAdmin) # 还可以自定义字段

       自定义第3张表中添加数据(在自定义的第3张表中,我们在添加数据时其实跟其它两张表没有关系了,因为自定义时我们定义了数据库类)

    def many_to_many(request):
        # 在自定义多对多的第3张表中添加数据
    # 第1种方法:对象级别
    models.HostRelation.objects.create( c1=models.Host.objects.get(id=1), c2=models.HostAdmin.objects.get(id=2) )
    # 第2种方法:数据库级别
    # models.HostRelation.objects.create( # c1_id = 2, # c2_id = 1 # ) return HttpResponse('ok')

      上述两种方法性能比较:第1种方法中两次数据库查询,1次数据库插入;第2种方法中0次数据库查询,1次数据库插入。

      4 查询数据库

        Django默认生成第3张表的查询/自定义第3张表的查询

    def many_to_many(request):
        # 第1种 Django默认生成第3张表的查询
        # 正向查
        admin_obj = models.HostAdmin.objects.all(id=1)
        for item in admin_obj:
            item.host.all()
    
        # 反向查
        host_obj = models.Host.objects.get(id=1)
        host_obj.hostadmin_set.all()
    
        # 第2种 自定义第3张表的查询
        #relation_list = models.HostRelation.objects.all()
        relation_list = models.HostRelation.objects.filter(c2__username='alex')
        for item in relation_list:
            print item.c1.hostname
            print item.c2.username
    
        return HttpResponse('ok')

      5 select_related的作用

      数据库表结构

    class UserType(models.Model):
        caption = models.CharField(max_length=32)
    
    class UserInfo(models.Model):
        user_type = models.ForeignKey('UserType') #这个user_type是一个对象,对象里面封装了ID和caption
        username = models.CharField(max_length=32)
        age = models.IntegerField()

      select_related是用来优化查询的,主要优化ForeignKey的查询

    def user_info(request):
        # 普通查询
        ret = models.UserInfo.objects.all()
        #打印执行的SQL语句
        print ret.query
    
        '''
         SELECT "app01_userinfo"."id", "app01_userinfo"."user_type_id", "app01_userinfo"."username", "app01_userinfo"."age" 
    FROM "app01_userinfo"
    ''' # select_related优化查询 ret = models.UserInfo.objects.all().select_related() #打印执行的SQL语句 print ret.query ''' 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") ''' return HttpResponse('ok')

        如果是普通查询,从执行的SQL语句可以看出,它只会获取UserInfo表中的数据;如果使用select_related,从执行的SQL语句可以看出它会把ForiegnKey关联的表自动做一次关联查询。它既获取UserInfo表的数据,同时也获取UserType表的数据。

    四 ORM中的F&Q

     1 F — 批量修改或更新数据

       假设数据库中有1个age字段,我想让所有age字段自加1或某些人自加1,那么可以利用ORM中F:

    #导入F模块
    from django.db.models import F
    #F指代当前行的age字段
    model.tb.object.all().update(age=F('age')+1)

     2 Q — 条件查询,非常有用

       默认情况下Django的查询

    #查找username='alex'且age=18的对象,条件不是很灵活
    models.UserInfo.objects.filter(username='alex',age=18)

        假如要查找username='alex'或username='eric'或username='rain',并且age=18的对象,那么默认的查询实现起来比较麻烦,为了解决这种情况,Django为我们提供了Q操作,下面是操作步骤:

    #导入Q模块
    from django.db.models import Q

    #
    第1步: #生成一个搜索对象 search_q = Q() #再生成两个搜索对象 search1 = Q() search2 = Q() #第2步: #标记search1中的搜索条件的连接符为'OR'(或条件) search1.connector = 'OR' #把搜索条件加入到search1中,注意都是元组的形式 search1.children.append(('字段名','字段内容')) search1.children.append(('字段名','字段内容')) search1.children.append(('字段名','字段内容')) search1.children.append(('字段名','字段内容')) #标记search2中的搜索条件的连接符为'OR'(或条件) search2.connector = 'OR' #把搜索条件加入到search2中 search2.children.append(('字段名','字段内容')) search2.children.append(('字段名','字段内容')) search2.children.append(('字段名','字段内容')) search2.children.append(('字段名','字段内容')) #第3步: #把多个搜索条件进行合并,以'AND'的形式(与条件) search_q.add(search1,'AND') search_q.add(search2,'AND') #第4步: #执行搜索,还是利用filter models.HostInfo.objects.filter(search_q)

    五 Django ORM总结

     1、一对多
     (1)添加数据
        通过对象级别
        通过数据库级别(数据在数据库中的存储方式,对象字段_id)
     (2)查找
        正向查找
         通过filter跨表,对象__跨表的字段
         获取值,obj.对象.跨表的字段
        反向查找
              通过filter跨表,Django自动生成与表名相同的对象__跨表的字段
         获取值,obj.Django自动生成与表名相同的对象_set.filter()/all()[0:]
     2、多对多
     (1)Django自动生成关系表
        正向:一行数据的对象.ManyToMany字段
            反向:一行数据的对象.表名_set
     (2)自定义关系表(推荐)
           不管是添加、查询,只需要操作自定义关系表
     3、select_related
      优化查询,一次性将查询的表和ForiegnKey关联的表加载到内存。

     4、Django中的F&Q操作

    参考资料:

       http://www.cnblogs.com/luotianshuai/p/5403513.html

  • 相关阅读:
    POJ3320 Jessica's Reading Problem
    POJ3320 Jessica's Reading Problem
    CodeForces 813B The Golden Age
    CodeForces 813B The Golden Age
    An impassioned circulation of affection CodeForces
    An impassioned circulation of affection CodeForces
    Codeforces Round #444 (Div. 2) B. Cubes for Masha
    2013=7=21 进制转换
    2013=7=15
    2013=7=14
  • 原文地址:https://www.cnblogs.com/maociping/p/5350278.html
Copyright © 2020-2023  润新知