• ORM之多表操作


    一、创建模型

    from django.db import models
    
    # Create your models here.
    
    class Book(models.Model):
        nid = models.AutoField(primary_key=True)
        title = models.CharField( max_length=32)
        publishDate=models.DateField()
        price=models.DecimalField(max_digits=5,decimal_places=2)
    
        # 与Publish建立一对多的关系,外键字段建立在多的一方
        publish=models.ForeignKey(to="Publish",to_field="nid",on_delete=models.CASCADE)
        # 与Author表建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表
        authors=models.ManyToManyField(to='Author',)    
    
    class Author(models.Model):
        nid = models.AutoField(primary_key=True)
        name=models.CharField( max_length=32)
        age=models.IntegerField()
    
        # 与AuthorDetail建立一对一的关系
         authorDetail=models.OneToOneField(to="AuthorDetail",on_delete=models.CASCADE)
    
    class AuthorDetail(models.Model):
    
        nid = models.AutoField(primary_key=True)
        birthday=models.DateField()
        telephone=models.BigIntegerField()
        addr=models.CharField( max_length=64)
    
    class Publish(models.Model):
        nid = models.AutoField(primary_key=True)
        name=models.CharField( max_length=32)
        city=models.CharField( max_length=32)
        email=models.EmailField()

    注意事项:

    1、可以在任意一张表的模型下自定义这张表的元类信息,来覆盖一些自动生成的元数据,自定义方式可参考如下:

    #以Publish表举例
    
    class Publish(models.Model):
        nid = models.AutoField(primary_key=True)
        name=models.CharField( max_length=32)
        city=models.CharField( max_length=32)
        email=models.EmailField()
    
        class Meta:
             #app_label这个选项只在一种情况下使用,就是你的模型类不在默认的应用程序包下的models.py文件中,这时候你需要指定你这个模型类是那个应用程序的。比如你在其他地方写了一个模型类,而这个模型类是属于myapp的,那么你这是需要指定为:
            app_label="myapp"     
            db_table="app01book"    #用于指定自定义数据库表名
            unique_together=["title","price"]   #设置联合唯一,这两个字段的值在数据库中存储的值不能完全一样
            verbose_name="书籍"   #给你的模型类起一个更可读的名字
            ordering=["price"]  #数据存储按照price字段排序,默认为升序,如果不指定排序的字段,django会默认按照主键升序来排序

    元类信息设置完成之后您可以使用如下命令来测试元数据设置的值:

    >>>Book._meta.verbose_name
                     
                    
    >>>Book._meta.model_name
                     
    
    >>>Book._meta.app_label
                    

    2、对于外键字段,Django 会在字段名上添加"_id" 来创建数据库中的列名。

    二、添加表记录

    一对多添加:

      方式一:

    book = Book.objects.create(
            title='冰与火之歌',
            price=320,
            publishDate = '2010-12-08',
            publish_id = 2
        )

      方式二:

    pub_obj = Publish.objects.filter(name = '人民出版社').first()
    book = Book.objects.create(
            title='java',
            price=88,
            publishDate='2015-11-24',
            publish=pub_obj
        )

    核心:book.publish为book模型对象所对应的出版社对象,book.publish_id为book对象所对应的出版社的主键值。

    多对多添加

      方式一:

    feng= Author.objects.filter(name='feng').first()
    zhao= Author.objects.filter(name='zhao').first()
    book.authors.add(feng,zhao)

      方式二:

    book.authors.add(1,2)

      方式三: 

    book.authors.add(*[1,2])

    多对多关系其它常用API:

    book.authors.remove(1)   #将主键值为1的作者对象从被关联对象集合中去除
    book.authors.clear() #清空被关联集合对象 book.authors.set(1) #先清空在设置

    三、跨表查询

    基于对象的跨表查询(子查询)

    一对多查询(Publish与Book):

    正向查询:关联属性所在的表查询关联表记录

    正向查询按字段:book.publish
    Book-------------------------------------->Publish
    <---------------------------------------
    反向查询:表名小写_set.all():pub_obj.book_set.all()
    #1 查询python这本书出版社的名字和邮箱
         book = Book.objects.filter(title = 'python').first()
         print(book.publish)    #与book这本书关联的出版社对象
         print(book.publish.name)   #与book这本书关联的出版社对象的名字
         print(book.publish.email)
    #2 查询苹果出版社出版的所有的书籍的名称
         pub_obj = Publish.objects.get(name='苹果出版社')
         print(pub_obj.book_set.all())  #queryset
         print(pub_obj.book_set.all().values("title"))  # queryset,列表里面放的一个个字典

    多对多查询(Author与Book):

         正向查询按字段 book.authors.all()
    Book -------------------------------------->Author
    <--------------------------------------
    反向查询按表名小写_set.all(): feng.book_set.all()
    # 查询python这本书籍的作者的年龄
        book = Book.objects.filter(title='python').first()
        ret = book.authors.all().values('age')
    
    # 查询feng出版过的所有的书籍名称
        feng= Author.objects.filter(name = 'feng').first()
        print(feng.book_set.all())

    一对一查询(Author与AuthorDetail)

            正常查询安字段:author_obj.authorDetail
    Author -----------------------------------------> AuthorDetail
    <------------------------------------------
    反向查询按表名小写:authordetail_obj.author
     # 查询alex的手机号
        author_obj = Author.objects.filter(name='feng').first()
        print(author_obj .authorDetail.tel)
    
    # 查询手机号为110的作者的名字
        ad = AuthorDetail.objects.filter(tel = 110).first()
        print(ad.author.name)

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

    KEY:无论是一对一、一对多还是多对多都遵循正向查询按字段,反向查询按表名小写。

      # 1 查询python这本书出版社的名字
        ret = Book.objects.filter(title='python').values('publish__name')
    
        ret = Publish.objects.filter(book__title='python').values('name')
    
        # 2 查询苹果出版社出版的所有的书籍的名称
        ret = Publish.objects.filter(name='苹果出版社').values('book__title')
    
        ret = Book.objects.filter(publish__name='苹果出版社').values('title')
    
        # 3 查询python这本书籍的作者的年龄
        ret = Book.objects.filter(title='python').values('authors__age')
    
        ret = Author.objects.filter(book__title='python').values('age')
    
        # 4 查询feng出版过的所有的书籍名称
        ret = Book.objects.filter(authors__name='feng').values('title')
    
        ret = Author.objects.filter(name='feng').values('book__title')
    
        # 5 查询手机号为110的作者的名字
        ret = AuthorDetail.objects.filter(tel=110).values('author__name')
    
        ret = Author.objects.filter(ad__tel=110).values('name')
    
        # 6 查询feng的手机号
        ret = Author.objects.filter(name='feng').values('ad__tel')
    
        ret = AuthorDetail.objects.filter(author__name='feng').values('tel')
    
        ########### 连续跨表  ###############
    
        # 查询苹果出版社出版过的所有书籍的名字以及作者的姓名
    
        ret = Publish.objects.filter(name='苹果出版社').values('book__title','book__authors__name')
        ret = Book.objects.filter(publish__name='苹果出版社').values('title','authors__name')
    
        # 手机号以110开头的作者出版过的所有书籍名称以及出版社名称
    
        #方式一
        ret = Book.objects.filter(authors__ad__tel__startswith=110).values('title','publish__name')
    
        #方式二
        ret = Author.objects.filter(ad__tel__startswith=110).values('book__title','book__publish__name')
    
        #方式三
        ret = AuthorDetail.objects.filter(tel__startswith=110).values('author__book__title','author__book__publish__name')

    related_name

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

    1
    publish = ForeignKey(Blog, related_name='bookList')
    # 练习: 查询人民出版社出版过的所有书籍的名字与价格(一对多)
    
    # 反向查询 不再按表名:book,而是related_name:bookList
    
    
    queryResult=Publish.objects.filter(name="人民出版社").values_list("bookList__title","bookList__price")

    四、聚合(aggregate)

    1
    2
    3
    4
    # 计算所有图书的平均价格
        >>> from django.db.models import Avg
        >>> Book.objects.all().aggregate(Avg('price'))
        {'price__avg'34.35}

    aggregate()QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。如果你想要为聚合值指定一个名称,可以向聚合子句提供它。

    1
    2
    >>> Book.objects.aggregate(average_price=Avg('price'))
    {'average_price'34.35}

    如果你希望生成不止一个聚合,你可以向aggregate()子句中添加另一个参数。所以,如果你也想知道所有图书价格的最大值和最小值,可以这样查询:

    1
    2
    3
    >>> from django.db.models import Avg, MaxMin
    >>> Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
    {'price__avg'34.35'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}

    五、分组

    单表分组查询:

    key:annotate()前values里是哪一个字段,就按哪一个字段group by

        # 查询书籍表每一个出版社id以及对应的书籍个数
        ret = Book.objects.values('publish_id').annotate(c=Count(1))
    
        # 查询每一个部门的名称以及对应员工的平均薪水
        ret = Emp.objects.values('dep').annotate(avg_salary = Avg('salary'))
    
        # 查询每一个省份的名称以及对应的员工最大年龄
        ret = Emp.objects.values('pro').annotate(max_age = Max('age'))

    注意:单表按主键分组没有意义

    跨表分组查询:

      # 查询每一个出版社的名称以及对应的书籍平均价格
        ret = Publish.objects.annotate(avg_price = Avg('book__price')).values('name','avg_price')
    
        # 查询每一个作者的名字以及出版的书籍的最高价格
        ret = Author.objects.annotate(max_price=Max('book__price')).values('name','max_price')
    
        # 查询每一个书籍的名称以及对应的作者的个数
        ret = Book.objects.annotate(c=Count('authors')).values('c')
    
        #查询作者数不止一个的书籍名称以及作者个数
       Book.objects.annotate(c=Count('authors__name')).filter(c__gt=1).values('title','c')
    
        #根据一本图书作者数量的多少对查询集 QuerySet进行排序
        Book.objects.annotate(c=Count('authors__name')).order_by('c')
    
        #统计每一本以py开头的书籍的名称以及作者个数
        Book.objects.filter(title__startswith='py').annotate(c=Count('authors__name')).values('title','c')

    六、F查询与Q查询

    F查询:

    在上面所有的例子中,我们构造的过滤器都只是将字段值与某个常量做比较。如果我们要对两个字段的值做比较,那该怎么做呢?

    Django 提供 F() 来做这样的比较。F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。

    1
    2
    3
    4
    # 查询评论数大于收藏数的书籍
     
       from django.db.models import F
       Book.objects.filter(commnetNum__lt=F('keepNum'))

    Django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操作。

    1
    2
    # 查询评论数大于收藏数2倍的书籍
        Book.objects.filter(commnetNum__lt=F('keepNum')*2)

    修改操作也可以使用F函数,比如将每一本书的价格提高30元:

    1
    Book.objects.all().update(price=F("price")+30) 

    Q查询:

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

    1
    2
    from django.db.models import Q
    Q(title__startswith='Py')

    Q 对象可以使用& 和| 操作符组合起来。当一个操作符在两个Q 对象上使用时,它产生一个新的Q 对象。

    1
    bookList=Book.objects.filter(Q(authors__name="yuan")|Q(authors__name="egon"))

    等同于下面的SQL WHERE 子句:

    1
    WHERE name ="yuan" OR name ="egon"

    你可以组合& 和|  操作符以及使用括号进行分组来编写任意复杂的Q 对象。同时,Q 对象可以使用~ 操作符取反,这允许组合正常的查询和取反(NOT) 查询:

    1
    bookList=Book.objects.filter(Q(authors__name="yuan") & ~Q(publishDate__year=2017)).values_list("title")

    查询函数可以混合使用Q 对象和关键字参数。所有提供给查询函数的参数(关键字参数或Q 对象)都将"AND”在一起。但是,如果出现Q 对象,它必须位于所有关键字参数的前面。例如:

    1
    2
    3
    bookList=Book.objects.filter(Q(publishDate__year=2016) | Q(publishDate__year=2017),
                                  title__icontains="python"
                                 )

    Q查询的另外一种写法:

       q=Q()
        q.connector="or"   #如果不写默认为and
        q.children.append(("title__startswith","py"))
        q.children.append(("price__gt",100))

    注意:q.children.append()元组中的数据可以进行字符串的拼接,如下

    val = request.GET.get('q')
            field = request.GET.get('field')
            if val:
                q = Q()
                q.children.append((field + '__contains', val))  # 注意里面放的是元组
                customer_list = customer_list.filter(q)

     补充知识点:中介模型

    试想一下有student和score两个表模型,两张表是多对多的关系,此时可以生成student_score第三个表,但是通过ManyToMany自动生成的第三张表,表里面的字段也是自动生成的,第三张表可以自动生成三个字段:id   student_id   score_id,但此时我们的需求是在第三张表里面还要有一个score字段,此时中介模型就可以很好的帮我们解决这个问题。

    class Student(models.Model):
        name = models.CharField( max_length=32)                                    
      courses=models.ManyToManyField("Courses",through="Score")
    #through='score'可以翻译成第三张score表不需要通过ManyToMany自动生成,要用自己写的score表,在自己写的score表中,可以随表写自己业务需求的字段,orm查询语句可以照常使用 class Course(models.Model): name = models.CharField( max_length=32) class Score(models.Model):
      student=models.ForeignKey("Student")
      course=models.ForeignKey("Course")
      score=models.IntegerField()
  • 相关阅读:
    C#实现Winform自定义半透明遮罩层
    C# winform 窗体弹出选择目录或文件 的对话框
    C# winform 窗体弹出选择目录或文件 的对话框
    python语言实现贪吃蛇
    python语言实现贪吃蛇
    用python写一个简单的表白代码
    用python写一个简单的表白代码
    用python写一个简单的表白代码
    百练2810:完美立方
    百练2810:完美立方
  • 原文地址:https://www.cnblogs.com/fengchong/p/9879898.html
Copyright © 2020-2023  润新知