Django模型层(多表操作,一对一,一对多,多对多,以及 聚合/分组/F/Q查询)

创建表模型

(一)多表查询
    一对一:models.OneToOneField(to_field='id',to='Authordatil')
    一对多:(外键设置唯一性)
        models.ForeignKey(to='Publish',to_field='id')

    多对多:自动生成第三张表
        models.ManyToManyField(to='Author')


(二)创建表模型
     作者与详情  ( 一对一 )
     书与作者( 多对多 )
     出版社与书(一对多 )

     出版社:id name  add  email
     书: id  name  price  publish

     作者: id  name  sex  authordatil
     详情: id  photo  address
分析思路
from django.db import models

# Create your models here.

class Publish(models.Model):
   # 主键 id
= models.AutoField(primary_key=True) name = models.CharField(max_length=32) address = models.CharField(max_length=64) email = models.EmailField() def __str__(self): return '%s,%s,%s'%(self.name,self.address,self.email) class Book(models.Model): id = models.AutoField(primary_key=True) name = models.CharField(max_length=32) price = models.DecimalField(max_digits=5,decimal_places=2) # 出版社与书( 一对多 )外键要设置唯一性,关联的表,关联的字段 publish = models.ForeignKey(to='Publish',to_field='id') # 书与作者( 多对多 ) authors = models.ManyToManyField(to='Author') def __str__(self): return self.name class Author(models.Model): id = models.AutoField(primary_key=True) name = models.CharField(max_length=32)

choices = ( (0,"女"),(1,"男") )
sex = SmallIntegerField( choices=choices, default=0 )
# 作者与详情 ( 一对一 ) 
authordatil = models.OneToOneField(to_field='id',to='Authordatil')
def __str__(self):
return self.name

class Authordatil(models.Model):
id
= models.AutoField(primary_key=True)
photo
= models.CharField(max_length=32)
address
= models.CharField(max_length=32)
  •  id 字段是自动添加的
  •   对于外键字段,Django 会在字段名上添加"_id" 来创建数据库中的列名
  •     外键字段 ForeignKey 有一个 null=True 的设置(它允许外键接受空值 NULL),你可以赋给它空值 None 。

添加/删除/修改表记录

批量插入

# 批量插入 bulk_create
li = []
for i in range(100):
    li.append(models.Book(name='图书%s'%i,price=10+i))  # 生成100个对象
models.Book.objects.bulk_create(li)  # 参数为列表,列表里为100个对象,一次插入100条

一对一 增/删/改 数据

Authordatil = Authordatil.objects.create(photo=121212121,address='东莞')   # 增

auther = Auther.objects.filter(name = '西游记').update( publish_id=1 ) # 改

auther = Auther.objects.filter("name" = "西游记").first() # 先查
auther.name = "东游记" # 再改
auther.save() # 最后保存

auther =Auther.objects.filter(name = '西游记').delete() # 删除

一对多 增/删/改 数据

增:
# publish_id: 数字
book = Book.objects.create(name='三国',price=56,publish_id=1)
# publish: 对象
publish = Publish.objects.get(id=1)
book = Book.objects.create(name='西游记',price=26,publish=publish)

# publish_id: 数字
book = Book.objects.filter(name = '西游记').update( publish_id=1 )
# publish: 对象
publish = Publish.objects.get(id=3)
book = Book.objects.filter(name = '西游记').update(publish=publish)

删:同单表查询一样
book = Book.objects.filter(name='西游记',price=22).delete()

多对多 增/删/改 数据

增:add ( obj1,obj2... )   可以传对象,可以传id,可以传多个
删:remove( obj1,obj2... )  可以传对象,可以传id,可以传多个

改:先清空,配置 清空: clear() 清空所有 配置:set() 里面传列表,里面可以是对象或者是id
*注意先清空,在配置
(五)多对多新增 (书 与 作者)
    增:add ( obj1,obj2... )   可以传对象,可以传id,可以传多个
        zxx = Author.objects.filter(name='zxx').first()
        yxx = Author.objects.filter(name='yxx').first()
        book = Book.objects.filter(name='西游记').first()
        book.authors.add(yxx,zxx)

    删:remove( obj1,obj2... )  可以传对象,可以传id,可以传多个
        lxx = Author.objects.filter(name='lxx').first()
        zxx = Author.objects.filter(name='zxx').first()
        book = Book.objects.filter(name='红楼梦').first()
        book.authors.remove(lxx,zxx)

    清空: clear()  清空所有
        book.authors.clear()


    配置:set()  里面传列表,里面可以是对象或者是id  *注意先清空,在配置
        lxx = Author.objects.filter(name='lxx').first()
        zxx = Author.objects.filter(name='zxx').first()
        book.authors.set([1,2])
        book.authors.set([lxx,zxx])
View Code

基于对象的查询表记录(子查询)

查询分析:正反向查询

从关联字段的表出发,查向另外一张表  为正向,反之为反向
eg:   正向: author
---关联字段在author--->authordetail ------> 按字段   反向: authordetail------关联字段在author--->author -----> 按表名小写

一对一 查询

正向(按字段名)
      author = Author.objects.filter(name='lxx').first()
      photo = author.authordatil.photo
反向(按表名小写)
      authordatil = Authordatil.objects.filter(photo=166667878).first()
      author_name = authordatil.author.name

一对多 查询

正向(按字段名)
      book = Book.objects.filter(name='茶社').first()
        publish = book.publish
        print(publish.name)
反向 ( 按 表名_set.all() )
          publish = Publish.objects.filter(name='武汉出版社').first()
          author = publish.book_set.all()

多对多 查询

多对多
    正向(按字段名)
        book = Book.objects.filter(name='红楼梦').first()
        author = book.authors.all()
    反向( 按 表名_set.all() )
        author = Author.objects.filter( name='lxx').first()
        book = author.book_set.all()

基于双下划线查询表记录(连表查询)

*注意多用与查询某张表的字段名

正向查询  ( 按字段,跨表可以在filter,也可以在values中 )
        photo = Author.objects.filter(name='lxx').values('authordatil__photo')
        print(photo)
反向查询( 按表名小写,跨表可以在filter,也可以在values中 )
        photo = Authordatil.objects.filter(author__name='lxx').values('photo')
        print(photo)

正向连续跨表查询( 按字段,跨表可以在filter,也可以在values中 )
    name = Publish.objects.filter(name='武汉出版社').values('book__authors__authordatil__photo')
反向连续跨表查询( 按表名小写,跨表可以在filter,也可以在values中 )
    photo = Author.objects.filter(book__publish__name='武汉出版社').values('book__authors__authordatil__photo')


练习:
# 练习:  查询苹果出版社出版过的所有书籍的名字与价格(一对多)
# 练习: 查询alex出过的所有书籍的名字(多对多)
# 练习: 查询人民出版社出版过的所有书籍的名字以及作者的姓名
# 练习: 手机号以151开头的作者出版过的所有书籍名称以及出版社名称
# 练习: 查询人民出版社出版过的所有书籍的名字与价格(一对多)

练习一:

1 用orm插入6个作者详情,插入6个作者
2 用orm插入6个出版社
3 用orm插入8本书,4种用publish=对象的形式,4种用publish_id=id的形式
4 两种形式修改书的出版社
5 基于对象查询,地址为山东的作者的姓名 ( 一对一 )
6 基于对象查询,电话为13888888作者的性别 ( 一对一 )
7 基于双下划线查询,地址为山东的作者的姓名
8 基于双下划线查询,电话为13888888作者的性别
9 基于对象查询北京出版社出版的所有图书
9 基于对象查询南京出版社出版的价格大于20的图书(一对多)
10 基于对象查询图书名字叫红楼梦这本书的作者(多对多)
11基于对象查询作者名为lqz,出版的所有图书
12 基于对象查询作者名为lqz,出版的价格大于20的所有图书
13 基于对象查询图书名以红开头,所有的出版社信息
14基于对象查询图书名以红开头,出版社以社结尾的所有出版社地址
15 查询所有价格大于20的图书
16 查询所有价格大于20的图书所有的出版社
17 查询手机号以13开头,所有的作者
18查询手机号以13开头,所有作者出版的所有图书
19 查询图书价格在[19.9,56,77.9]内的所有图书
20 查询图书价格在[19.9,56,77.9]内的所有图书,对应的所有作者
21 查询图书价格在[19.9,56,77.9]内的所有图书,对应的所有作者的手机号
    4 两种形式修改书的出版社
        方法(1)   book = Book.objects.filter(name='红楼梦').first()
                    book.publish_id = 2
                    book.publish = publish
                    book.save()

        方法(2)    publish = Publish.objects.filter(name='深圳出版社').first()
                    book = Book.objects.filter(name='红楼梦').first()
                    book.publish = publish
                    book.save()

    5 基于对象查询,地址为山东的作者的姓名 ( 一对一 )
        authordatil = Authordatil.objects.filter(address='山东').first()
        name = authordatil.author.name

    6 基于对象查询,电话为13888888作者的性别 ( 一对一 )
        authordatil = Authordatil.objects.filter(photo =  133323223).first()
        sex = authordatil.author.sex

    7 基于双下划线查询,地址为山东的作者的姓名
         authordatil = Authordatil.objects.filter(address='东京').values('author__name')

    8 基于双下划线查询,电话为13888888作者的性别
        sex = Authordatil.objects.first(photo=133323223).values('author__sex')

    9 基于对象查询北京出版社出版的所有图书
        publish = Publish.objects.filter(name='杭州出版社').first()
        books = publish.book_set.all()

    9 基于对象查询南京出版社出版的价格大于20的图书(一对多)
        publish = Publish.objects.filter(name='杭州出版社').first()
        book = publish.book_set.all().filter(price__gt=20)

    10 基于对象查询图书名字叫红楼梦这本书的作者(多对多)
        book = Book.objects.filter(name='红楼梦').first()
        authors = book.authors.all().values('name')

    11基于对象查询作者名为lqz,出版的所有图书
        author = Author.objects.filter(name='lxx').first()
        book = author.book_set.all()

    12 基于对象查询作者名为lqz,出版的价格大于20的所有图书
        author = Author.objects.filter(name='lxx').first()
        book = author.book_set.all().filter(price__gt=31)

    13 基于对象查询图书名以红开头,所有的出版社信息
        book = Book.objects.filter(name__startswith='').first()
        publish = book.publish

    14基于对象查询图书名以红开头,出版社以社结尾的所有出版社地址
        book = Book.objects.filter(name__startswith='').values_list('publish__address')
        print(book)
        publish = Publish.objects.filter(address__endswith='').values_list('address')
        for i in publish:
            if i in book:
                print(i)

    15 查询所有价格大于20的图书
        book = Book.objects.filter(price__gt=20).all()

    16 查询所有价格大于20的图书所有的出版社
        books = Book.objects.filter(price__gt=20)
        for book in books:
            publish = book.publish
            print(publish)

    17 查询手机号以13开头,所有的作者
        方法一: authordatils = Authordatil.objects.filter(photo__startswith=13).values('author__name')
        方法二: authordatils = Authordatil.objects.filter(photo__startswith=13)
                for authordatil in authordatils:
                    author = authordatil.author
                    print(author)


    18查询手机号以13开头,所有作者出版的所有图书
        方法一:  books = Authordatil.objects.filter(photo__startswith=13).values('author__book__name')
        方法二:  authordatils = Authordatil.objects.filter(photo__startswith=13).first()
                  # 拿到信息表 再到作者 再到书
                  book = authordatils.author.book_set.all()

    19 查询图书价格在[19.9,56,77.9]内的所有图书
        book = Book.objects.filter(price__in=[19.9, 56, 77.9]).all()

    20 查询图书价格在[19.9,56,77.9]内的所有图书,对应的所有作者
        方法一: author = Book.objects.filter(price__in=[19.9, 56, 77.9]).values('authors')
        方法二: books = Book.objects.filter(price__in=[19.9, 56, 77.9])
                for book in books:
                    author = book.authors.all()


    21 查询图书价格在[19.9,56,77.9]内的所有图书,对应的所有作者的手机号
        方法一:     books = Book.objects.filter(price__in=[19.9, 56, 77.9]).values('authors__authordatil__photo')
        方法二:     books = Book.objects.filter(price__in=[19.9, 56, 77.9]).all()
                    for book in books:
                        authors = book.authors.all()
                        for author in authors:
                            photo = author.authordatil.photo
                            print(photo)
答案
作业二:
1 修改名字为lqz作者的手机号为12345678901
2 修改以红开头的图书的所有作者手机号为123321321
3 修改出版社是北京出版社出版的所有图书的价格为99.99
4 为红楼梦这本书,新增lqz,egon两个作者(两种方式)
5 更改红楼梦这本书的作者为lqz,xiaohou(两种方式) ??????????
6 把上题lxx 换成 egon
6 删除红楼梦这本书xiaohou这个作者
7 清空红楼梦这本书所有作者
8 查询图书价格大于30的所有作者的名字
9 查询北京出版社出版的所有图书的作者名字
10 查询北京出版社出版的所有图书的作者手机号
11 查询西游记这本书所有作者的个数
12 查询北京出版社出版的图书个数
 1 修改名字为lqz作者的手机号为12345678901
        author = Author.objects.filter(name='lxx').first()
        authordatil = author.authordatil
        authordatil.photo = 12345678901
        authordatil.save()

   2 修改以红开头的图书的所有作者手机号为123321321
        books = Book.objects.filter(name__startswith='')
        for book in books:
            authors = book.authors.all()
            for author in authors:
                authordatil = author.authordatil
                authordatil.photo = 123321321
                authordatil.save()

   3 修改出版社是北京出版社出版的所有图书的价格为99.99
        publish = Publish.objects.filter(name='长沙出版社').first()
        books = publish.book_set.all()
        for book in books:
            book.price = 99.99
            book.save()
            print(book.price)

   4 为红楼梦这本书,新增lqz,egon两个作者(两种方式)
        方法一:
        book = Book.objects.filter(name='红楼梦').first()
        book.authors.add(4,5)
        方法二:
        egon = Author.objects.filter(name= 'egon').first()
        wxx = Author.objects.filter(name='wxx').first()
        book = Book.objects.filter(name='红楼梦').first()
        book.authors.add(egon,wxx)

   5 更改红楼梦这本书的作者为lqz,xiaohou(两种方式)  ??????????
        book = Book.objects.filter(name='红楼梦').first()
        authors = book.authors.clear()
        print(authors)
        lxx = Author.objects.filter(name='lxx').first()
        zxx = Author.objects.filter(name='zxx').first()
        book.authors.set([lxx,zxx])

        把lxx 换成 egon
        lxx =Author.objects.filter(name='lxx').first()
        egon = Author.objects.filter(name='egon').first()

        book = Book.objects.filter(name='边城').first()
        book.authors.remove(lxx)
        book.authors.set([egon,])

   6 删除红楼梦这本书xiaohou这个作者
        lxx = Author.objects.filter(name='lxx').first()
        book = Book.objects.filter(name='红楼梦').first()
        book.authors.remove(lxx)

   7 清空红楼梦这本书所有作者
        book = Book.objects.filter(name='红楼梦').first()
        book.authors.clear()

   8 查询图书价格大于30的所有作者的名字
         book = Book.objects.filter(price__gt=30).values('authors__name','price')

   9 查询北京出版社出版的所有图书的作者名字
        publish = Publish.objects.filter(name='武汉出版社').values('book__authors__name')

   10 查询北京出版社出版的所有图书的作者手机号
         name = Publish.objects.filter(name='武汉出版社').values('book__authors__authordatil__photo')

   11 查询西游记这本书所有作者的个数
        book = Book.objects.filter(name='西游记').first()
        con = book.authors.all().count()

   12 查询北京出版社出版的图书个数
        publish = Publish.objects.filter(name='武汉出版社').first()
        con = publish.book_set.all().count()
答案

聚合查询(aggregate)

from  django.db.models import Avg,Count,Min,Max,Sum
con = Book.objects.all().aggregate(Max('price'),Min('price'),Avg('price'),Sum('price'))
# 计算所有图书的平均价格
# 计算图书的最高价格
# 计算图书的最高价格,最低价格,平均价格,总价
    计算所有图书的平均价格
    con = Book.objects.all().aggregate(Avg('price'))
    print(con)
    计算图书的最高价格
    con = Book.objects.all().aggregate(Max('price'))
    print(con)

    计算图书的最高价格,最低价格,平均价格,总价
    con = Book.objects.all().aggregate(Max('price'),Min('price'),Avg('price'),Sum('price'))
    print(con)
答案

分组查询(annotate)

 group by 谁,就以谁做基表,filter过滤,annotate取分组,values取值
 values在前 以谁分组   values在后 表示 取值
 filter在前 where条件   filter在后 表示 having
# 统计每一本书作者个数
# 统计每一个出版社的最便宜的书(以谁group by 就以谁为基表)
# 统计每一本以py开头的书籍的作者个数
# 统计每一本以py开头的书籍的作者个数--套用模板
# 查询各个作者出的书的总价格
# 查询名字叫lqz作者书的总价格
# 查询所有作者写的书的总价格大于30
# 统计不止一个作者的图书
 # 统计每一本书作者个数
    con = Book.objects.values('pk').all().annotate(c= Count('authors')).values('name','c')
    print(con)
    # 统计每一个出版社的最便宜的书
    con = Publish.objects.values('pk').all().annotate(c= Min('book__price')).values('name','c')
    print(con)

    # 统计每一本以py开头的书籍的作者个数
    con = Book.objects.filter(name__startswith='').annotate(c=Count('authors')).values('name','c')
    print(con)
    # 统计每一本以py开头的书籍的作者个数--套用模板
    con = Book.objects.filter(name__startswith='').all().annotate(c=Count('authors')).values('name', 'c')
    print(con)
    # 查询各个作者出的书的总价格
    con = Author.objects.annotate(c= Sum('book__price')).values('name','c')
    print(con)

    # 查询名字叫lqz作者书的总价格
    con = Author.objects.filter(name='egon').annotate(c= Sum('book__price')).values('name','c')
    print(con)

    # 查询所有作者写的书的总价格大于30
    con = Author.objects.annotate(c=Sum('book__price')).filter(c__gt=70).values('name', 'c')
    print(con)

    # 统计不止一个作者的图书
    con =  Book.objects.annotate(c = Count('authors')).filter(c__gt=1).values('name','c')
    print(con)

    # F查询
    # 查询评论数大于阅读数的书
    from django.db.models import F
    con = Book.objects.filter(commit_num__gt=F('read_num')).values('name')
    print(con)
    # 把所有书的评论数加1
    con = Book.objects.all().update(commit_num=F('read_num')+1)
    print(con)
    # 把python这本书的阅读数减5
    con = Book.objects.filter(name='红楼梦').all().update(read_num=F('read_num') -5 )
    print(con)
答案

F查询

无法引用字段上的值,把字段名的值包裹起来,就能用了
from django.db.models import F

eg: Book.objects.filter(id=1).update(F("price")+1) # 将第一本书的价格加一
# 查询评论数大于阅读数的书
# 把所有书的评论数加1
# 把python这本书的阅读数减5
# 查询评论数大于阅读数的书
    from django.db.models import F
    con = Book.objects.filter(commit_num__gt=F('read_num')).values('name')
    print(con)
    # 把所有书的评论数加1
    con = Book.objects.all().update(commit_num=F('read_num')+1)
    print(con)
    # 把python这本书的阅读数减5
    con = Book.objects.filter(name='红楼梦').all().update(read_num=F('read_num') -5 )
    print(con)
答案

Q查询

from django.db.models import Q
与 &     或 |      非 ~

Q查询: 基于关系运算     &与  |或  ~非
&与 (默认为 与)
Book.objects.filter(Q(price=15),Q(name="金瓶眉”)) # 查看价格为15 与(且) 名字为金瓶眉的书
|或
Book.objects.filter(Q(price=15) | Q(name="金瓶眉”)) # 查看价格为15 或 名字为金瓶眉的书
~非
Book.objects.filter(~Q(price=15) & ~Q(name="金瓶眉”)) # 查看价格不为15 且 名不为金瓶眉的书
# 查询作者名字是lqz或者名字是egon的书
# 查询作者不是lqz的书
构建很复杂的逻辑,需要用括号来区分
 from django.db.models import Q
    # 查询作者名字是lqz或者名字是egon的书
    con = Author.objects.filter(Q(name='lxx') | Q(name='wxx')).values('book__name')
    print(con)
    # 查询作者不是lqz的书
    # con = Author.objects.filter(~Q(name='lxx')).values('book__name')
    con = Author.objects.filter(~Q(name='lxx')).values('book__name')
    print(con)

    con = Book.objects.filter(~Q(authors__name='lxx')).values('name')
    print(con)
    # 构建很复杂的逻辑, 需要用括号来区分
    ret = Book.objects.filter((Q(name='红楼梦') & Q(price__gt=100)) | Q(pk__gt=2))
    print(ret)
答案

 

posted @ 2018-11-14 10:33  星牧  阅读(848)  评论(0编辑  收藏  举报