Day 51 Django_模型层_多表操作

多表操作

创建模型

实例:我们来假定下面这些概念,字段和关系
作者模型:一个作者有姓名和年龄。
作者详细模型:把作者的详情放到详情表,包含生日,手机号,家庭住址等信息。作者详情模型和作者模型之间是一对一的关系(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',)

 

生成表如下:





注意事项:

    • 表的名称myapp_modelName,是根据 模型中的元数据自动生成的,也可以覆写为别的名称  
    • id 字段是自动添加的
    • 对于外键字段,Django 会在字段名上添加"_id" 来创建数据库中的列名
    • 这个例子中的CREATE TABLE SQL 语句使用PostgreSQL 语法格式,要注意的是Django 会根据settings 中指定的数据库类型来使用相应的SQL 语句。
    • 定义好模型之后,你需要告诉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"
                             )

 

posted @ 2020-04-25 17:08  亦双弓  阅读(102)  评论(0编辑  收藏  举报