目录

一、 基于对象的跨表查询

1.1 一对多查询(Book与Publish)

1.2 一对一查询(Author与AuthorDetail)

1.3 多对多查询(Author与Book)

1.3.1 练习前的准备

1.3.2 开始查询

1.3.3 特别注意

二、基于双下划线的跨表查询

2.1 练习一,一对多

2.2 练习二,多对多

2.3 练习三,三表联查

2.4 练习四

三、聚合与分组查询

3.1 聚合

3.2 分组

对所查询对象的关联对象进行聚合

四、F查询与Q查询

4.1 F查询

4.2 Q查询

五、修改表记录

六、删除表记录

七、定义Django models中的meta选项

models.py文件内容如下

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")

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)

keepNum=models.IntegerField()
commentNum=models.IntegerField()

# 与Publish建立一对多的关系,外键字段建立在多的一方

publish=models.ForeignKey(to="Publish",to_field="nid")

# 与Author表建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表

authors=models.ManyToManyField(to='Author')

以下所有查询及操作均基于此models.py实现

一、 基于对象的跨表查询

1.1 一对多查询(Book与Publish)

正向查询

基于Book表,按Book表字段publish去查

# 查询nid=1的书籍的出版社所在的城市

book_obj=Book.objects.get(nid=1)

city=book_obj.publish.city

#book_obj.publish 是nid=1的书籍对象关联的出版社对象

反向查询

基于Publish表,通过表名加_set实现

# 查询人民出版社出版过的所有书籍

publish = Publish.objects.get(name="人民出版社")

book_list=publish.book_set.all()

# 取到与人民出版社关联的所有书籍对象的集合

for book_obj in bool_list:

print(book_obj.title)

1.2 一对一查询(Author与AuthorDetail)

正向查询

基于Author表,按Author表下的authorDetail字段

# 查询egon作者的手机号

author_egon = Author.objects.get(name='egon')

egon_phone = author_egon.authorDetail.telephone

反向查询

基于AuthorDetail表查询

# 查询所有住址在北京的作者的姓名

authorDetail_list = AuthorDetail.objects.filter(addr='beijing')

for obj in authorDetail_list:

print(obj.author.name)

1.3 多对多查询(Author与Book)

1.3.1 练习前的准备

创建表和批量插入数据

重新编辑models.py,内容如下:

from django.db import models

# Create your models here.

class Publish(models.Model):

name = models.CharField(max_length=32)

address = models.CharField(max_length=64)

email = models.EmailField(default='222@qq.com')

date = models.DateField(default='1970-01-01')

def __str__(self):

return self.name

class Author(models.Model):

name = models.CharField(max_length=32)

phone = models.IntegerField()

qq = models.IntegerField(default=0)

address = models.CharField(max_length=64,default='Beijing')

def __str__(self):

return self.name

class Book(models.Model):

title = models.CharField(max_length=60)

price = models.DecimalField(max_digits=6,decimal_places=2)

authors = models.ManyToManyField(to="Author")

publishDate = models.DateField()

publisher = models.ForeignKey(to="Publish")

def __str__(self):

return self.title

批量插入数据代码如下:

urls.py修改如下:

urlpatterns = [

url(r'^admin/', admin.site.urls),

url(r'^batch/', views.batch),

]

views.py修改如下:

from django.shortcuts import render,HttpResponse

import random,time

from app01.models import *

# Create your views here.

# 生成随机日期:1999-09-12

def randDate(num):

# num 是产生多少个日期字符串

a1 = (1976, 1, 1, 0, 0, 0, 0, 0, 0) # 设置开始日期时间元组(1976-01-01 00:00:00)

a2 = (1990, 12, 31, 23, 59, 59, 0, 0, 0) # 设置结束日期时间元组(1990-12-31 23:59:59)

start = time.mktime(a1) # 生成开始时间戳

end = time.mktime(a2) # 生成结束时间戳

date_list = []

# 随机生成10个日期字符串

for i in range(num):

t = random.randint(start, end) # 在开始和结束时间戳中随机取出一个

date_touple = time.localtime(t) # 将时间戳生成时间元组

date = time.strftime("%Y-%m-%d", date_touple) # 将时间元组转成格式化字符串(1976-05-21)

date_list.append(date)

return date_list

book_list = ["俊友","基督山伯爵","呼啸山庄","傲慢与偏见",

"巴黎圣母院","威尼斯商人","福尔摩斯探案集",

"雾都孤儿","浮士德","蝴蝶梦","包法利夫人",

"红与黑","远大前程","小王子","飘","围城",

"小妇人","驴皮记","莎士比亚全集","悲惨世界",

"欧也妮·葛朗台","复活","简爱","爱玛","唐璜",

"大卫·科波菲尔","唐·吉诃德","安娜·卡列尼娜",

"笑面人","一千零一夜","环游世界八十天","贝姨",

"绿野仙踪","神秘岛","海底两万里","死魂灵",

"贝姨","海上劳工","于连","高老头","双城记",

"黑桃皇后","好兵帅克","罪与罚","斯巴达克思",

"上尉的女儿","名利场","圣女贞德","窈窕淑女",

"苔丝","鲁宾逊漂流记","曼斯菲尔德花园","白鲸",

"百万英镑",]

# 随机生成书箱数据

def batch(req):

for book in book_list:

rand_price = random.randint(20, 300)

rand_pub_obj = random.choice(Publish.objects.all())

rand_date = random.choice(randDate(len(book_list)))

author_obj_list=[]

for i in range(random.randint(1,3)):

rand_author_obj = random.choice(Author.objects.all())

author_obj_list.append(rand_author_obj)

book_obj = Book.objects.create(title=book,price=rand_price,publishDate=rand_date,publisher=rand_pub_obj)

book_obj.authors.add(*author_obj_list)

return HttpResponse("插入数据成功")

1.3.2 开始查询

正向查询

基于Book表,按Book表下的authors字段查询

# 俊友这本书所有作者的名字以及手机号

book_obj = Book.objects.filter(title="俊友").first()

value = book_obj.authors.values('name','phone')

print(value)

结果如下:

反向查询

基于Author表

# 查询Egon出过的所有书籍的名字

author_obj = Author.objects.filter(name='Egon').first()

value = author_obj.book_set.values('title')

print(value)

以上代码结果如下:

注意:book_set 是Book表名的小写加_set,反向查找时用到。这里用的是单下划线

1.3.3 特别注意

你可以通过在ForeignKey()和ManyToManyField()的定义中设置 related_name 的值来覆写 FOO_set 的名称。例如,如果 Article model 中做一下更改: publish = ForeignKey(Blog, related_name='bookList'),那么接下来就会如我们看到这般

# 查询 人民出版社出版过的所有书籍

publish=Publish.objects.get(name="人民出版社")

book_list=publish.bookList.all()

# 与人民出版社关联的所有书籍对象集合

如上,一对多,多对多字段定义时设置了related_name时,查询调用时,就必须用related_name的值。

二、基于双下划线的跨表查询

Django 还提供了一种直观而高效的方式在查询(lookups)中表示关联关系,它能自动确认 SQL JOIN 联系。要做跨关系查询,就使用两个下划线来链接模型(model)间关联字段的名称,直到最终链接到你想要的 model 为止。

关键点:正向查询按字段,反向查询按表名。

以下所有查询均基于<1.3.1练习前的准备>中所创建的表

2.1 练习一,一对多

查询人民出版社出版过的所有书籍的名字与价格(一对多)

正向查询:

基于Book表,按字段publisher查询

value = Book.objects.filter(publisher__name='人民出版社').values('title','price')

print(value)

查询结果:

人'}, {'price': Decimal('98.00'), 'title': '神秘岛'}, {'price': Decimal('172.00'), 'title': '曼斯菲尔德花园'}]>

反向查询

基于Publish表,按表名Book的小写book

value = Publish.objects.filter(name='人民出版社').values_list('book__title','book__price')

print(value)

查询结果如下:

注意: values()方法获取到的是QuerySet列表中包含的字典,而values_list()方法获取到的是QuerySet列表中包含的元组。

特别注意:在filter()中使用双下划线做跨表查询不需要加引号;而在values()和values_list()中使用则需要加引号。

2.2 练习二,多对多

查询Egon写过的所有书籍的名字(多对多)

正向查询

基于Book表的author字段

value=Book.objects.filter(authors__name='Egon').values('title')

print(value)

查询结果:

反向查询

基于Author表,通过Book表的小写book加“__”双下划线加字段名查询,例如:book__title

value=Author.objects.filter(name='Egon').values('book__title')

print(value)

查询结果:

注意:从这个查询结果上看,字典的key是由django自动命令成“表名小写”+“双下划线__”+“字段名”,如果需要通过字典的键去获取值的时候,要特别注意了。

2.3 练习三,三表联查

查询人民出版社出版过的所有书籍的名字以及作者的姓名

正向查询

基于Book表查询

value=Book.objects.filter(publisher__name='人民出版社').values('title','authors__name')

print(value)

查询结果:

反向查询

基于Publish表查询

value = Publish.objects.filter(name='人民出版社').values('book__title','book__authors__name')

print(value)

查询结果:

2.4 练习四

手机号以176开头的作者出版过的所有书籍名称以及出版社名称

value = Book.objects.filter(authors__phone__startswith=176).values_list('title','publisher__name')

print(value)

查询结果:

注意:

反向查询时,如果定义了related_name ,则用related_name替换表名,例如: publish = ForeignKey(Blog, related_name='bookList'):

# 练习1: 查询人民出版社出版过的所有书籍的名字与价格>>(一对多)

# 反向查询 不再按表名:book,而是related_name:bookList

queryResult=Publish.objects.filter(name="人民出版社").values_list("bookList__title","bookList__price")

三、聚合与分组查询

将一张表当作一个大的分组,且不以表中的某个字段来分组时,就可以直接用aggregate(),当需要以表中某个字段进行分组,再对每个分组的进行聚合计算时,必须用annotate()

3.1 聚合

aggregate(*args,*kwargs)*

例如:计算所有图书的平均价格

# 需要先导入Avg(),Max(),Min(),Count(),Sum()等聚合函数

from django.db.models import Avg,Max,Min,Count

value = Book.objects.aggregate(Avg('price'))

print(value)

计算结果:

{'price__avg': 148.85185185185185}

注意:结果是一个字典,字典的key是django自动用“字段名”+"聚合函数名"来命名的,还可以自定义这个字典的键名,只需将上述代码中Avg('price')替换为avgPrice=Avg('price')即可,avgPrice就是你自定义的key。代码如下:

value = Book.objects.aggregate(avgPrice=Avg('price'))

print(value)

结果如下:

{'avgPrice': 148.85185185185185}

aggregate()是QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的标识符,值是计算出来的聚合值。

如果你希望生成不止一个聚合,你可以向aggregate()子句中添加多个参数。所以,如果你也想知道所有图书价格的最大值和最小值,可以这样查询:

value = Book.objects.aggregate(Avg('price'),Max('price'),Min('price'))

print(value)

计算结果:

{'price__avg': 148.85185185185185, 'price__max': Decimal('296.00'), 'price__min': Decimal('23.00')}

3.2 分组

annotate()

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

annotate的返回值是querySet,如果不想遍历对象,可以用上valuelist()或values()显示.

例如:统计每一本书的作者个数

value = Book.objects.annotate(author_count=Count('authors')).values_list('title','author_count')

print(value)

计算结果:

SQL语句解析

SELECT

"app01_book"."title",

COUNT(

"app01_book_authors"."author_id"

) AS "author_count"

FROM

"app01_book"

LEFT OUTER JOIN "app01_book_authors" ON (

"app01_book"."id" = "app01_book_authors"."book_id"

)

GROUP BY

"app01_book"."id",

"app01_book"."title",

"app01_book"."price",

"app01_book"."publishDate",

"app01_book"."publisher_id"

LIMIT 21;

解析:

Book.objects.annotate(author_count=Count('authors'))

拆分解析:

Book.objects等同于Book.objects.all(),翻译成的sql类似于: select id,name,.. from Book

这样得到的对象一定是每一本书对象,有n本书籍记录,就分n个组,不会有重复对象,每一组再由annotate分组统计。'''

对所查询对象的关联对象进行聚合

练习1:统计每一个出版社的最便宜的书

方式一:

value = Publish.objects.annotate(minBook=Min('book__price')).values_list('name','minBook')

print(value)

查询结果:

注意:方式一,如果有一个出版社没有出版过书籍,那么值就是None。

方式二:

value = Book.objects.values('publisher__name').annotate(minBook=Min('price'))

print(value)

查询结果:

解析:方式二中,查看 Book.objects.values("publish__name")的结果和对应的sql语句可以理解为values内的字段即group by的字段

注意:方式二,如果某个出版社没有出版书籍,则不统计。

以下练习不再附查询结果,仅看操作语法。

练习2: 统计每一本以py开头的书籍的作者个数

queryResult=Book.objects.filter(title__startswith="Py").annotate(num_authors=Count('authors'))

练习3:统计不止一个作者的图书

queryResult = Book.objects.annotate(num_authors=Count('authors')).filter(num_authors__gt=1)

练习4:根据每本图书作者数量的多少对查询集 QuerySet进行排序

Book.objects.annotate(num_authors=Count('authors')).order_by('num_authors')

练习5:对每个作者参与写作的书籍数量从大到小排序

author_list=Author.objects.annotate(books_num=Count('book__id')).order_by('-books_num')

练习6:对每个出版社出版的书籍数量从大到小排序

publish_list = Publish.objects.annotate(pubs_num=Count('book__id')).order_by('-pubs_num')

练习7:查询各个作者出的书的总价格

# 按author表的所有字段 "group by"

queryResult=Author.objects.annotate(SumPrice=Sum("book__price")).values_list("name","SumPrice")

print(queryResult)

# 按authors__name "group by"

queryResult2=Book.objects.values("authors__name").annotate(SumPrice=Sum("price")).values_list("authors__name","SumPrice")

print(queryResult2)

四、F查询与Q查询

4.1 F查询

在上面所有的例子中,我们构造的过滤器都只是将字段值与某个常量做比较。如果我们要对两个字段的值做比较,那该怎么做呢?

Django 提供 F() 来做这样的比较。F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。

例如:查询评论数大于收藏数的书籍

# 先导入F

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)

4.2 Q查询

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

Q 对象可以使用操作符:

& 表示逻辑与

| 表示逻辑或

~ 表示逻辑非(波浪号)

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

例如:查询作者是Yuan或Egon的所有书籍

# 需要先导入Q

from django.db.models import 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")

五、修改表记录

# 方式一:

author=Author.objects.get(id=5)

author.name="Tenglan"

author.save()

# 方式二:

Author.objects.filter(id=5).update(name='Tenglan')

# 过滤时不可用get(id=5)

注意:、

方式二修改不能用get()过滤的原因是:update()是QuerySet对象的方法,而get()返回的是一个model对象,它没有update()方法;filter()方法返回的是一个QuerySet对象,因为filter的过滤条件,可能查询出多条数据,如年龄为25岁的员工:filter(age=25),就有多个对象。

方式一中的save()方法,会更新一行数据的所有字段,不管那些字段值是否改变过,都更新一遍。而某些时候,我们只需要更新行里的某几个字段,就需要用update()方法。所以update()的效率要比save()的效率更高。

此外,update()方法对于任何结果集(QuerySet)均有效,这意味着你可以同时更新多条记录,update()方法会返回一个整型数值,表示受影响的记录条数。

注意,这里因为update返回的是一个整形,所以没法用query属性;对于每次创建一个对象,想显示对应的raw sql,需要在settings加上日志记录部分。

六、删除表记录

删除方法就是 delete()。它运行时立即删除对象而不返回任何值。例如:e.delete()

你也可以一次性删除多个对象。每个 QuerySet 都有一个 delete() 方法,它一次性删除 QuerySet 中所有的对象。例如:

# 下面的代码将删除 pub_date 是2005年的 Entry 对象

Entry.objects.filter(pub_date__year=2005).delete()

要牢记这一点:无论在什么情况下,QuerySet 中的 delete() 方法都只使用一条 SQL 语句一次性删除所有对象,而并不是分别删除每个对象。如果你想使用在 model 中自定义的 delete() 方法,就要自行调用每个对象的delete 方法。(例如,遍历 QuerySet,在每个对象上调用 delete()方法),而不是使用 QuerySet 中的 delete()方法。

在 Django 删除对象时,会模仿 SQL 约束 ON DELETE CASCADE 的行为,换句话说,删除一个对象时也会删除与它相关联的外键对象。例如:

b = Blog.objects.get(pk=1)

# This will delete the Blog and all of its Entry objects.

b.delete()

要注意的是: delete() 方法是 QuerySet 上的方法,但并不适用于 Manager 本身。这是一种保护机制,是为了避免意外地调用 Entry.objects.delete() 方法导致 所有的 记录被误删除。如果你确认要删除所有的对象,那么你必须显式地调用:

Entry.objects.all().delete()

如果不想级联删除,可以定义外键字段时设置为:

pubHouse = models.ForeignKey(to='Publisher', on_delete=models.SET_NULL, blank=True, null=True)

通过一个内嵌类 "class Meta" 给你的 model 定义元数据

,具体请点击访问 Django models中的meta选项

django orm级联_第19天,Django之ORM进阶续相关推荐

  1. django orm级联_第 03 篇:创建 Django 博客的数据库模型

    HelloGitHub-Team 成员--追梦人物 设计博客的数据库表结构 博客最主要的功能就是展示我们写的文章,它需要从某个地方获取博客文章数据才能把文章展示出来,通常来说这个地方就是数据库.我们把 ...

  2. django mysql orm教程_带你了解Django ORM操作(基础篇)

    前言 在日常开发中,需要大量对数据库进行增删改查操作. 如果头铁的话,使用原生SQL是最好的,毕竟性能又高,又灵活. 但是通常情况下,我们不是太需要那么苛刻的性能,也没有那么多刁钻的需求用原生SQL ...

  3. django python3 异步_详解配置Django的Celery异步之路踩坑

    人生苦短,我用python. 看到这句话的时候,感觉可能确实是很深得人心,不过每每想学学,就又止步,年纪大了,感觉学什么东西都很慢,很难,精神啊注意力啊思维啊都跟不上.今天奶牛来分享自己今天踩的一个坑 ...

  4. django http 收发_如何从一个django服务器发送请求到另一个服务器

    我有一个 django问题.我想发送来自浏览器或业务逻辑的数据 在我的django服务器上到另一个django服务器或只是相同的服务器但不同的端口,来处理请求.我能怎么做?我试图使用socket实现, ...

  5. python从入门到实践18章django存在问题_第18章 Django入门 P371

    声音简介-感谢粉丝NN娜娜小天使的打赏,此音频即日起以24小时/天,1次/小时的速度更新,直至完结~ 特别说明: 此音频仅作为更好地阅读此书的辅助工具,请大家购买正版书籍. 所有音频全部免费供大家试听 ...

  6. python diango 并发_利用gunicorn提高django的并发能力

    引言 手头上的项目有一些采用django框架编写, 如果说并发量比较小的时候简单的runserver是可以应对的. 那么当并发达到一两千的时候,该怎么提高django的并发能力呢? Overview ...

  7. Django之初步实现登录功能,APP及ORM

    form表单提交数据注意事项 1.注意form标签的书写,form标签必须有method属性和action属性 2.所有获取用户输入的表单标签(如input等)需放在<form></ ...

  8. python数据模型搭建_python之路(19)django数据库模型(model)

    前言 object relation mapping(ORM)关系对象映射表,一个类实例映射为一条表记录 目录 数据库配置 1.django 默认使用sqlite的数据库,如果需要使用mysql数据库 ...

  9. django文档_如何在django官方文档中快速找到需要的内容

    许多新手程序员发现Django文档内容非常庞大. 假设想学习如何为用户执行登录.看着很简单:登录是Django的核心功能.如果搜索" django登录"或搜索文档,则会看到一些选项 ...

最新文章

  1. CA验证数字证书的有效性
  2. git 撤销文件的修改(checkout/reset )
  3. C++:String的写时拷贝
  4. word vba 读出光标所在的列数_word高效技巧:这几个表格操作让工作更快速
  5. mysql运维技巧_​mysql初级运维使用技巧
  6. [Python] 关键字 assert
  7. FILEUTILS 介绍
  8. 浅入浅出 Java 排序算法
  9. Leetcode之删除链表的倒数第N个节点
  10. vmware tools的下载
  11. java本学期期末学员,本学期期末学员共参加了3门课的考试,即Java,C,SQL,编写方法计算每位学员3门课的平均分,...
  12. Windows下批处理一键修改系统时间并运行程序
  13. 一般试卷的纸张大小是多少_试卷标准字体大小是多少 考试试卷标准字体格式...
  14. 66个求职应聘技巧性问答(三)
  15. pacman 查询_pacman命令
  16. 云计算 三种模式 各种云的区别
  17. 进化优化算法--第二章:爬山法
  18. Qt 开发使用VSCode
  19. 编程一年,我学会了什么?
  20. 区块链---双花问题

热门文章

  1. Java线程到底有几种状态
  2. 快速填充excel表格一列
  3. (1)1994-2019年俄罗斯家庭调查数据(调查表+数据集)(2)各国地理距离、文化距离、制度距离、人均gdp(至2020年)(3)IFR各国分行业-工业机器人数据(1993-2019年)(4
  4. 论企业网站建设市场现状
  5. 南阳计算机软件培训,南阳加工中心数控编程培训UG软件电脑编程培训图层管理...
  6. Java生产Pdf文件,注意事项(坑)
  7. JAXB Simple Example
  8. js_判断当前页面是否有网络和网络连接超时
  9. 侄儿子非要抢我手机玩吃鸡!但他才六岁菜的很。我花五分钟写了个游戏,他玩了一天。
  10. 合工大JAVA实验四web_合工大JAVA实验报告.doc