zoukankan      html  css  js  c++  java
  • Django多表操作

    创建一个多表模型

      表与表之间存在一对一,一对多,多对多的关系,加外键约束和不加外键约束的区别,一对一的外键约束是在一对多的约束上加上唯一约束。

      创建一个简单的多表关联模型: 

      作者模型:一个作者有姓名和年龄。

      作者详细模型:把作者的详情放到详情表,包含手机号,家庭住址等信息。作者详情模型和作者模型之间是一对一的关系(one-to-one)

      出版商模型:出版商有名称,所在城市。

      书籍模型: 书籍有书名,一本书可能会有多个作者,一个作者也可以写多本书,所以作者和书籍的关系就是多对多的关联关系(many-to-many);一本书只应该由一个出版商出版,所以出版商和书籍是一对多关联关系(one-to-many)。

      大概模型图:(骨架相同,里面的参数跟下面要写的类型不尽相同)

      模型建立如下:

    在框架中的models里面编辑内容:
    
    from django.db import models
    
    to指向表,to_field指向你关联的字段,不写这个,默认会自动关联主键字段,on_delete级联删除

     如果编辑设置不级联,也必须加上null=True

    class Author(models.Model):
    #id可以不用谢,会自动生成主键自增形式的,to_filed不写也会自动对应对应表的主键,默认级联
        name=models.CharField(max_length=32)
        age=models.IntegerField()
        authordetail=models.OneToOneField(to='AuthorDetail')
    #与AuthorDetail建立一对一的关系,一对一的这个关系字段写在两个表的任意一个表里面都可以
    
    class AuthorDetail(models.Model):
        birthday=models.DateField()
        telephone=models.BigIntegerField()
        addr=models.CharField(max_length=64)
    
    class Publish(models.Model):
        name=models.CharField(max_length=32)
        city=models.CharField(max_length=32)
    
    class Book(models.Model):
        title=models.CharField(max_length=32)
        price=models.DecimalField(max_digits=5,decimal_places=2)
    
     # 与Publish建立一对多的关系,外键字段建立在多的一方,字段publish如果是外键字段,那么它自动是int类型
        publish=models.ForeignKey(to='Publish')
    
    #与Author表建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表,并且注意一点,你查看book表的时候,你看不到这个字段,因为这个字段就是创建第三张表的意思,不是创建字段的意思,所以只能说这个book类里面有authors这个字段属性
    
        authors=models.ManyToManyField(to='Author')
    
    #多对多的表关系,在sql里需要手动创建一个第三张表,然后写上两个字段,每个字段外键关联到另外两张多对多关系的表,orm里会自动帮我们建立第三张表
    (了解:关于多对多表的三种创建方式)
    方式一:自行创建第三张表
    
    class Book(models.Model):
        title = models.CharField(max_length=32, verbose_name="书名")
    
    
    class Author(models.Model):
        name = models.CharField(max_length=32, verbose_name="作者姓名")
    
    
    # 自己创建第三张表,分别通过外键关联书和作者
    class Author2Book(models.Model):
        author = models.ForeignKey(to="Author")
        book = models.ForeignKey(to="Book")
    
        class Meta:
            unique_together = ("author", "book")
    方式二:通过ManyToManyField自动创建第三张表
    
    
    class Book(models.Model):
        title = models.CharField(max_length=32, verbose_name="书名")
    
    
    # 通过ORM自带的ManyToManyField自动创建第三张表
    class Author(models.Model):
        name = models.CharField(max_length=32, verbose_name="作者姓名")
        books = models.ManyToManyField(to="Book", related_name="authors")
    方式三:设置ManyTomanyField并指定自行创建的第三张表
    
    
    class Book(models.Model):
        title = models.CharField(max_length=32, verbose_name="书名")
    
    
    # 自己创建第三张表,并通过ManyToManyField指定关联
    class Author(models.Model):
        name = models.CharField(max_length=32, verbose_name="作者姓名")
        books = models.ManyToManyField(to="Book", through="Author2Book", through_fields=("author", "book"))
        # through_fields接受一个2元组('field1','field2'):
        # 其中field1是定义ManyToManyField的模型外键的名(author),field2是关联目标模型(book)的外键名。
    
    
    class Author2Book(models.Model):
        author = models.ForeignKey(to="Author")
        book = models.ForeignKey(to="Book")
    
        class Meta:
            unique_together = ("author", "book")

    #当我们需要在第三张关系表中存储额外的字段时,就要使用第三种方式,但是当我们使用第三种方式创建多对多关联关系时,就无法使用orm提供的set、add、remove、clear方法来管理多对多的关系了,需要通过第三张表的model来管理多对多关系。
    创建多表之间关系时,需要的一些参数:
    to
        设置要关联的表。
    
    to_field
        设置要关联的字段。
        
    on_delete
        同ForeignKey字段。
    创建一对一关系字段时的一些参数
    to
        设置要关联的表
    
    to_field
        设置要关联的表的字段
    
    related_name
        反向操作时,使用的字段名,用于代替原反向查询时的'表名_set'。
    related_query_name
        反向查询操作时,使用的连接前缀,用于替换表名。
    
    on_delete
        当删除关联表中的数据时,当前表与其关联的行的行为。
    创建一对多字段时的一些参数
    多对多的参数:
        to
            设置要关联的表
    
        related_name
            同ForeignKey字段。
    
        related_query_name
            同ForeignKey字段。
        through
            在使用ManyToManyField字段时,Django将自动生成一张表来管理多对多的关联关系。
    
            但我们也可以手动创建第三张表来管理多对多关系,此时就需要通过        
        through来指定第三张表的表名。
    
        through_fields
            设置关联的字段。
    
        db_table
            默认创建第三张表时,数据库中表的名称。 
    创建多对多时的一些参数

    表记录的添加,删除,更改记录

    一对多的方式:

    # 书与出版社一对多关系:数据的增添方法一:
        # publish_obj=models.Publish.objects.filter(name='红浪漫出版社')[0]
        # models.Book.objects.create(
        #     title='python',
        #     price=100,
        #     publish=publish_obj
        # )
    #出版社对象作为值给publish,其实就是自动将publish字段变成publish_id,然后将publish_obj的id给取出来赋值给publish_id字段
    # 数据的增添方法二: (这种方式来添加数据用的比较多)
    # models.Book.objects.create(
       # title='html',
       # price=89,
      # publish_id=1 # )
    #直接可以写id值,注意字段属性的写法和上面不同,这个是publish_id=xxx,上面是publish=xxx。

    删除和更新操作跟但单表的操作一样:
    一对多的删除数据操作:
    models.Book.objects.filter(id=1).delete()
    一对多的更新数据操作:
    models.Book.objects.filter(id=2).update(price=136)
     

    一对一的方式:

    作者与作者详细信息的一对一关系
    一对一的增添方式:
        models.Author.objects.create(
            name='无助夏梦',
            age=63,
            authordetail=models.AuthorDetail.objects.get(id=1)
        )
        一对一的另一种增添方式:
        models.Author.objects.create(
            name='卿倪',
            age=28,
            authordetail_id=3,
        )

    一对一的更改操作:
    models.Author.objects.filter(id=3).update(age=19)

    一对一的删除操作:
    models.Author.objects.filter(id=2).delete()

    多对多的方式:

    # 书籍与作者的多对多关系:
    # 多对多的增加信息操作:
    #     book_obj=models.Book.objects.create(
    #         title='java',app01_publish
    #         price=126,
    #         publish_id=3
    #     )
    生成一个书籍对象
    #     author1=models.Author.objects.get(id=1)
    #     author3=models.Author.objects.get(id=3)
    在Author表中取出两个model对象
    #     book_obj.authors.add(author1,author3)
    在第三张表中添加出书籍与作者的对应关系,此时编辑的是一个书籍对象对应的两个作者
    
    第二种多对多的创建方法,这种方式用的最多
    #     book_obj=models.Book.objects.get(id=3)
    #     book_obj.authors.add(*[2,3])
    
    第三种多对多的创建方式
    #     book_obj=models.Book.objects.get(id=3)
    #     book_obj.authors.add(1,2)

     被添加的表,添加信息随便添加,删除信息不能随便删除;添加的表,删除随便删除,添加则不能随便添加

    多对多方式的删除和更新
    
    多对多的其他常用示例:
    book_obj.authors.remove()      # 将某个特定的对象从被关联对象集合中去除。   
    book_obj.authors.remove(*[1,2]),将多对多的关系数据删除
    book_obj.authors.clear()       #清空被关联对象集合
    book_obj.authors.set()         #先清空再设置  
    
    事例:
    book_obj = models.Book.objects.filter(nid=4)[0]
        # book_obj.authors.remove(2) #将第三张表中的这个book_obj对象对应的那个作者id为2的那条记录删除
        # book_obj.authors.clear()  #将对应的信息全部清空
        # book_obj.authors.set('2') #先清除掉所有的关系数据,然后只给这个书对象绑定这个id为2的作者,所以只剩下一条记录  3---2,比如用户编辑数据的时候,选择作者发生了变化,那么需要重新选择,所以我们就可以先清空,然后再重新绑定关系数据,注意这里写的是字符串,数字类型不可以
        book_obj.authors.set(['1',]) #这么写也可以,但是注意列表中的元素是字符串

    基于对象的跨表查询

    正向查询:关系字段写在哪个表,我从这个表跨到另一个表查找的内容就是正向查询,反之,就是反向查询

    正向查询靠属性,根据表格里的外键查找到外键绑定的表的信息,再直接查询相关的具体信息

    反向查询:反向查询的表要小写就可找到相对应的那个表(类名小写_set:一对多,多对多这样写)以这种格式查找

    一对多查询:

    一对多查询:(正向查询)
        查询主键为2的书籍出版社所在的城市
        book_obj=models.Book.objects.get(id=2)
        ret=book_obj.publish.city
        print(ret)
        return HttpResponse('OK')
    一对多查询:(反向查询)
        查询20期出版社出版过那些书籍
        publish_obj=models.Publish.objects.filter(name='20期出版社').first()
        book_list=publish_obj.book_set.all()
        print(book_list)

     一对一查询:

    一对一查询:(正向查询)
        挽风是那个地方的
        author_obj=models.Author.objects.get(name='挽风')
        addr_info=author_obj.authordetail.addr
        print(addr_info)
    一对一查询: (反向查询) 看一下谁在昌平 ad_obj
    =models.AuthorDetail.objects.filter(addr='昌平')[0] print(ad_obj.author.name)

     多对多查询:

    多对多查询:(正向查询)
        linux的作者是谁
        book_obj=models.Book.objects.filter(title='linux').first()
        authors_list=book_obj.authors.all()
        print(authors_list)
    
    多对多查询:  (反向查询)   
         挽风写了哪些书
        author_obj=models.Author.objects.get(name='挽风'print(author_obj.book_set_all())    

    基于双下划线的跨表查询(基于join所实现)

      基于双下划线的查询:正向查询按照属性查询,反向查询则按照表名的小写来进行连接哪张。,一对一,多对多,一堆多都是一个写法,在写orm查询的时候,因为走的时join连表操作,所以写的表哪个在前,哪个在后都没问题。

    双下划线一对多方式跨表查询

        一对多查询:
        查询红浪漫出版社出版过的所有书籍名字和价格
        (正向查询):正向查询找属性,根据属性加上双下划线后跟上关联的表的属性进行匹配
        book_obj=models.Book.objects.filter(publish__name='红浪漫出版社').values('title','price')
        print(book_obj)
        (反向查询)反向查询找表名,根据跟我关联的表名加上双下划线后跟上相关的属性进行查询
        pub_obj=models.Publish.objects.filter(name='红浪漫出版社').values('book__title','book__price')
        print(pub_obj)

    双下划线一对一方式跨表查询

    一对一查询:
        查找挽风在那个地方
        (正向查询)
        ret=models.Author.objects.filter(name='挽风').values_list('authordetail__addr')
        print(ret)
        (反向查询)
        ret=models.AuthorDetail.objects.filter(author__name='挽风').values('addr')
        print(ret)

    双下划线多对多方式跨表查询

       多对多查询:
        python的作者是谁
        (正向查询)
        ret=models.Book.objects.filter(title='python').values('authors__name')
        print(ret)
    
        (反向查询)
        ret=models.Author.objects.filter(book__title='python').values_list('name')
        print(ret)

    进阶联系(连续跨表)

    # 练习: 查询人民出版社出版过的所有书籍的名字以及作者的姓名
    
    
        # 正向查询
        queryResult=Book.objects
                .filter(publish__name="人民出版社")
                .values_list("title","authors__name")
        # 反向查询
        queryResult=Publish.objects
                  .filter(name="人民出版社")
                  .values_list("book__title","book__authors__age","book__authors__name")
    
    
    # 练习: 手机号以151开头的作者出版过的所有书籍名称以及出版社名称
    
    
        # 方式1:
        queryResult=Book.objects
                .filter(authors__authorDetail__telephone__regex="151")
                .values_list("title","publish__name")
        # 方式2:    
        ret=Author.objects
                  .filter(authordetail__telephone__startswith="151")
                  .values("book__title","book__publish__name")

    related_name方式

    当进行反向查询的时候,如果定义了related_name ,则用related_name替换表名,例如:

    在models里的函数里定义数据类型时可以这样写,这样当反向查询进行查找表名时,则必须写上修改后的名即可查找到,原来的表名失效
    publish = ForeignKey(to='Publish', related_name='bookList')

    聚合模块,F查询和Q查询,分组查询

    聚合查询

    聚合查询,aggregate()是Queryset的一个终止语句,只要调用,它返回一个包含键值对的字典,键的名称是按照字段和聚合函数的名称自动生成的,之后的querset
    方法也不能用,所以一般写在最后面,

    from django.db.models import Avg,Max,Min
    
    计算所有图书的平均价格
        ret=models.Book.objects.all().aggregate(Avg('price'))#或者给它起名字:aggretate(a=Avg('price'))
        print(ret)   #{'price__avg': 99.5}/{'a': 99.5}
    
    
    如果你希望生成不止一个聚合,你可以向aggregate()子句中添加另一个参数。所以,如果你也想知道所有图书价格的最大值和最小值,可以这样查询:
        Book.objects.aggregate(Avg('price'), Max('price'), Min('price')) 

    F查询

    我们构造的过滤器都只是将字段值与某个常量做比较。如果我们要对两个字段的值做比较,就需要用到F查询来做

        F查询:
    from django.db.models import F
    
    # 查询评论数大于收藏数的书籍
    Book.objects.filter(commentNum__lt=F('keepNum'))
    
    Django支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操作。
    # 查询评论数大于收藏数2倍的书籍
    Book.objects.filter(commentNum__lt=F('keepNum')*2)
    
    修改操作也可以使用:
    #将所有的书籍的价格都上涨10元    models.Book.objects.filter(price=F('price')).update(price=F('price')+10)
    
    #将所有大于平均价格的图书降价10元
    ret = models.Book.objects.all().aggregate(Avg('price'))    models.Book.objects.filter(price__gt=ret['price__avg']).update(price=F('price')-10)

    Q查询

      filter() 等方法中的关键字参数查询都是一起进行“AND” 的。 如果你需要执行更复杂的查询(例如OR 语句),你可以使用Q对象

        # Q查询:
    
    from django.db.models import Q
    
        # Q对象可以使用&(与),|(或),~(非)操作符组合起来,当一个操作符在两个Q对象使用时,它产生一个新的Q对象
    
        # ret=models.Book.objects.filter(Q(price__gt=50)|Q(price=146))
        # print(ret.values_list())
    
       可以组合& 和|  操作符以及使用括号进行分组来编写任意复杂的Q 对象。同时,Q 对象可以使用~ 操作符取反,这允许组合正常的查询和取反(NOT) 查询:
    bookList=Book.objects.filter(Q(authors__name="yuan") & ~Q(publishDate__year=2017)).values_list("title")
    bookList=Book.objects.filter(Q(Q(authors__name="yuan") & ~Q(publishDate__year=2017))&Q(id__gt=6)).values_list("title") #可以进行Q嵌套,多层Q嵌套等,其实工作中比较常用
    
       查询函数可以混合使用Q 对象和关键字参数。所有提供给查询函数的参数(关键字参数或Q 对象)都将"AND”在一起。但是,如果出现Q 对象,它必须位于所有关键字参数的前面。例如:
    bookList=Book.objects.filter(Q(publishDate__year=2016) | Q(publishDate__year=2017),
                                  title__icontains="python"  #也是and的关系,但是Q必须写在前面
                                 )

    综合查询练习题(死亡五小)

    #1 查询每个作者的姓名以及出版的书的最高价格
        ret = models.Author.objects.values('name').annotate(max_price=Max('book__price'))
        print(ret) #注意:values写在annotate前面是作为分组依据用的,并且返回给你的值就是这个values里面的字段(name)和分组统计的结果字段数据(max_price)
        # ret = models.Author.objects.annotate(max_price=Max('book__price')).values('name','max_price')#这种写法是按照Author表的id字段进行分组,返回给你的是这个表的所有model对象,这个对象里面包含着max_price这个属性,后面写values方法是获取的这些对象的属性的值,当然,可以加双下划线来连表获取其他关联表的数据,但是获取的其他关联表数据是你的这些model对象对应的数据,而关联获取的数据可能不是你想要的最大值对应的那些数据
    # 2 查询作者id大于2作者的姓名以及出版的书的最高价格
        ret = models.Author.objects.filter(id__gt=2).annotate(max_price=Max('book__price')).values('name','max_price')#记着,这个values取得是前面调用这个方法的表的所有字段值以及max_pirce的值,这也是为什么我们取关联数据的时候要加双划线的原因
        print(ret)
    
    #3 查询作者id大于2或者作者年龄大于等于20岁的女作者的姓名以及出版的书的最高价格
        # ret = models.Author.objects.filter(Q(id__gt=2)|Q(age__gte=20),sex='female').annotate(max_price=Max('book__price')).values('name','max_price')
    #4 查询每个作者出版的书的最高价格 的平均值
        # ret = models.Author.objects.values('id').annotate(max_price=Max('book__price')).aggregate(Avg('max_price')) #{'max_price__avg': 555.0} 注意,aggregate是queryset的终止句,得到的是字典
        # ret = models.Author.objects.annotate(max_price=Max('book__price')).aggregate(Avg('max_price')) #{'max_price__avg': 555.0} 注意,aggregate是queryset的终止句,得到的是字典
    
    #5 每个作者出版的所有书的价格以及最高价格的那本书的名称(通过orm玩起来就是个死题,需要用原生sql)
        '''
        select title,price from (select app01_author.id,app01_book.title,app01_book.price from app01_author INNER JOIN app01_book_authors on app01_author.id=
    app01_book_authors.author_id INNER JOIN app01_book on app01_book.id=
    app01_book_authors.book_id ORDER BY app01_book.price desc) as b  GROUP BY id
    '''
    
        print(ret)
  • 相关阅读:
    Link assemblies causes app crashes if you have an EditText
    Link causes xamarin Android binding library project to crash
    Linux系统目录结构详解
    Centos Linux系统优化二
    Centos Linux系统优化一
    rm命令详解
    mv命令详解
    cp命令详解
    echo命令详解
    touch命令详解
  • 原文地址:https://www.cnblogs.com/sikuaiqian/p/10933138.html
Copyright © 2011-2022 走看看