Django之QuerySet 查询

mac2022-06-30  69

首先来看下如何查询。我们在网页中增加书名的查询链接

后端的查询处理代码:这里由于authors是manytomanyfiled,因此我们这里用r.authors.all().first()来查询符合条件的第一个

def books_inquery_result(request): ret=[] if request.method == 'POST': bookname=request.POST['bookname'] result=Book.objects.filter(title=bookname) for r in result: ret.append(r.authors.all().first()) return HttpResponse(ret)

但是当输入书名flask1的时候,我们查出来的确实下面这些代码。这是为什么呢。

我们回头看下之前添加books的操作:

ret3.publisher=Publisher.objects.all()[1] ret3.publish_date=publish_date ret3.save() ret3.authors.add(Author.objects.all()[1])

我们查询对应的Author.objects.all()[1]发现也是同样的结果。

def result2(request): result=Author.objects.all()[1] return HttpResponse(result)

但是我们想要到的是书的作者和在网页上输入的对象关联起来。那么我们添加的代码就需要更新如下:这样就把之前的输入给加了进来

ret3=Book() ret3.title=title ret3.publisher=Publisher.objects.get(name=publish) ret3.publish_date=publish_date ret3.save() ret3.authors.add(Author.objects.get(email=email))

此时再进行查询,就可以得到正确的结果了。而且我们还可以通过

r.authors.all().first().email r.authors.all().first().first_name r.authors.all().first().last_name 接下来看下如何查询出版商的信息,出版商是ForeignKey的关系。因此查询也很简单,通过下面的方式就可以查出出版商的信息。 r.publisher.address r.publisher. name r.publisher. city r.publisher. state_province r.publisher. country r.publisher. website select_related查询优化,首先我们在setting.py的末尾加上下面的配置: LOGGING = {     'version': 1,     'disable_existing_loggers': False,     'handlers': {         'console':{             'level':'DEBUG',             'class':'logging.StreamHandler',         },     },     'loggers': {         'django.db.backends': {             'handlers': ['console'],             'propagate': True,             'level':'DEBUG',         },     } } 这个logging的配置作用是在终端查询的时候自动打印出sql语句,这样我们就能很直观的看到查询的方式。比如前面的Book查询。我们来看下 Python manage.py shell In [1]: from site_prj.models import Publisher,Book,Author   In [2]: books=Book.objects().all In [4]: b1=books[0] (0.002) SELECT "site_prj_book"."id", "site_prj_book"."title", "site_prj_book"."publisher_id", "site_prj_book"."publish_date" FROM "site_prj_book" LIMIT 1; args=() In [6]: b1.title Out[6]: u'flask'   In [7]: b1.publisher (0.000) SELECT "site_prj_publisher"."id", "site_prj_publisher"."name", "site_prj_publisher"."address", "site_prj_publisher"."city", "site_prj_publisher"."state_province", "site_prj_publisher"."country", "site_prj_ publisher"."website" FROM "site_prj_publisher" WHERE "site_prj_publisher"."id" = 2; args=(2,) Out[7]: <Publisher: æ 我们看到在查询publisher的时候又调用了sql语句,原因在于Book中publisher 和Book的关系是ForeignKey的关系,也就是多对一的关系。可能一本书有多个出版商都在出版,那么我们能不能在查询的时候将这些出版商全都查出来呢。这就需要用到select_related的方法了。 ret2=Book.objects.all().select_related('publisher')  #得到所有的文章 r2=ret2[10]  #选择第10篇return HttpResponse(r2.publisher.state_province)  #得到出版次文章的所有出版商的省份   In [9]: book2=Book.objects.all().select_related('publisher')   In [10]: b2=book2[2] (0.000) SELECT "site_prj_book"."id", "site_prj_book"."title", "site_prj_book"."publisher_id", "site_prj_book"."publish_date", "site_prj_publisher"."id", "site_prj_publisher"."name", "site_prj_publisher"."address", "site_prj_publisher"."city", "site_prj_publisher"."state_province", "site_prj_publisher"."country", "site_prj_publisher"."website" FROM "site_prj_book" INNER JOIN "site_prj_publisher" ON ("site_prj_book"."publish er_id" = "site_prj_publisher"."id") LIMIT 1 OFFSET 2; args=() In [11]: b2.publisher.name Out[11]: u'\xe6\x88\x90\xe9\x83\xbd\xe6\x97\xa5\xe6\x8a\xa5'   从下面的调试可以看到,没有再进行SQL查询。

那么我们现在来看下发现查询。什么是反向查询呢,之前的查询是引用方查询被引用方

比如在Book中publisher 和Book的关系是ForeignKey的关系,也就是多对一的关系.如果我想查出这本书的作者还写了哪些书。就需要用到反向查询。 代码如下: author=Author.objects.filter(first_name=first,last_name=second)for a in author: ret.append(a.book_set.all().first()) 得到每个作者的对象后用author.book_set.all().first()得到该作者写过的所有书。 同样的通过输入出版商也可以查出该出版商的所有书籍。 publish=Publisher.objects.filter(name=publishname)for p in publish: ret_publish.append(a.book_set.all().first())下面介绍下Values获取字典形式的结果。def result3(request): ret=[]result=Book.objects.values('title','authors','publisher','publish_date') for r in result: ret.append(r) return HttpResponse(ret) 这样能将所有的查询结果以字典的形式呈现出来。我们看到其中publisher和authro都是数字。而不是具体的名称。

我们来看下对应的sql语句:可以看到在sql语句中就是选用的是Publisher和Author的id作为查询条件且使用的是左链接的方式。 SELECT "site_prj_book"."title", "site_prj_book_authors"."author_id", "site_prj_book"."publisher_id", "site_prj_book"."publish_date" FROM "site_prj_book" LEFT OUTER JOIN "site_prj_book_authors" ON ("site_prj_book"."id" = "site_prj_book_authors"."book_id"); args=() 如果不想看字典的形式,而只是想看列表的方式,可以用values_list的方式 result=Book.objects.values_list('title','authors','publisher','publish_date')

Annotate进行聚合,计数,平均数,求和等 下面首先来计算作者的个数: 首先引入模块:django.db.models里面包含所有的计算函数 from django.db.models import *def result2(request): result=Author.objects.all().values('email').annotate(count=Count('email')).values('email','count') return HttpResponse(result) 这个函数的作用是统计作者中的email使用总次数。计每个作者的email出现的次数。Annotate代表以某个元素进行聚类的意思。这里是以每个名字的次数进行聚类。返回结果如下。 我们还可以通过聚类求平均,但是目前的表中没有整数字段。我们在Author中添加一个age的字段。添加方法如下,进入python manage.py shell执行如下的语句。 In [1]: from django.db import connection   In [2]: cursor=connection.cursor()   In [3]: cursor.execute('Alter TABLE Authro add age Integer') In [8]: cursor.execute('Alter TABLE site_prj_Author add age Integer') (0.014) Alter TABLE site_prj_Author add age Integer; args=None Out[8]: <django.db.backends.sqlite3.base.SQLiteCursorWrapper at 0x3050a80> In [3]: print Author.objects.all().query SELECT "site_prj_author"."id", "site_prj_author"."first_name", "site_prj_author"."last_name", "site_prj_author"."email", "site_prj_author"."age" FROM "site_prj_author"   添加后我们就可以通过下面的方式进行作者平均年龄的计算了                                                                                                                          result=Author.objects.all().values('age').annotate(average=Avg('age')).values('age','average') 对应的SQL语句: SELECT "site_prj_author"."age", AVG("site_prj_author"."age") AS "average" FROM "site_prj_author" GROUP BY "site_prj_author"."age"; args=()   链式查询: 过滤成都日报出版且城市是成都的出版商 result=Publisher.objects.filter(name='成都日报').filter(city='成都') 过滤成都出版社且城市不是成都出版商 result=Publisher.objects.filter(name='成都日报').exclude(city='成都') 对应的SQL语句: SELECT "site_prj_publisher"."id", "site_prj_publisher"."name", "site_prj_publisher"."address", "site_prj_publisher"."city", "site_prj_publisher"."state_province", "site_prj_publisher"."country", "site_prj_publisher"."website" FROM "site_prj_publisher" WHERE (NOT ("site_prj_publisher"."name" = '成都日报') AND "site_prj_publisher"."city" = '成都');   Defer排除不需要的字段:有些模型的字段太多,我们只想看其中几种可以用defer来排查某些字段 result=Author.objects.all().defer('age').query 对应的SQL:可以看到没有查询age字段 SELECT "site_prj_author"."id", "site_prj_author"."first_name", "site_prj_author"."last_name", "site_prj_author"."email" FROM "site_prj_author"   相对应的only就是选择需要的字段。 result=Author.objects.all().only('age').query 对应的SQL: SELECT "site_prj_author"."id", "site_prj_author"."age" FROM "site_prj_author"   最后补充model的所有类型描述:

AutoField:一个自动递增的整型字段,添加记录时它会自动增长。你通常不需要直接使用这个字段;如果你不指定主键的话,系统会自动添加一个主键字段到你的model。(参阅自动主键字段)

BooleanField:布尔字段,管理工具里会自动将其描述为checkbox。

CharField:字符串字段,单行输入,用于较短的字符串,如要保存大量文本, 使用 TextField,CharField有一个必填参数:

TextField:一个容量很大的文本字段, admin 管理界面用 <textarea>多行编辑框表示该字段数据。

CommaSeparatedIntegerField:用于存放逗号分隔的整数值。类似 CharField,必须maxlength 参数。

DateField:日期字段,admin 用一个文本框 <input type=”text”> 来表示该字段数据(附带一个 JavaScript 日历和一个”Today”快捷按键。有下列额外的可选参数:

auto_now:当对象被保存时,自动将该字段的值设置为当前时间.通常用于表示 “last-modified” 时间戳; auto_now_add:当对象首次被创建时,自动将该字段的值设置为当前时间.通常用于表示对象创建时间。

FilePathField:选择指定目录按限制规则选择文件,有三个参数可选, 其中”path”必需的,这三个参数可以同时使用, 参数描述:

path:必需参数,一个目录的绝对文件系统路径。 FilePathField 据此得到可选项目。 Example: “/home/images”; match:可选参数, 一个正则表达式, 作为一个字符串, FilePathField 将使用它过滤文件名。 注意这个正则表达式只会应用到 base filename 而不是路径全名。 Example: “foo。*\。txt^”, 将匹配文件 foo23.txt 却不匹配 bar.txt 或 foo23.gif; recursive:可选参数, 是否包括 path 下全部子目录,True 或 False,默认值为 False。

FloatField:浮点型字段。 必须提供两个 参数, 参数描述:

max_digits:总位数(不包括小数点和符号) decimal_places:小数位数。如:要保存最大值为 999 (小数点后保存2位),你要这样定义字段:models.FloatField(…,max_digits=5, decimal_places=2),要保存最大值一百万(小数点后保存10位)的话,你要这样定义:models.FloatField(…,max_digits=19, decimal_places=10)

ImageField:类似 FileField, 不过要校验上传对象是否是一个合法图片。它有两个可选参数:height_field 和 width_field,如果提供这两个参数,则图片将按提供的高度和宽度规格保存。 该字段要求 Python Imaging 库。

IntegerField:用于保存一个整数。

IPAddressField:一个字符串形式的 IP 地址, (如 “202.1241.30″)。

NullBooleanField:类似 BooleanField, 不过允许 NULL 作为其中一个选项。 推荐使用这个字段而不要用 BooleanField 加 null=True 选项。 admin 用一个选择框 <select> (三个可选择的值: “Unknown”, “Yes” 和 “No” ) 来表示这种字段数据。

PhoneNumberField:一个带有合法美国风格电话号码校验的 CharField(格式:XXX-XXX-XXXX)。

PositiveIntegerField:类似 IntegerField, 但取值范围为非负整数(这个字段应该是允许0值的…可以理解为无符号整数)

PositiveSmallIntegerField:

正小整型字段,类似 PositiveIntegerField, 取值范围较小(数据库相关)SlugField“Slug” 是一个报纸术语。 slug 是某个东西的小小标记(短签), 只包含字母,数字,下划线和连字符。它们通常用于URLs。 若你使用 Django 开发版本,你可以指定 maxlength。 若 maxlength 未指定, Django 会使用默认长度: 50,它接受一个额外的参数:

SlugField:是一个报纸术语. slug 是某个东西的小小标记(短签), 只包含字母,数字,下划线和连字符.它们通常用于URLs。

SmallIntegerField:类似 IntegerField, 不过只允许某个取值范围内的整数。(依赖数据库)

TimeField:时间字段,类似于 DateField 和 DateTimeField。

URLField:用于保存 URL。 若 verify_exists 参数为 True (默认), 给定的 URL 会预先检查是否存在(即URL是否被有效装入且没有返回404响应)。

USStateField:美国州名缩写,由两个字母组成(天朝人民无视)。

XMLField:XML字符字段,校验值是否为合法XML的 TextField,必须提供参数:

schema_path:校验文本的 RelaxNG schema 的文件系统路径。

 

 

转载于:https://www.cnblogs.com/zhanghongfeng/p/7976355.html

相关资源:django queryset 去重 .distinct()说明
最新回复(0)