小白学Django---------------多表操作

一.多表操作

1.创建模型

假如有以下几个模型及关系:

作者模型:一个作者有姓名和年龄。

作者详细模型:把作者的详情放到详情表,包含生日,手机号,家庭住址等信息。作者详情模型和作者模型之间是一对一的关系(one-to-one)

出版商模型:出版商有名称,所在城市以及email。

书籍模型: 书籍有书名和出版日期,一本书可能会有多个作者,一个作者也可以写多本书,所以作者和书籍的关系就是多对多的关联关系(many-to-many);一本书只应该由一个出版商出版,所以出版商和书籍是一对多关联关系(one-to-many)。

Django1.0的版本,Django2.0的版本有点会不一样
#出版社
class Publish(models.Model): 
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=64)
    email = models.EmailField()

#作者详细
class AuthorDetail(models.Model):
    phone = models.IntegerField()
    email = models.EmailField()

#作者
class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    authordetail = models.OneToOneField(to=AuthorDetail)
#图书
class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=5,decimal_places=2)
    pub_date = models.DateField()
    publish = models.ForeignKey(to=Publish)
    authors = models.ManyToManyField(to=Author)
#   ManyToManyField==>django会创建第三章表,表里有三个字段id book_id author_id,并且会做外键关联
注意事项:
  • 表的名称myapp_modelName,是根据 模型中的元数据自动生成的,也可以覆写为别的名称
  • id 字段是自动添加的
  • 对于外键字段,Django 会在字段名上添加"_id" 来创建数据库中的列名
  • Django 会根据settings 中指定的数据库类型来使用相应的SQL 语句。
  • 定义好模型之后,你需要告诉Django使用这些模型。你要做的就是修改配置文件中的INSTALL_APPSZ中设置,在其中添加models.py所在应用的名称。
  • 外键字段 ForeignKey 有一个 null=True 的设置(它允许外键接受空值 NULL),你可以赋给它空值 None
  • 如果想创建多个model.py文件,那么在app下建一个包models,然后可以写多个py文件作为models文件,文件名不能重名。并且在__init__.py文件中写上读入模块语句,然后在执行数据库迁移命令

    from . import models1
    from . import models2
    from app01.models import models3
    ......
    

2.添加表记录

一对一
1 先创建没有外键的数据,然后类的字段等于对象
authordetail = AuthorDetail.objects.create(phone=123456789,email=123@qq.com)
author = Author.objects.create(name='tom',age=18,authordetail=authordetail)
2 直接指名道姓的给authordetail_id赋值
author = Author.objects.create(name='tom',age=18,authordetail_id=1)
一对多
方式一
publish = Publish.objects.create(name='北京出版社',addr='北京',email='[email protected]')
Book.objects.create(name='红楼梦',price=10,pub_date='2018-09-18',publish=publish)

方式二
publish = Publish.objects.filter(name='北京出版社').first()
Book.objects.create(name='水浒传',price=10,pub_date='2018-02-13',publish_id=publish.id)
多对多
# 多对多,添加关联关系add,传对应表的(作者表的id)的id
book = Book.objects.create(name='红楼梦',price=10,pub_date='2018-09-18',publish_id=2)
book.add(1,2)
book.add(*(1,2))

add里面可以传对象,也可以传多个,以逗号分隔,也可以传*(对象1,对象2)

# remove  解除绑定关系,参数可以传对象(author)也可以传id(author_id),可以传多个,也可以传*(...)
book =  Book.objects.filter(pk=1).first()
book.authors.remove(2)
author = Author.objects.filer(pk=1).first()
book.authors.remove(author)

# clear 清空,一次性全部解除绑定关系
book =  Book.objects.filter(pk=1).first()
book.authors.clear()

# set 先执行clear,再执行add,参数必须是可迭代对象(里面可以是id,也可以是对象)
book =  Book.objects.filter(pk=1).first()
book.authors.set([6])
book.authors.all()

3.查询

一对一
	# 一对一正向查询
    # tom的手机号
    tom=Author.objects.filter(name='tom').first()
    tel=tom.authordetail.phone
    print(tel)
    # 一对一反向查询
    # 地址在北京的作者姓名
    authordetail=AuthorDatail.objects.filter(addr='北京').first()
    name=authordetail.author.name
    print(name)

一对多
 	# 一对多正向查询
    book=Book.objects.filter(name='红楼梦').first()
    print(book.publish)#与这本书关联的出版社对象
    print(book.publish.name)
    # 一对多反向查询
    # 人民出版社出版过的书籍名称
    pub=Publish.objects.filter(name='人民出版社').first()
    res=pub.book_set.all()
    print(res)
多对多
	# 正向查询----查询红楼梦所有作者名称
    book=Book.objects.filter(name='红楼梦').first()
    res=book.authors.all()
    print(res)
    for author in ret:
        print(author.name)
    # 反向查询 查询tom这个作者写的所有书
    author=Author.objects.filter(name='tom').first()
    res=author.book_set.all()
    print(res)
总结

A表(关联字段) B表

正向查询 A—>B

反向查询 B—>A

关系 正向查询 反向查询
一对一 按字段 按表名小写
一对多 按字段 按表名小写_set
多对多 按字段 按表名小写_set

4.基于双下划线的多表查询(连表查询)

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

一对一
	# 查询tom的手机号
    # 正向查
    ret=Author.objects.filter(name='tom').values('authordetail__phone')
    print(ret)
    # 反向查
    ret= AuthorDatail.objects.filter(author__name='lqz').values('phone')
    print(ret)
一对多
 	# 正向查询按字段,反向查询按表名小写
    # 查询红楼梦这本书出版社的名字
    # select * from app01_book inner join app01_publish
    # on app01_book.publish_id=app01_publish.nid
    ret=Book.objects.filter(name='红楼梦').values('publish__name')
    print(ret)
    ret=Publish.objects.filter(book__name='红楼梦').values('name')
    print(ret)
多对多
	# 正向查询按字段,反向查询按表名小写
    # 查询红楼梦这本书出版社的名字
    # select * from app01_book inner join app01_publish
    # on app01_book.publish_id=app01_publish.nid
    ret=Book.objects.filter(name='红楼梦').values('publish__name')
    print(ret)
    ret=Publish.objects.filter(book__name='红楼梦').values('name')
    print(ret)
    # sql 语句就是from的表不一样
    # -------多对多正向查询
    # 查询红楼梦所有的作者
    ret=Book.objects.filter(name='红楼梦').values('authors__name')
    print(ret)
    # ---多对多反向查询
    ret=Author.objects.filter(book__name='红楼梦').values('name')
    ret=Author.objects.filter(book__name='红楼梦').values('name','authordetail__addr')
    print(ret)
总结
关系 正向查询 反向查询
一对一 按字段 按表名小写
一对多 按字段 按表名小写
多对多 按字段 按表名小写

连续跨表

# ----进阶练习,连续跨表
# 查询手机号以33开头的作者出版过的书籍名称以及书籍出版社名称
# author_datail author book publish
# 基于authorDatail表
    ret=AuthorDatail.objects.filter(phone__startswith='33').values('author__book__name','author__book__publish__name')
    print(ret)
    # 基于Author表
    ret=Author.objects.filter(authordetail__phone__startswith=33).values('book__name','book__publish__name')
    print(ret)
    # 基于Book表
    ret=Book.objects.filter(authors__authordetail__phone__startswith='33').values('name','publish__name')
    print(ret)
    # 基于Publish表
    ret=Publish.objects.filter(book__authors__authordetail__phone__startswith='33').values('book__name','name')
    print(ret)

五,聚合查询与分组查询

聚合

aggregate(*args,**kwargs)

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

常用聚合函数
from django.db.models import Avg,Count,Max,Min,Sum

例如:
Book.objects.aggregate(average_price=Avg('price'))
Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))

分组

annotate()为调用的QuerySet中每一个对象都生成一个独立的统计值(统计方法用聚合函数)。

总结 :跨表分组查询本质就是将关联表join成一张表,再按单表的思路进行分组查询。

# 统计每一本书作者个数
from django.db.models import Avg, Max, Sum, Min, Max, Count
book_list = Book.objects.all().annotate(author_num=Count("authors"))
for book in book_list:
     print(book.name)
     print(book.author_num)
    
book_list=Book.objects.all().annotate(author_num=Count("authors")).values('name','author_num')
print(book_list)

annotate的返回值是querySet,如果不想遍历对象,可以用上valuelist:
queryResult=Publish.objects.annotate(MinPrice=Min("book__price")).values_list("name","MinPrice")

六 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 对象可以使用&(表示and)| (表示or)操作符组合起来。当一个操作符在两个Q 对象上使用时,它产生一个新的Q 对象。同时,Q 对象可以使用~ 操作符取反,这允许组合正常的查询和取反(NOT) 查询:
bookList=Book.objects.filter(Q(authors__name="tom")|Q(authors__name="bob"))
ret=Book.objects.filter(~Q(name='红楼梦'))


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

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

# Q和键值对联合使用,但是键值对必须放在Q的后面(描述的是一个且的关系)
# 查询名字不是红楼梦,并且价格大于100的书
ret=Book.objects.filter(~Q(name='红楼梦'),price__gt=100)

猜你喜欢

转载自blog.csdn.net/qq_42721964/article/details/84024016