Django model 层之Making Query总结2

简介: Django model 层之Making Query总结

 

检索单个对象

例:

>>> only_one_entry = Person.objects.get(id=1)

 

注意:

如果没有匹配的查询结果,则抛出DoesNotExist异常,如果查询结果多余一个对象,则抛出  MultipleObjectsReturned,对比之下,如果查询不到结果,filter则返回空结果集,不会报错。

 

更多检索方法参考API:

https://docs.djangoproject.com/en/2.0/ref/models/querysets/#django.db.models.query.QuerySet

 

 

主键快捷查找

Django提供pk(主键)查找快捷方式,

 

SELECT * FROM `myapp_store`

 

id  name   last_update

7   aimi   2018-03-25

8   ximi   2018-03-25

9   xima   2018-03-25

10  masu   2018-03-25

11  gami   2018-03-25

12  gama   2018-03-25

其中id为表的主键

 

检索主键id为7的对象

>>> Store.objects.get(id__exact=7)

>>> Store.objects.get(id=7)

>>> Store.objects.get(pk=7)

 

检索主键id为7,8,9的对象

>>> Store.objects.filter(pk__in=[7, 8, 9])

<QuerySet [<Store: Store object>, <Store: Store object>, <Store: Store object>]>

 

 

 

同样的,pk查找也支持联合查询。

>>> from myapp.models import Fruit

>>> f = Fruit.objects.get(id=3)

>>> f.store.add(Store.objects.get(id=7))

 

 

检索myapp_fruit表中,同myapp_store表记录存在多对多关联关系,且myapp_store.id主键值为7的记录。

>>> Fruit.objects.filter(store__id=7)

<QuerySet [<Fruit: Fruit object>]>

 

>>> Fruit.objects.filter(store__pk=7)

<QuerySet [<Fruit: Fruit object>]>

 

>>> Fruit.objects.filter(store__id__exact=7)

<QuerySet [<Fruit: Fruit object>]>

 

注意双下线的书写。

 

 

限制查询结果集

例:返回检索结果的前两个对象

>>> Person.objects.all()[:2]

 

等同于SELECT * FROM `myapp_person` LIMIT 2

 

例:返回从第2条开始的对象,一共返回1个对象。

>>> Person.objects.all()[1:2]

 

等同于SELECT * FROM `myapp_person` LIMIT 1, 1

 

注意:

1、不支持复数索引,比如 Person.objects.all()[-1]

2、对查询结果“切片”,会返回一个新的结果集,并不会重新计算查询。但是如果切片使用了步长值,则每次取值都会重新执行查询,为了按步长值返回列表。例:第一个对象开始,每隔一个对象取一次,直到索引为10停止。

>>> Person.objects.all()[:10:2]

 

对于接收单个对象,我们也可以这么做,先排序,然后取第一个对象。

>>> Person.objects.order_by('id')[0]

 

等同于

>>> Person.objects.order_by('id')[0:1].get()

 

注意:使用get()如果没有匹配记录,则会抛出DoesNotExist 异常。

 

字段查询

基本的查询关键词,格式形如:field__

lookuptype=value ,可用于filter,exclude,get等函数。注意,双下划线。

 

常用字段查询

lte 小于等于

 

gte 大于等于

 

gt  大于

 

lt  小于

 

exact 精确查询

 

iexact 类似exact, 不同之处在于大小写不敏感

 

contains 包含,模糊查询,大小写敏感。

 

startswith 仅匹配开头

 

endswith 仅匹配结尾

 

istartswith 仅匹配开头,类似startswith,不同之处在于大小写不敏感

 

iendswith 仅匹配结尾,类似endswith,不同之处在于大小写不敏感

 

例子:查询id小于等于 1的对象。

>>> Person.objects.filter(id__lte = 1)

 

等同于

SELECT * FROM `myapp_person` WHERE id <=1;

 

>>> Person(first_name='yu', last_name='lai').save()

>>> Person(first_name='yu', last_name='laiyu').save()

 

例:查找last_name值为laiyu的对象

>>> Person.objects.get(last_name__exact='laiyu')

 

等效做法:

>>> Person.objects.get(last_name='laiyu')

 

等同于SELECT * FROM `myapp_person` WHERE last_name='laiyu';

例:检索last_name包含lai的对象

>>> Person.objects.filter(last_name__contains='lai')

 

等同于

SELECT * FROM `myapp_person` WHERE last_name LIKE '%lai%'

 

例:查询last_name以lai开头的对象

>>> Person.objects.filter(last_name__startswith='lai')

 

等同于

SELECT * FROM `myapp_person` WHERE last_name LIKE '%lai'

 

 

例:查询last_name以yu结尾的对象

>>> Person.objects.filter(last_name__endswith='lai')

 

等同于

SELECT * FROM `myapp_person` WHERE last_name LIKE 'lai%'

 

更多字段查询参考API

https://docs.djangoproject.com/en/2.0/ref/models/querysets/#field-lookups

 

 

F表达式和Filter的配合使用

可结合Filter,用F表达式,对同一model的不同Field做检索

 

SELECT * FROM `myapp_news`

 

id  title  n_comments n_pingbacks  rank

1   news1  20              40           34

2   news2  35              30           25

3   news3  14              20           34

 

检索myapp_news表,查找n_comments值大于n_pingbacks值的记录

>>> from myapp.models import News

>>> from django.db.models import F

>>> mynews = News.objects.filter(n_comments__gt=F('n_pingbacks'))

>>> for news in mynews:

...    print(news.title)

...

news2

 

检索myapp_news表,查找n_pingbacks值大于等于n_comments值2倍的记录

>>> mynews = News.objects.filter(n_pingbacks__gte=F('n_comments') * 2)

>>> for news in mynews:

...    print(news.title)

...

news1

 

检索myapp_news表,查找rank值等于n_comments+n_pingbacks的记录

>>> mynews = News.objects.filter(rank=F('n_comments') + F('n_pingbacks'))

>>> for news in mynews:

...     print(news.title)

...

news3

 

对于date/time field,还可以加减timedlta对象

>>> from datetime import timedelta

>>> Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3))

 

更多参考链接:

https://docs.djangoproject.com/en/1.11/topics/db/queries/#filters-can-reference-fields-on-the-model

 

 

转义字符%_

iexact, contains, icontains, startswith, istartswith, endswith 和iendswith这些类似SQL LIKE语句的字段查找,会自动化转义 % 和 _ 这两种字符。

例:

>>> Book.objects.filter(book_name__contains = '%')

 

等同SELECT * FROM myapp_book WHERE book_name LIKE '%\%%';

 

 

缓存和查询

Django会缓存查询结果集。可适当利用这个特征来加速查询,减轻系统压力。

例子:

>>> from myapp.models import Book

>>> print([b.book_name for b in Book.objects.all()])

>>> print([b.borrower for b in Book.objects.all()])

 

如果按以上方式书写,会去数据库查询两次,而且两次的查询还可能不一样,因为有可能新增了数据、删除了数据,为了避免这个问题,简单的做法是保存查询结果集,并重用它。优化方案如下:

# 计算查询结果集

>>> print([b.book_name for b in queryset])

 

# 重用缓存的结果集

>>> print([b.borrower for b in queryset])

 

 

不使用缓存的查询

当仅查询部分查询结果集时,会检查缓存,但是如果结果集还没被填充(个人理解,还没执行数据库查询,并没有真正返回整个查询结果集存放至定义的查询结果集变量),后续子查询返回的结果项将不会被缓存。这也就意味着如果使用数组切片,或者索引的方式限制返回结果集,将不使用缓存,(Querysets do not always cache their results. When evaluating only part of the queryset, the cache is checked, but if it is not populated then the items returned by the subsequent query are not cached. Specifically, this means that limiting the queryset using an array slice or an index will not populate the cache.)

 

例如以下,重复获取查询结果集中索引值为4的对象

>>> queryset = Book.objects.all()

>>> print(queryset[4]) # 查询数据库

>>> print(queryset[4]) # 不使用缓存,再次查询数据库。

 

>>> queryset = Book.objects.all()

>>> [book for book in queryset]  # 查询数据库

>>> print(queryset[4])  # 使用缓存

>>> print(queryset[4])  # 使用缓存

 

其它一些会导致查询整个结果集并缓存的行为举例

>>> [entry for entry in queryset]

>>> bool(queryset)

>>> entry in queryset

>>> list(queryset)

 

注意:单纯的print结果集,并不会填充缓存,因为调用__repr__()仅返回整个查询结果集的切片(Simply printing the queryset will not populate the cache. This is because the call to __repr__() only returns a slice of the entire queryset)

 

使用Q对象执行更复杂的查找

可以使用Q实现OR的关系

例子:查找myapp_book表中,book_name为yueding或者duzhe的记录

>>> from django.db.models import Q

>>> Book.objects.filter(Q(book_name='yueding') | Q(book_name='duzhe'))

<QuerySet [<Book: Book object>, <Book: Book object>]>

 

 

等价于:

SELECT * FROM myapp_book WHERE boo_name = 'yueding' or book_name='duzhe'

 

使用~Q实现NOT查询

例:查询myapp_book表中,book_name为yueding,或者 book_name不为duzhe的记录

>>> Book.objects.filter(Q(book_name='yueding') | ~Q(book_name='duzhe'))

 

 

每个接收关键词参数的查询函数(filter,exclude,get等)都可以传递1个或多个Q对象,作为位置参数。如果提供了多个Q对象参数给查询函数,这些参数之间将是 AND 关系。

 

>>> Book.objects.filter(Q(borrower_id=2), Q(book_name='duzhe') | Q(book_name='yueding'))

 

等同于执行SQL

SELECT * FROM myapp_book WHERE borrower_id = 2 AND (book_name = 'duzhe' OR book_name = 'yueding')

 

可以混用关键词参数,和Q对象,他们之间为 AND 关系,但是关键词参数必须位于位置参数后面。

>>> Book.objects.get(Q(book_name='duzhe') | Q(book_name='yueding'), borrower_id=2)

<Book: Book object>

 

比较对象

可使用标准python比较符 == 比较同一个model的两个实例,实质是比较实例的主键值。

例:

>>> one_book = Book.objects.get(book_name='yueding')

>>> anther_book = Book.objects.get(id=1)

>>> one_book == anther_book

True

 

等价比较法:

>>> one_book.id == anther_book.id

True

 

假设主键不是id,比如说是name呢,如下

>>> one_book.name == anther_book.name

 

 

删除对象

delete(),执行该方法,删除对象并返回被删除对象的数量及每种对象类型的被删除的数量(使用字典方式表示)

例:删除myapp_book表中,book_name为qinglv的记录

>>> one_book = Book.objects.get(book_name='qinglv')

>>> one_book.delete()

(1, {'myapp.Book': 1})

 

批量删除

每个查询结果集都有一个delete()方法,删除QuerySet的所有成员

 

例:删除myapp_book表中,所有borrower_id为2的记录

>>> Book.objects.filter(borrower_id=2).delete()

(2, {'myapp.Book': 2})

 

假如想删除所有对象,可参照如下语句

>>> Book.objects.all().delete()

 

备注:即便通过filter检索出来的结果为空,调用delete函数也不会报错,假如borrower_id=2的记录不存在,执行delete函数也不会报错。

 

复制model实例

例:

>>> book = Book(book_name="test1", borrower_id = 2)

>>> book.save() # 新增第一条记录

 

>>> book.pk = None # 设置主键值为None

>>> book.save() #执行save方法时,会新增第二条记录,主建值自动加1,其它内容和第一条保持一样

 

如果使用了继承,则如果想想复制继承类的实例,则复制之前需要设置主键列及pk为None

>>> from myapp.models import Book,Blog

>>> borrower = Person.objects.get(id=1)

>>> dj_blog = Blog(book_name='mybook', borrower=borrower, author='laiyu')

>>> dj_blog.save()

 

>>> dj_blog.id = None

>>> dj_blog.pk = None

>>> dj_blog.save()

 

SELECT * FROM myapp_book WHERE book_name = 'mybook'

显示:

id  book_name  borrower_id

8   mybook       1

9   mybook       1

 

 

SELECT * FROM myapp_blog

显示:

book_ptr_id   author

8                laiyu

9                laiyu

 

注意,继承类实例的数据存储,继承字段的值是存在基类对应的数据表中的。

 

 

这种处理方式并不会复制不属是model数据库表部分的关系。比如Entry model有一个指向Author的ManyToManyField。在复制entry后,需要为新的entry设置多对多的关系。

 

entry = Entry.objects.all()[0] # 之前创建的某个entry

old_authors = entry.authors.all()

entry.pk = None

entry.save()

entry.authors.set(old_authors)

 

对于一对一关系,必须复制关联对象,并赋值给对新对象的field,以面违反一对一唯一性约束。

例,假设entry已经被复制了。

detail = EntryDetail.objects.all()[0]

detail.pk = None

detail.entry = entry

detail.save()

 

批量更新多个对象

使用update()方法,一次性批量更新查询结果集中对象的某个field值。

 

例:查找myapp_news表中,id大于1的记录,并把这些记录的rank字段值,统一+1

>>> from django.db.models import F

>>> News.objects.filter(id__gt=1).update(rank=F('rank') + 1)

 

当然,我们也可以一次性更新多个字段,字段之间用逗号分隔,如下,同时更新title和rank

>>> News.objects.filter(id__gt=1).update(title='newTitle', rank=F('rank') + 1)

 

update参数也可以写成字典的形式

>>> data = {'title': 'newTitle',' rank:'0'}

>>> News.objects.filter(id__gt=1).update(**data)

 

2

 

注意:仅可设置和其它表不存在关联关系的field。如果是更新无关联Field,只需要提供新值作为常量值即可。但是如果要更新外键Field,则需要提供model实例作为新值。(You can only set non-relation fields and ForeignKey fields using this method. To update a non-relation field, provide the new value as a constant. To update ForeignKey fields, set the new value to be the new model instance you want to point to.)

 

例:

设置myapp_book表,所有记录的外键borrower为shouke

>>> borrower=Person.objects.filter(last_name='shou', first_name='ke')[0]

>>> Book.objects.all().update(borrower=borrower)

6

 

注意,update操作后立即生效,并返回匹配查询的行记录(不一定是更新的行记录数,如果某些行已经是最新值的情况下)。另外,update只能更新一张表:model主表。可以基于关联field做过滤,但是只能更新主表中的列。

 

 

 

更多资料参考链接:

https://docs.djangoproject.com/en/1.11/topics/db/queries/#updating-multiple-objects-at-once

 

 

关联对象

一对多关系

 

正向查找

例子:通过属性Field访问被关联对象(外键对象)

>>> book = Book.objects.get(id=2)

>>> book.borrower

<Person: Person object>

>>>

 

修改外键对象

>>> person = Person.objects.get(id=2)

>>> book.borrower=person

>>> book.save()

 

如果定义ForeignKey时,指定了null=True,可指定None来移除关联。

>>> book = Book.objects.get(id=2)

>>> book.person = None

>>> book .save() # "UPDATE myapp_book SET person_id  = NULL ...;"

 

一对多关系的访问,会缓存第一次获取的被关联对象。

>>> book = Book.objects.get(id=2)

>>> print(book.borrower) # 访问数据库

>>> print(book.borrower) # 使用缓存

 

使用select_related方法,会递归缓存一对多关系。

比如:

>>> book = Book.objects.select_related().get(id=2)

>>> print(book.borrower) # 使用缓存

>>> print(book.borrower) # 使用缓存

 

 

“反向”追踪关系

例:查找myapp_book表中,同myapp_person表存在外键引用,且被引用记录id为1的表记录

>>> person=Person.objects.get(id=1)

>>> person.book_set.all()

<QuerySet [<Book: Book object>, <Book: Book object>, <Book: Book object>, <Book: Book object>, <Book: Book object>]>

>>>

 

# person.book_set是一个方便查询结果集的管理器. 注意书写格式:ForeignKey指向的model的实例.包含ForeignKey Field的小写model名称_set。

 

>>> person.book_set.filter(book_name='mybook')

<QuerySet [<Book: Book object>, <Book: Book object>]>

>>> person.book_set.count()

5

 

可以在定义外键时,通过设置related_name来重写FOO_set名称。比如

更改Book model

borrower = ForeignKey(Person, to_field='id', on_delete=models.CASCADE, related_name='books')。

则可这么写

>>> person=Person.objects.get(id=1)

>>> person.books.all()

 

>>> person.books.filter(book_name='mybook')

>>> person.books.count()

 

使用自定义反向管理器

参考链接:

https://docs.djangoproject.com/en/2.0/topics/db/queries/#using-a-custom-reverse-manager

 

 

处理被关联对象的其它方法

add(obj1, obj2, ...)

添加指定model对象到被关联对象集

 

create(**kwargs)

创建一个新对象,保存并放入被关联对象集。返回新建对象。

 

remove(obj1, obj2, ...)

从被关联对象集中移除model对象。

 

clear()

移除所有关联对象集

 

set(objs)

替换被关联的结果对象集。

 

本节可参考 “修改对象 -修改包含多对多Field的对象”

>>> book1 = Book.objects.filter(book_name='duzhe')[0]

>>> book2 = Book.objects.filter(book_name='shenghuo')[0]

>>> person=Person.objects.get(id=2)

>>> person.book_set.set([book1, book2])

 

结果,更新了myapp_book中的两条记录,更新结果如下(borrower_id变成了2)

id  book_name  borrower_id

2   duzhe      2

4   shenghuo   2

 

>>> book2 = Book.objects.filter(book_name='test1')[0]

>>> person.book_set.add(book2)

 

结果,更新了myapp_book中的1条记录,更新结果如下(borrower_id变成了2)

id  book_name  borrower_id

6   test1      2

 

>>> person.book_set.create(book_name='mybook3')

<Book: Book object>

 

结果,往myapp_book表新增一条记录,且borrower_id为2

id  book_name  borrower_id

10   mybook3    2

 

注意:不能像上面一样,按下面的方法使用clear,remove,会报错(似乎只能用于多对多)

>>> person.book_set.clear()

>>> person.book_set.remove()

 

 

参考链接:

https://docs.djangoproject.com/en/2.0/topics/db/queries/#additional-methods-to-handle-related-objects


 

多对多关系

类似“一对多”关系

>>> from myapp.models import Fruit

>>> f = Fruit.objects.get(pk=3)

>>> f.store.all()

<QuerySet [<Store: Store object>]>

>>> f.store.count()

1

>>> f.store.filter(id=7)

<QuerySet [<Store: Store object>]>

 

反向检索

>>> from myapp.models import Store

>>> s = Store.objects.get(id=7)

>>> s.fruit_set.all()

<QuerySet [<Fruit: Fruit object>]>

 

 

参考链接:

https://docs.djangoproject.com/en/2.0/topics/db/queries/#many-to-many-relationships

 

一对一关系

 

参考链接:

https://docs.djangoproject.com/en/2.0/topics/db/queries/#one-to-one-relationships

 

 

 

入口参考链接:

https://docs.djangoproject.com/en/2.0/topics/db/queries/

 

目录
相关文章
|
1月前
|
关系型数据库 MySQL Java
Django学习二:配置mysql,创建model实例,自动创建数据库表,对mysql数据库表已经创建好的进行直接操作和实验。
这篇文章是关于如何使用Django框架配置MySQL数据库,创建模型实例,并自动或手动创建数据库表,以及对这些表进行操作的详细教程。
61 0
Django学习二:配置mysql,创建model实例,自动创建数据库表,对mysql数据库表已经创建好的进行直接操作和实验。
|
数据库 Python
[Python]Django模型(Model)(二)
[Python]Django模型(Model)(二)
|
设计模式 SQL 数据库
|
Python
django model进阶学习
django model进阶学习
49 0
|
存储 关系型数据库 MySQL
Django第2步_创建model数据库表
Django第2步_创建model数据库表
112 0
|
数据库 Python
Django model 层之事务管理总结
Django model 层之事务管理总结
134 0
|
SQL JSON 测试技术
Django model层之执行原始SQL查询
Django model层之执行原始SQL查询
119 0
|
关系型数据库 MySQL 数据库
Django model 层之聚合查询总结
Django model 层之聚合查询总结
108 0
|
SQL 关系型数据库 MySQL
Django model 层之Making Query总结1
Django model 层之Making Query总结
73 0
|
SQL JavaScript 前端开发
Django之Model世界
Model 到目前为止,当我们的程序涉及到数据库相关操作时,我们一般都会这么搞: 创建数据库,设计表结构和字段 使用MySQLdb 来连接数据库,并编写数据访问层代码 业务逻辑层去调用数据访问层执行数据库操作     import MySQLdb def GetList(sql): db = MySQLdb.
1020 0