我是励志哥

django模型层之多表关系

一. 多表操作

  数据库表关系之关联字段与外键约束

  一对多

  book(多)

  publish(一)

  查询<<水浒传>>这本书出版社的地址: 

select publish_id from Book where title="水浒传"
select addr from Publish where id=1

  一旦确定了一对多的关系: 建立一对多的关系,在多的一方建立关联字段(publish_id)

  多对多:

  book


  author

  book2author:

  panshao出版过得书籍名称(子查询)

select id from author where name='panshao';    # 在作者表中拿出panshao
select book_id from book2author where author_id=1;   # 找出作者id等于1的书籍的id
select title from book where id=book_id;

  一旦确定表关系是多对对: 创建第三张表  id   book_id     author_id

  一对一:

  author

  authordetail

  一旦确定是一对多的关系: 建立一对多的关系=======>在多的表中建立关联字段   (一个出版社可以出版多本书)

  一旦确定为多对多的关系: 建立多对多的关系========> 创建第三张表(关联表): id和两个关联字段(一本书可以有多个作者,一个作者可以出版多本书)

  一旦确定一对一关系: 建立一对一的关系========>在两张表中任意一张表中建立关联字段+unique(一个作者只有一个详细信息)

  数据库表关系之创建关联表

from django.db import models


class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8, decimal_places=2)
    publish_time = models.DateField(auto_now_add=True)
    # 外键关系
    publish = models.ForeignKey(to='Publish') # 默认关联的是主键,如果相关联其他字段则可以使用to_field=
    authors = models.ManyToManyField(to='Author')  # 告诉数据库让建一个多对多的表,在这里只是标识

class Publish(models.Model):
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)
    # email = models.EmailField()


class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    authordetail = models.OneToOneField(to='AuthorDetail')


class AuthorDetail(models.Model):
    phone = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)

  注意事项:

  1. id字段是自动添加的

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

  3. 外键字段foreignkey有一个null=True 的设置(他允许外检接受空值NULL), 你可以赋给他空值None

  一对一就是OnetoOneField;一对多就是ForeignKey;他俩是一样的,会在那张表中生成一个字段,加一个_id+一个约束关系;多对多就是ManyToManyField,生成一个第三张关系表

2. 多表操作

  一对多增删改查

# book_obj = models.Book.objects.create(title='都是东游记',price=108, publish_id=1 )
    print(book_obj.title)  # 返回值book_obj就是添加记录的对象
   
方式二:
publish_obj= models.Publish.objects.filter(id=2).first() # 查询id为2的出版社对象;不加first就是queryset的那个集合
book_obj = models.Book.objects.create(title='西游记', price=345.45, publish=publish_obj)  # publish就是Book表里面的那个字段 # 让他等于publish的一个实例模型对象,赋值给publish_obj给了他一个对象,相当于把方式一的方法给翻译过来了

  改:

book_obj = models.Book.objects.filter(pk=1).update(publish_id=3)  # 拿出book_id等于1 的书籍把他的出版社id改为其他的出版社
方式二: 传对象
publish_obj = models.Publish.objects.filter(pk=4).first()  # 拿出id等于4 的出版社
models.Book.object.filter(pk=1).update(publish=publish_obj) # 将id等于1 的书籍的出版社改为id等于4 的出版社id

  删除:

models.Book.objects.filter(pk=1).delete()  # 将id为1的书籍进行删除
models.Publish.objects.filter(pk=1).delete()  # 将publish_id为1的书籍进行删除

  多对多的增删改查

# 该主键为1的书籍添加两个作者
    # 思路: 先拿出主键唯一的书籍
    book_obj = models.Book.objects.filter(pk=15).first()
    print(book_obj.authors)   # 对象点击多对多虚拟字段,会直接跨到多对多的第三张表

    book_obj.authors.add(2,3)  # 将id等于1,3 的作者添加至第三张表中

方式二:
author_obj = models.Author.objects.filter(pk=15).first()
book_obj.authors.add(author_obj)

  add给书籍添加作者,括号内既可以传数字也可以传对象,并且可以支持一次性传多个,用逗号隔开就行

  改:

book_obj = models.Book.objects.filter(pk=15).first()   # 拿出id等于15的书籍
  book_obj.authors.set([2,3])       # 将id等于15的书籍作者设置为2, 3

# 将主键为15的书籍改为作者为2, 3
    book_obj = models.Book.objects.filter(pk=15).first()
    # book_obj.authors.set([2,3])
    author_obj = models.Author.objects.filter(pk=3).first()
    book_obj.authors.set([author_obj,])   # 自动将author_obj的id值直接传进来

  set()括号内,需要传一个可迭代对象,可迭代对象中可以是多个数字的组合,也可以是多个对象组合,但是不要混着用!!!

  删除:

book_obj = models.Book.objects.filter(pk=15).first()
book_obj.authors.remove(2)  # 删除作者为2的那条数据

author_obj = models.Author.objects.filter(pk=1).first()
    # author_obj1 = models.Author.objects.filter(pk=2).first()
    # author_obj2 = models.Author.objects.filter(pk=3).first()
    # book_obj.authors.remove(author_obj)   # id等于1的作者删除
    # book_obj.authors.remove(author_obj1,author_obj2)

  remove()括号内既可以传数字,也可以传对象,并且支持传多个,逗号隔开

将某本书跟作者的关系全部清空
book_obj = models.Book.objects.filter(pk=1).first()
book_obj.authors.clear()  # 清空当前书籍与作者的所有关系

3. 跨表查询

  正向与反向的概念 

  正向: 外键字段在当前查询的对象里面,由这个对象查另外一张表,就是正向查询

  反向: 关系字段不在当前对象中,当前字段没有标识

# 一对一
# 正向:author---关联字段在author表里--->authordetail       按字段
# 反向:authordetail---关联字段在author表里--->author       按表名小写

  
# 一对多
# 正向:book---关联字段在book表里--->publish    按字段
# 反向:publish---关联字段在book表里--->book    按表名小写_set.all() 因为一个出版社对应着多个图书

# 多对多
# 正向:book---关联字段在book表里--->author     按字段
# 反向:author---关联字段在book表里--->book     按表名小写_set.all() 因为一个作者对应着多个图书


正向查询按外键字段
反向查询按表名小写
"""
"""基于对象的跨表查询(子查询:将一张表的查询结果当做另外一个查询语句的条件)"
  

  基于对象的跨表查询(子查询:将一张表的查询结果当做另外一张表查询语句的条件)

1. 查询书籍id是15的出版社的名称
book_obj = models.Book.objects.filter(pk=15).first()  # 先拿出id为15的书籍
print(book_obj.publish.name)      # 外键关系在book内,正向查询,直接 . publish就行

2. 查询书籍id为15 的作者的姓名
book_obj = models.Book.objects.filter(pk=15).first()
    print(book_obj.authors.all())
    res = book_obj.authors.all()
    for i in res:
        print(i.name)

# 当我们在遇到app01.Author.None时,不要慌张,有可能是在authors后面缺少一个all()
3.查询作者是panshao的家庭住址
author_obj = models.Author.objects.filter(name='panshao').first()  # 拿出叫panshao的这个人名字
 print(author_obj.authordetail.addr)     # 进行正向查询

4.查询出版社是攀少网络传媒出版社出版的书籍
publish_obj = models.Publish.objects.filter(name='攀少网络传媒出版').first()
print(publish_obj.book_set.all())
5. 查询作者是panshao的写过的所有的书籍
publish_obj = models.Author.objects.filter(name='panshao').first()
print(publish_obj.book_set.all())

6. 查询电话是130的作者名字
author_detail_obj = models.AuthorDetail,obj.filter(phone=130).first()
print(author_detail_obj.author.name)

# 当我们反向查询的结果是多个的时候就需要我们去加_set,,,,,,,,当你反向查询的结果是多个的时候 就需要加_set,,否则直接表明小写即可

  

 

查询书籍id为15的作者的电话号码
book_obj = models.Book.object.filter(pk=15).first()
author_list = book_obj.authors.all()
for author_obj in author_list:
    print(author_obj.author_detail.phone)

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

1. 查询panshao作者的手机号
res = models.Author.objects.filter(name='panhao').values('author_detail__phone')  # values是查询某张表的某个字段
print(res)

2. 查询手机号为130的作者姓名
author_detail_obj = models.AuthorDetail.objects.filter(phone=130).values('author__name')

3. 查询panshao这个作者的年龄和手机号:
 # 正向
    res = models.Author.objects.filter(name='jason').values('age','author_detail__phone')
    print(res)
    反向
    res1 = models.AuthorDetail.objects.filter(author__name='jason').values('phone','author__age')
    print(res1)

4. 查询书籍id是1的作者的电话号码
res = models.Book.objects.filter(id=1).values('authors__authordetail__phone')
res1 = models.Book.objects.filter(pk=1).values('外键字段1__外键字段2__外键字段3__普通字段')


"""只要表里面有外键字段 你就可以无限制跨多张表""

 

四. 聚合查询

1.统计所有书的总价格(aggregate)
    from django.db.models import Max,Min,Count,Avg,Sum
    res = models.Book.objects.aggregate(Sum('price'))
    res1 = models.Book.objects.aggregate(Avg('price'))
    res2 = models.Book.objects.aggregate(Count('price'))
    res3 = models.Book.objects.aggregate(Max('price'))
    res4 = models.Book.objects.aggregate(Min('price'))
    res5 = models.Book.objects.aggregate(Max('price'),Min('price'),Count('pk'),Avg('price'),Sum('price'))
    print(res5)
    print(res)
    print(res1)
    print(res2)


    分组查询(关键字annotate)
    统计每一本书的作者个数
    from django.db.models import Max, Min, Count, Avg, Sum
    res = models.Book.objects.annotate(author_num = Count('authors')).values('author_num','title')    # 按book分组,models后面跟什么就按什么分组
    print(res)

    统计出每个出版社卖的最便宜的书的价格
    res = models.Publish.objects.annotate(mmp = Min('book__price')).values('name','mmp')
    print(res)

    统计不止一个作者的图书
    res = models.Book.objects.annotate(author_num=Count('authors')).filter(author_num__gt=1)
    print(res)


     """
    只要是queryset对象 就可以无限制的调用queryset对象的方法!!!
    最最常用的就是对一个已经filter过滤完的数据 再进行更细化的筛选
    
    """

    # 查询各个作者出的书的总价格
    # res = models.Author.objects.annotate(sp=Sum('book__price')).values('name','sp')
    # print(res)


    

 

五. F查询:

#F查询的本质就是从数据库中获取某个字段的值
    # 查询库存数大于卖出数的书籍
    """之前查询等号后面的条件都是我们认为输入的
        现在变成了需要从数据库中获取数据放在等号后面
    """
    from django.db.models import F
    # res = models.Book.objects.filter(kucun__gt=F('maichu'))
    # print(res)
 # 将书籍库存数全部增加1000
    # models.Book.objects.update(kucun=F('kucun')+1000)

    # 把所有书名后面加上'新款'

    # from django.db.models.functions import Concat
    # from django.db.models import Value
    #
    # ret3 = models.Book.objects.update(title=Concat(F('title'), Value('新款')))
    # models.Book.objects.update(title = F('title')+'新款')  # 不能这么写

  

六. q查询

 # Q查询
    from django.db.models import Q
    # 查询书籍名称是三国演义或者价格是444.44
    # res = models.Book.objects.filter(title='三国演义',price=444.44)  # filter只支持and关系
    # res1 = models.Book.objects.filter(Q(title='三国演义'),Q(price=444))  # 如果用逗号 那么还是and关系
    # res2 = models.Book.objects.filter(Q(title='三国演义')|Q(price=444))   # 或关系
    # res3 = models.Book.objects.filter(~Q(title='三国演义')|Q(price=444))  # 不是三国演义的书籍
    # print(res2)


    # Q高级用法
    q = Q()
    q.connector = 'or'  # 修改查询条件的关系   默认是and
    q.children.append(('title__contains','三国演义'))  # 往列表中添加筛选条件
    q.children.append(('price__gt',444))  # 往列表中添加筛选条件
    res = models.Book.objects.filter(q)  # filter支持你直接传q对象  但是默认还是and关系
    print(res)

  

 

posted @ 2019-09-20 01:13  python黑客编程  阅读(256)  评论(0编辑  收藏  举报