美文网首页
Django模型层_多表操作

Django模型层_多表操作

作者: 采星星的小太阳BavaLI | 来源:发表于2020-04-28 18:35 被阅读0次

    创建模型
    实例:我们来假定下面这些概念,字段和关系
    作者模型:一个作者有姓名和年龄。
    作者详细模型:把作者的详情放到详情表,包含生日,手机号,家庭住址等信息。作者详情模型和作者模型之间是一对一的关系(one-to-one)
    出版商模型:出版商有名称,所在城市以及email。
    书籍模型: 书籍有书名和出版日期,一本书可能会有多个作者,一个作者也可以写多本书,所以作者和书籍的关系就是多对多的关联关系(many-to-many);一本书只应该由一个出版商出版,所以出版商和书籍是一对多关联关系(one-to-many)。
    模型建立如下:

    from django.db import models
    # Create your models here.
    
    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()
    
    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',)
    

    注意事项:

    1.表的名称myapp_modelName,是根据 模型中的元数据自动生成的,也可以覆写为别的名称  
    2.id 字段是自动添加的
    3.对于外键字段,Django 会在字段名上添加"_id" 来创建数据库中的列名
    这个例子中的CREATE TABLE SQL 语句使用PostgreSQL 语法格式,要注意的是Django 会根据settings 中指定的数据库类型来使用相应的SQL 语句。
    4.定义好模型之后,你需要告诉Django 使用这些模型。你要做的就是修改配置文件中的INSTALL_APPSZ中设置,在其中添加models.py所在应用的名称。
    外键字段 ForeignKey 有一个 null=True 的设置(它允许外键接受空值 NULL),你可以赋给它空值 None

    补充下面示例中用到的两张表:

    注意:如果在一个 models.py 中,加入了以下这两张表但同时把上面已经 5 张表的 class 注释掉了,那么ORM会先把上面已经生产的 5 张表删除,然后在进行创建,所以这时要做的就是加入下面两个 calss,上面已存在的保持不变即可。当对已有的 class 进行增加字段时,原有的字段也是保持不变,需要再新加的字段中输入默认值:default=0 即可。

    class Emp(models.Model):
        nid = models.AutoField(primary_key=True)
        name = models.CharField(max_length=6)
        age = models.IntegerField()
        salary = models.IntegerField()
        province = models.CharField(max_length=12)
        dep = models.ForeignKey(to="Dep",to_field='nid',on_delete=models.CASCADE)
    
    
    class Dep(models.Model):
        nid = models.AutoField(primary_key=True)
        name = models.CharField(max_length=12)
    

    添加表纪录

    def add(requset):
        ####### 添加 一对一 的关系数据 #######
        # 出版社表
        pub = Publish.objects.create(name='天空出版社',city='大理',email='dl@qq.com')
        
        # 作者 作者详情表
        aud = AuthorDetail.objects.create(birthday='1992-1-1', telephone=130000, addr='中国')
        Author.objects.create(name='egon', age=1, authorDetail_id=5)
    
        ####### 添加 一对多 的关系数据 #######
        # 为book表绑定出版社
        # 方法1
        book = Book.objects.create(title='少有人的路',publishDate='2018-12-23',price=68,publish_id=1)
    
        # 方法2
        pub_obj = Publish.objects.filter(name='天空出版社').first()
        book = Book.objects.create(title='python',publishDate='2008-4-7',price=38,publish=pub_obj)
    
        print(book.title)
        print(book.price)
        print(book.publish) # 这本书关联的出版社对象
        print(book.publish_id)
    
        # 查询 book 所对应的出版社 email 信息
        book_obj = Book.objects.filter(nid=1).first()
        book_email = book.publish.email
    
        ####### 添加 一对多 的关系数据 #######
        book = Book.objects.filter(title='python').first()
        alex = Author.objects.filter(name='alex').first()
        egon = Author.objects.filter(name='egon').first()
    
        book.authors.add(alex,egon)
        book.authors.add(5,6)   # 作者表的主键
        book.authors.add(*[5,6])   # 作者表的主键
    
    
        # 解除多对多的关系
        book = Book.objects.filter(title='python').first()
        book.authors.remove(5)
        book.authors.remove(5,6)
        book.authors.remove(*[5,6])
    
        # 清空所有
        book.authors.clear()
    
    
        # 查看与本书相关的所有作者
        book = Book.objects.filter(title='python').first()
        print(book.authors.all().values('name'))  # <QuerySet [{'name': 'alex'}, {'name': 'egon'}]>
        return HttpResponse("b_a")
    

    基于对象的跨表查询
    基于对象查询
    一对多

    def query(requset):
        ########## 基于基于对象查询 ##########
        # 一对多正向查询:查询书本的出版社名称
        book = Book.objects.filter(title='谈美').first()
        # b_p = book.publish.name   # 与本书相关的出版社对象
        b_p = book.publish.name
    
        # 一对多反向查询:查询出版社出版的书籍
        publish = Publish.objects.filter(name='商务出版社').first()
        pub = publish.book_set.all()
        print(pub)
        return HttpResponse(pub)
    
    ''' 
    A---B
    关联属性在A表
    正向查询:A---B
    反向查询:B---A
    
    #一对多查询
        正向查询:看字段
        方向查询:表名小写_set
        
                                  book.publish
        Book(关联属性) 《-----------------------------------》Publish
                        publish.book_set.all() # 返回值:queryset
    '''
    

    多对多

    # 多对多
        # 多对多正向查询:查询书本的作者名称
        book = Book.objects.filter(title='python').first()
        author_list = book.authors.all()
        for list in author_list:
            print(list.name)
    
        # 多对多反向查询:查询作者出版的书籍
        aut = Author.objects.filter(name='alex').first()
        book_list = aut.book_set.all()
        for list in book_list:
            print(list)
        return HttpResponse(list)
    
        ''' 
                A---B
                关联属性在A表
                正向查询:A---B
                反向查询:B---A
    
                #一对多查询
                    正向查询:看字段
                    方向查询:表名小写_set
    
                                            book.authors.all()
                    Book(关联属性) 《-----------------------------------》Publish
                                      Author.book_set.all()  # 返回值:queryset
            '''
    

    一对一

    # 一对一
        # 一对一正向查询:查询 alex 的手机号
        alex = Author.objects.filter(name='alex').first()
        alex_t = alex.authorDetail.telephone
        print(alex_t)
    
    
        # 一对一反向查询:通过手机号得到作者信息
        ad = AuthorDetail.objects.filter(telephone='120000').first()
        alex_name = ad.author.name
        alex_age = ad.author.age
        print(alex_age,alex_name)
    
        ''' 
                A---B
                关联属性在A表
                正向查询:A---B
                反向查询:B---A
    
                #一对一查询
                    正向查询:看字段
                    方向查询:表名小写
    
                                                        author.authorDetail
                    Author(关联属性:authorDetail) 《-----------------------------------》AuthorDetail
                                                        authorDetail.author  
            '''
    

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

    正向查询按字段,反向查询按表名小写,用来告诉 ORM 引擎 join 哪张表

    一对多

    # 一对多
        # 一对多的正向查询:查询书本出版社的名称
        b_p_name = Book.objects.filter(title='python').values("publish__name")
        print(b_p_name)     # <QuerySet [{'publish__name': '天空出版社'}]># 一对多的反向查询:查询出版社出版的书名
        p_b_name = Publish.objects.filter(book__title='python').values("name")
        print(p_b_name)     # <QuerySet [{'name': '天空出版社'}]>
        return HttpResponse(p_b_name)
    

    多对多

    # 多对多
        # 多对多的正向查询:查询 python 这书本的作者名称
        # 通过 book 表 join 与其关联的 Author 表
        autname = Book.objects.filter(title='python').values("authors__name")
        print(autname)      # <QuerySet [{'authors__name': 'alex'}, {'authors__name': 'egon'}]># 多对多的正向查询:查询出版过 python 的作者
        # 通过 Author 表 join 与其关联的 book 表
        name = Author.objects.filter(book__title='python').values("name")
        print(name)     # <QuerySet [{'name': 'alex'}, {'name': 'egon'}]>
        return HttpResponse(name)
    

    一对一

    # 一对一
        # 一对一的正向查询:查询作者的手机号
        phone = Author.objects.filter(name='alex').values("authorDetail__telephone")
        print(phone)    # <QuerySet [{'authorDetail__telephone': 120000}]># 一对一的反向查询:查询作者的手机号
        phone = AuthorDetail.objects.filter(author__name='alex').values('telephone')
        print(phone)    # <QuerySet [{'telephone': 120000}]>
        return HttpResponse(phone)
    

    连续跨表

    # 连续跨表
        # 手机号以 12 开头的作者出版过的书籍名称和出版社名称
        # 正向查询
        name = Book.objects.filter(authors__authorDetail__telephone__regex="12").values('title', 'publish__name')
        print(name)  # <QuerySet [{'title': 'python', 'publish__name': '天空出版社'}]>
    
        # 反向查询
        name = Author.objects.filter(authorDetail__telephone__startswith="12").values('book__title','book__publish__name')
        print(name)     # <QuerySet [{'book__title': 'python', 'book__publish__name': '天空出版社'}]>
        return HttpResponse('OK')
    
    
        # 查询人民出版社出版过的所有书籍的名字以及作者的姓名
        # 正向查询
        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")
    

    聚合和分组查询
    聚合:aggregate 表示使用聚合

    
    from django.db.models import Avg, Min, Max, Count
    def query(requset):
        # 聚合
        # 返回值:字典
        avg_p = Book.objects.all().aggregate(Avg("price"))
        print(avg_p)  # {'price__avg': Decimal('46.400000')}  存在默认名称:字段 + 聚合函数名
    
        p1 = Book.objects.all().aggregate(price_avg=Avg("price"))  # 自定义名称
        print(p1)  # {'price_avg': Decimal('46.400000')}  存在默认名称:字段 + 聚合函数名
    
        # 注意:当输出多个值时,其中一个使用聚合函数重命名,其它的也必须使用重名,即要用都用,要不用都不用
        p2 = Book.objects.all().aggregate(price_avg=Avg("price"), price_cou=Count("price"))  # 输出多个值
        print(p2)  # {'price_avg': Decimal('46.400000'), 'price_cou': 5}  存在默认名称:字段 + 聚合函数名
    
        return HttpResponse('OK')
    

    分组查询:annotate 返回值:QuerySet

    单表分组

    # 单表分组
        # 查询每一个部门的名称以及员工的平均薪水
        avg = Emp.objects.all().values('dep_id').annotate(Avg("salary"))
        print(avg)
        # <QuerySet [{'dep_id': 1, 'salary__avg': 2000.0}, {'dep_id': 2, 'salary__avg': 4000.0}]>
        return HttpResponse('OK')
    
        # 单表分组查询的ORM语法:单表模型.objects.values("group by 的字段").annotate(聚合函数("统计字段"))
        # 注意:在单表分组中,按照主键分组是毫无意义的
    
        # Emp.objects.all()             select * from Emp
        # Emp.objects.values('name')    select t.name form Emp t
    

    多表分组

    # 一对多
        # 查询每一个出版社的名称以及出版的书籍个数
        info = Book.objects.values('publish__name').annotate(Count("title"))
        info = Book.objects.values('publish__nid').annotate(c=Count("title")).values("publish__name", 'c')
        print(info)
        # <QuerySet [{'publish__name': '天空出版社', 'c': 2}, {'publish__name': '海洋出版社', 'c': 2}]>
    
        # 多对多
        # 查询每一个作者的名字以及出版过书籍的最高价值
        res = Author.objects.values('pk').annotate(max=Max("book__price")).values('name','max')
        print(res)
    
        # 多表分组查询的ORM语法:表模型.objects.values("pk").annotate(聚合函数("统计字段"))
    

    练习

    # 练习
        # 查询每一个书籍名称以及对应的作者个数
        book = Book.objects.values('pk').annotate(c=Count("authors__name")).values('title',"c")
        print(book)
    
        # 统计每一个以字母开头书籍的作者个数
        num =Book.objects.filter(authors__name__regex="^[a-z]").annotate(c=Count("authors__nid")).values("title","c")
        print(num)  # <QuerySet [{'title': 'pyhton', 'c': 2}, {'title': 'java', 'c': 1}]>
    
        # 统计不止一个作者的书籍
        num = Book.objects.values('pk').annotate(c=Count('authors__nid')).filter(c__gt=1).values('title',"c")
        print(num)
        return HttpResponse("OK")
    

    F 与 Q 查询
    F查询
    在上面所有的例子中,我们构造的过滤器都只是将字段值与某个常量做比较。如果我们要对两个字段的值做比较,那该怎么做呢?
    Django 提供 F() 来做这样的比较。F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。

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

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

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

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

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

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

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

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

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

    等同于下面的SQL WHERE 子句:

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

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

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

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

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

    相关文章

      网友评论

          本文标题:Django模型层_多表操作

          本文链接:https://www.haomeiwen.com/subject/sbuvwhtx.html