django学习之Model(四)MakingQuery

上一篇写到MakingQuey中的filter,本篇接着来。

10)-扩展多值的关系

如果对一个ManyToManyField或ForeignKey的表进行filter过滤查询的话,有2中方法可以用。分别是:

#1
Blog.objects.filter(entry__headline__contains=‘Lennon‘,
        entry__pub_date__year=2008)

#2
Blog.objects.filter(entry__headline__contains=‘Lennon‘).filter(
        entry__pub_date__year=2008)

假设,现有一个blog表,它对应了很多的entry的表,而且这个blog既有含有“Lennon”的entry,也有含有2008的entry,但是没有那种2者都有的entry,那么第一个方法过滤的是2者都有的entry,所以没有符合条件的blog返回。第一种方法可以理解为“并且”的意思。第二种是第一个filter返回含有“Lennon”的blog,然后在这个blog集合中,再选有2008的,所以会有blog返回。开始理解起来很费劲,抓住特点:一个blog对应多个entry。例如,blog1对应了entry1,entry2,entry3,其中entry1只有“Lennon”,没有2008;entry2只有2008,没有“Lennon”;entry3则什么都没有。那么用第一种方法来filter的时候,则没有满足既有Lennon又有2008的entry;第二种方法的第一个filter,因为entry1有Lennon,所以blog1为返回值,然后对返回值blog1进行filter,条件为有2008,而blog1中的entry2是有2008的,所以blog1作为返回值。

这个也适用于exlude().

11)-filters可以引用model中的fields

class F

目前为止的例子中,都是用field中的值来作为filter的条件来进行数据查询。但是如果需要把一个field的值与他所在的model的其他field的值呢?

django提供F()表达式来实现这种比较。F()的实例是在一个query中来对model中的field进行引用。这些引用可以作为filter的条件来进行过滤。

例如,要查询blog的entry,comments满足的数量比pingbacks的多,可以用F()来作用在pingbacks上,然后把结果作为filter的条件:

>>> from django.db.models import F
>>> Entry.objects.filter(n_comments__gt=F(‘n_pingbacks‘))

注意:F()表达式中是n_comments比大n_pingbacks。所以,也有倍数、加、减这样的操作可以来比较:

>>> Entry.objects.filter(n_comments__gt=F(‘n_pingbacks‘) * 2)#
n_comments比n_pingbacks的2倍多

 >>> Entry.objects.filter(rating__lt=F(‘n_comments‘) + F(‘n_pingbacks‘))#n_comments与n_pingbacks的和比rating多

gt是多理解为>,lt理解为<.

而下面的例子则是两个字符串一样:

>>> Entry.objects.filter(authors__name=F(‘blog__name‘))

12)-pk

是primary key的意思:

>>> Blog.objects.get(id__exact=14) # Explicit form
>>> Blog.objects.get(id=14) # __exact is implied
>>> Blog.objects.get(pk=14) # pk implies id__exact
# Get blogs entries with id 1, 4 and 7
>>> Blog.objects.filter(pk__in=[1,4,7])

# Get all blog entries with id > 14
>>> Blog.objects.filter(pk__gt=14)

同样应该有这些符号的组合,分别是__双下划线,lt,gt,exact等等。

如果查询含有%符号的,下例给出:

>>> Entry.objects.filter(headline__contains=‘%‘)

同样的_下划线符号也是,django会自动来处理的,就像上边这行程序这样写就行了。

5-QuerySet的缓存

每个QuerySet都有缓存来降低对数据库database的访问。弄明白了之后,可以写出高效率的代码。

在新创建的QuerySet中,缓存是空的。当QuerySet第一次 被计算的时候,django把QuerySet放在其缓存中,然后把具体要查询的数据项作为结果返回。把QuerySet这种缓存的行为时刻记住,有的时候很容易使用不当的。例如下面,建立了2个QuerySet,分别使用后,就丢掉了,这样造成了2次访问database,而且第二个e很可能不是第一个e,因为在第二个发生之前,database很可能已经被写入新数据或删除什么东西了:

>>> print([e.headline for e in Entry.objects.all()])
>>> print([e.pub_date for e in Entry.objects.all()])

正确的做法如下,建立个变量来存放QuerySet,让它一直放在内存中,然后每次用的时候从内存中去读取:

>>> queryset = Entry.objects.all()
>>> print([p.headline for p in queryset]) # Evaluate the query set.
>>> print([p.pub_date for p in queryset]) # Re-use the cache from the evaluation.

注意,第一行并没有touch the database,访问数据库发生在第二行代码。

QuerySet也并不是总缓存结果的,当计算queryset的一部分的时候,要缓存,但是如果只是用一个下一层的queryset来访问结果的时候,并没有缓存。这也意味着,可以用数组分片或者一个索引来限制queryset而不产生缓存,例如下面的例子,显示只定义了一个queryset来存放objects,然后用数组来访问其中的某一个,这样就没有缓存,每次都要重新访问数据库:

>>> queryset = Entry.objects.all()
>>> print queryset[5] # Queries the database
>>> print queryset[5] # Queries the database again

然而,如果整个queryset都被计算了,那么就会产生缓存:

>>> queryset = Entry.objects.all()
>>> [entry for entry in queryset] # Queries the database
>>> print queryset[5] # Uses cache
>>> print queryset[5] # Uses cache

还有一些例子也是整个querset都被计算了,当然产生了缓存:

>>> [entry for entry in queryset]
>>> bool(queryset)
>>> entry in queryset
>>> list(queryset)

6-用Q对象来进行复杂的查询

在filter()方法中,用关键字来查询,这些关键字是AND的关系,即”并且“,如果需要进行更复杂的查询,例如OR关系,可以用Q对象这种方法。

Q对象(django.db.models.Q)是用来封装关键字的集合的。这些关键字包括上文的lookup查询中所提到的关键字。例如,下面这个Q对象封装了LIKE查询:

from django.db.models import Q
Q(question__startswith=‘What‘)

Q对象可以用逻辑符号&或者|来连接,结果返回一个Q对象:

Q(question__startswith=‘Who‘) | Q(question__startswith=‘What‘)

同等于SQL语句:

WHERE question LIKE ‘Who%‘ OR question LIKE ‘What%‘

可以把Q对象组合成复杂的表达,也可以用~符号,代表”NOT,非“的意思:

Q(question__startswith=‘Who‘) | ~Q(pub_date__year=2005)

每个查询函数(filter(), get(), exclude())都需要有关键字,Q对象就可以作为这些查询函数的关键字:

Poll.objects.get(
    Q(question__startswith=‘Who‘),
    Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
)

翻译成SQL语句就相当痛苦了:

SELECT * from polls WHERE question LIKE ‘Who%‘
    AND (pub_date = ‘2005-05-02‘ OR pub_date = ‘2005-05-06‘)

查询函数也可以混合使用keywords和Q object,但是要注意,Q object必须在keyword之前定义:

#正确的
Poll.objects.get(
    Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)),
    question__startswith=‘Who‘)
#错误的
Poll.objects.get(
    question__startswith=‘Who‘,
    Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)))

7-比较对象

用python标准的比较操作符==就可以进行model实例的比较。而这实际上实在比较primarykey的值:

>>> some_entry == other_entry
>>> some_entry.id == other_entry.id#实际上

如果model的primarykey不叫id,没关系,照样还是在比较primarykey,例如一个model的primarykey是name,则下面2行代码是同样意思的:

>>> some_obj == other_obj
>>> some_obj.name == other_obj.name

8-删除对象

delete()用来进行对象的删除,而且是立即生效,并且没有返回值:

e.delete()

也可以批量来进行删除操作,每个QuerySet都有delete()操作,可以把QuerySet中的所有对象都删掉:

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

delete是Manager的一个方法,但是由于怕用户误操作而没有显式的显现出来。因为delete是纯粹的在SQL中执行的方法,所以不需要用户在过程中去认为的调用。如果你在一个model的class定义中自己定义了一个delete()的话,那么就相当于覆盖掉了Manager提供的delete,这样就需要用户自己去调用了,而且这种自己定义的delete()还不能批量的来操作,也就是不能作为QuerySet中的参数来调用,而是需要自己对每个object单独进行delete一次。

django提供的delete()方法,会删除由foreignkey来关联到一起的object:

b = Blog.objects.get(pk=1)
# This will delete the Blog and all of its Entry objects.
b.delete()

这种串联的用法是通过 on_delete 参数传递给Foreignkey的。

delete是Manager的一个方法,为了防止用户误操作,所以不用显式调用,但是如果确实需要调用delete()来删除所有的objects的话,应该明确的指出来这个QuerySet:

Entry.objects.all().delete()

9-复制model实例

尽管没有built-in的方法来实现model 实例的复制,但是可以用model的所有的fields的值来创建一个新的实例,最简单的情况就是,先把pk设置成None,然后再save:

1 blog = Blog(name=‘My blog‘, tagline=‘Blogging is easy‘)
2 blog.save() # blog.pk == 1
3
4 blog.pk = None
5 blog.save() # blog.pk == 2

思考:也就是,第1行代码只是把Blog内容放在了blog这个变量中,并没写进去数据库,只有调用save()方法时,才发生写的操作。而且每当调用save(),都相当于要把这个数据(blog)写进数据库。而数据库的pk是唯一而不能重复的,所有先要把blog.pk设成None,然后再操作,由SQL语句把pk由依次递增为2了。

如果有model的继承时,会略复杂一些:

class ThemeBlog(Blog):
    theme = models.CharField(max_length=200)

django_blog = ThemeBlog(name=‘Django‘, tagline=‘Django is easy‘, theme=‘python‘)
django_blog.save() # django_blog.pk == 3

因为继承的工作原理,这次需要把pk和id都设成None了:

django_blog.pk = None
django_blog.id = None
django_blog.save() # django_blog.pk == 4

这个过程并没有复制相关联的objects,如果想要复制这种关系,需要多写一些代码了。在本文的例子中,Entry和Author是多对多的关系(ManyToManyField):

1 entry = Entry.objects.all()[0] # some previous entry
2 old_authors = entry.authors.all()
3 entry.pk = None
4 entry.save()
5 entry.authors = old_authors # saves new many2many relations

今天到这,明天继续!

django学习之Model(四)MakingQuery,布布扣,bubuko.com

时间: 2024-10-14 16:40:16

django学习之Model(四)MakingQuery的相关文章

django学习之Model(五)MakingQuery

接着上篇. 10-一次更新多个对象 有时想要对QuerySet中的所有对象的某一个field来设定一个值,这时候可以像下边这样用update(): # Update all the headlines with pub_date in 2007. Entry.objects.filter(pub_date__year=2007).update(headline='Everything is the same') 这样使用的update()只能是没有关联关系的model或者有ForeignKey的

Django学习笔记(四)—— Admin

疯狂的暑假学习之  Django学习笔记(四)-- Admin 参考:<The Django Book> 第6章 Django 可以使用admin自动创建管理界面. 1. 配置 django-admin.py startproject 创建的项目,如果没有注解掉默认的配置,python manage.py syncdb 创建用户后,直接 http://xxxxxx/admin 输入密码即可进入. 如果修改了配置文件,保证将 django.contrib.admin 加入setting.py 中

django学习之Model(三)QuerySet

接下来主要学习Models中的Making queries 写好models.py后,django会自动提供一个数据库的抽象API,来实现CRUD(create, retrieve, update, delete).这一部分主要就是怎样去用这些API.在data model reference会有全部的讲解. 接下来都会反复用到下边这个例子,或者在这段代码上进行扩展: from django.db import models class Blog(models.Model): name = mo

django学习之Model(二)

继续(一)的内容: 1-跨文件的Models 在文件头部import进来,然后用ForeignKey关联上: from django.db import models from geography.models import ZipCode class Restaurant(models.Model): # ... zip_code = models.ForeignKey(ZipCode) 2-field名字的约束 1)-不能是Python预留字 2)-不能有连续的2个下划线,例如foo__ba

Django学习之model进阶

本节目录 一 QuerySet 二 中介模型 三 查询优化 四 extra 五 整体插入 六 xxx 七 xxx 八 xxx 一 QuerySet 可切片 使用Python 的切片语法来限制查询集记录的数目 .它等同于SQL 的LIMIT 和OFFSET 子句. >>> Entry.objects.``all``()[:5] # (LIMIT 5) >>> Entry.objects.all()[5:10] # (OFFSET 5 LIMIT 5) 不支持负的索引(例如

Django 学习笔记(四)模板变量

关于Django模板变量官方网址:https://docs.djangoproject.com/en/1.11/ref/templates/builtins/ 1.传入普通变量 在hello/Hello World/temlplates/index.html中,修改html文件 <!DOCTYPE html> <html lang="zh-cn"> <head> <title>{{title}}</title> <met

Python框架之Django学习笔记(四)

第一个基于Django的页面:Hello World 正如我们的第一个目标,创建一个网页,用来输出这个著名的示例信息:Hello world. 第一个视图 Hello world视图非常简单. 这些是完整的函数和导入声明,把它们输入到views.py文件: 1 from django.http import HttpResponse 2 3 def hello(request): 4 return HttpResponse("Hello World!\nIt's my First django

Django学习笔记(三)—— 模型 model

疯狂的暑假学习之 Django学习笔记(三)-- 模型 model 参考:<The Django Book> 第5章 1.setting.py 配置 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.', # 用什么数据库管理系统 'NAME': '', # 数据库名称,如果用sqlite,要写完整路径 'USER': '', # 如果用sqlite,这个不用写 'PASSWORD': '', # 如果用sqlite,这个不用写

Django学习笔记(二)App创建之Model

通过实例学习, 构建一个投票(Polls)Application, 目标结果包含两个site, 一个site用来显示投票问题以及投票结果(即将展示出来的网站), 另一个site用来管理Poll实例的增删改查(即后台内容管理CMS). 1.   创建工程 django-admin.py startproject mysite 在当前目录下, 会创建一个mysite的工程目录. 那么我们的代码放在哪里比较好呢? 可能会放到OS自带的server根目录(document root)下, 比如/var/