Django model 层之Making Query总结

Django model 层之Making Query总结

by:授客 QQ1033553122

 

实践环境

Python版本:python-3.4.0.amd64

下载地址:https://www.python.org/downloads/release/python-340/

 

 

Win7 64位

 

Django  1.11.4

下载地址:https://www.djangoproject.com/download/

 

 

API交互

MySQL数据库为例,假设项目目录结构如下:

mysite/

myapp/

    __init__.py

admin.py

apps.py

migrations/

    __init__.py

models.py

tests.py

views.py

    manage.py
    mysite/
        __init__.py
        settings.py
        urls.py
        wsgi.py

 

 

models.py内容如下:

from django.db import models

# Create your models here.
class Person(models.Model):

   first_name = models.CharField(max_length=30)
   last_name = models.CharField(max_length=30)

class Book(models.Model):
    book_name = models.CharField(max_length=30)
    borrower = models.ForeignKey(Person, to_field='id', on_delete=models.CASCADE)


class Blog(Book):

   author = models.CharField(max_length=50)

 

class Store(models.Model):

    id = models.AutoField(primary_key=True)

    name = models.CharField(max_length=50)

    last_update = models.DateField(auto_now=True)

 

class Fruit(models.Model):

    store = models.ManyToManyField(Store)

    name = models.CharField(max_length=100)

onsale_date = models.DateField()

 

class News(models.Model):

   title = models.CharField(max_length=20)

   n_comments = models.IntegerField()

   n_pingbacks = models.IntegerField()

 

进入到项目根目录下,运行python

cd /d F:\project\Django\MyProjects\mysite\

python

 

 

>>> import os

>>> os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")

'mysite.settings'

>>> 

 

说明:

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project_name.settings")

 

添加以上代码避免出现以下问题:

django.core.exceptions.ImproperlyConfigured: Requested setting DEFAULT_INDEX_TABLESPACE, but settings are not configured. You must either define the environment variable DJANGO_SETTINGS_MODULE or call settings.configure() before accessing

 

 

>>> import django

>>> django.setup()

 

说明:

添加以上代码,避免出现以下问题

……

 raise AppRegistryNotReady("Apps aren't loaded yet.")

django.core.exceptions.AppRegistryNotReady: Apps aren't loaded yet.

 

创建对象

>>> from myapp.models import Person

 

说明:

from app_name.models import module

 

python当前目录为项目根目录下,models位于app目录下,所以如上 app_name.models

 

>>> person = Person(first_name="ke", last_name="shou")

>>> person.save()

 

执行save()方法时,等同于执行以下sql INSERT语句。

INSERT INTO `myapp_person` (`id`, `first_name`, `last_name`) values('1','ke','shou');

 

说明:创建对象时也可以使用字典解引的方式,如下:

>>> fields_dict = {"first_name":"ke", "last_name":"shou"}

>>> person = Person(**fields_dict)

 

 

创建包含外键field的对象

>>> from myapp.models import Person,Book

 

方式1:创建对象时,外键Field名称要写为“外键Field名称_id”的形式

>>> book1 = Book(borrower_id='1', book_name='mybook')

>>> book1.save()

 

 

注意:如果,外键Field名称直接使用所在模块类中的定义的类属性名称,直接设置值为外键Field的值,则会提示外键Field的值必须为外键Field所关联类的实例,如下:

>>> book1 = Book(borrower='1', book_name='mybook')

……

    self.field.remote_field.model._meta.object_name,

ValueError: Cannot assign "'1'": "Book.borrower" must be a "Person" instance.

 

方式2:解决以上依赖问题

>>> person2 = Person(first_name="ku", last_name="shou")

>>> person2.save()

>>> book2 = Book(borrower=person2, book_name='mybook2')

>>> book2.save()

 

 

创建包含多对多Field的对象

 

针对多对多Field,除了建立model对应的表外,django还会新增一张关联表:app名称_包含ManyToManyField类型Field的model名称_ManyToManyField类型Field指定的model名称(例中myapp_fruit_store),存放两个model对应的表对应记录的id

 

>>> from myapp.models import Store,Fruit

>>> amy_store = Store.objects.create(name='aimi')

 

SELECT * FROM `myapp_store` WHERE `name` = 'aimi';

显示:

id   name   last_update

7    aimi   2018-03-25

 

说明:以create方式建立对象,无需执行obj.save()函数就会在表新增对应的记录,如上,如果添加,amy_store.save()代码,则会在此基础上,再新增一条记录。内容和amy_store对象的一样。

 

 

 

注意:针对多对多,不能按以下方式创建对象,会报错:

>>> apple = Fruit(name='apple',onsale_date='2018-03-24')

>>> apple.save()

 

SELECT * FROM `myapp_fruit` WHERE `name` = 'apple';

显示:

id    name    onsale_date

3     apple   2018-03-24

 

注意:调用add之前,必须保证对象自身已存在,即已创建,否则会报类似如下的错误:

ValueError: "<Fruit: Fruit object>" needs to have a value for field "id" before this many-to-many relationship can be used.

 

由此可见,我们不能通过类似以下语句,一次性创建包含ManyToManyField类型Field的对象

banana = Fruit(store=aimi_store,name='banana',

onsale_date='2018-03-24')

banana.save()

 

到目前为止,关联表myapp_fruit_store里还是没有记录

>>> apple.add(aimi_store)

 

SELECT * FROM `myapp_fruit_store`

显示:

id    fruit_id     store_id

1     3             7

 

 

修改对象

person.last_name="yu"

person.save()

 

执行save()方法时,等同于执行以下sql UPDATE语句。

UPDATE `myapp_person` SET last_name ='yu'  where id =1;

 

修改包含外键的对象

清空Person,Book moduel关联的表数据后,执行以下操作

 

例:

>>> person = Person(first_name='ke', last_name="shou")

>>> person.save()

>>> book = Book(book_name="yueding", borrower=person)

>>> book.save()

 

SELECT * FROM myapp_person

显示:

id   first_name    last_name

1    ke            shou

 

SELECT * FROM myapp_book

显示:

id   book_name  borrower_id

1    yueding    1

 

>>> person = Person(first_name='yu', last_name='lai')

>>> person.save()

>>> book.borrower = person

>>> book.save()

 

SELECT * FROM myapp_person

显示:

id   first_name    last_name

1    ke            shou

2    yu            lai

 

SELECT * FROM myapp_book

显示:

id   book_name  borrower_id

1    yueding    2

 

修改包含多对多Field的对象

add

增加model对象到关联对象,即在model对应表中增加关联记录

>>> ximi_store = Store.objects.create(name='ximi')

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

>>> apple.store.add(ximi_store)

 

SELECT * FROM `myapp_fruit_store`

显示:

id    fruit_id     store_id

1     3             7

2     3             8

 

结果如上,新增一条关联记录,关联新增的ximi_store

 

当然,我们也可以一次性关联多个对象

>>> xima_store = Store.objects.create(name='xima')

>>> masu_store = Store.objects.create(name='masu')

>>> apple.store.add(xima_store, masu_store)

 

SELECT * FROM `myapp_fruit_store`

显示:

id    fruit_id     store_id

1     3             7

2     3             8

3     3             9

4     3             10

 

remove

从已关联对象中,删除指定关联对象

承接add

>>> apple.store.remove(masu_store)

 

SELECT * FROM `myapp_fruit_store`

显示:

id    fruit_id     store_id

1     3             7

2     3             8

3     3             9

 

注意:上面是基于“add”中操作的,如果还没获取对象,则需要通过类似 model_name.objects.get(id=1)的方式,先获取对象,然后操作对象。

 

set

注:以下基于remove之后的操作

>>> gami_store = Store.objects.create(name='gami')

>>> gama_store = Store.objects.create(name='gama')

>>> new_list = [gami_store, gama_store]

>>> apple.store.set(new_list)

 

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

 

 

SELECT * FROM `myapp_fruit_store`

显示:

id    fruit_id     store_id

6     3             11

5     3             12

 

如上,关联的对象,全部被替换为最新的了

 

clear

清空所有已关联对象

>>> apple.store.clear()

 

结果myapp_fruit_store表中的记录全部被清空了。

 

参考链接:

https://docs.djangoproject.com/en/1.11/ref/models/relations/#django.db.models.fields.related.RelatedManager.add

 

检索对象

检索所有对象

例:

>>> person = Person(first_name="ke", last_name="shou")

>>> person.save()

 

>>> all_entries = Person.objects.all()

>>> all_entries

<QuerySet [<Person: Person object>]>

>>> print(all_entries.query) # 查看获取结果集执行对sql语句

说明:

all()方法会返回数据库表中所有记录的结果集,等同于以下sql SELECT 语句

SELECT * FROM `myapp_person`

 

另外,我们可以通过list(QueryResultSet)把QuerySet类型的查询结果转为列表类型

 

通过过滤器检索特定对象

例:

>>> entries = Person.objects.filter(first_name='ke')

 

说明:

等同于

>>> entries = Person.objects.all().filter(first_name='ke')

 

执行以上filter方法,等同于执行以下SQL方法

SELECT * FROM `myapp_person` WHERE first_name = 'ke'

 

 

>>> entries = Person.objects.exclude(last_name='yu')

 

执行以上exclude方法,等同于执行以下SQL方法

SELECT * FROM `myapp_person` WHERE first_name != 'yu'

 

 

链式过滤器

例:

>>> Person.objects.filter(first_name='ke').filter(last_name='shou')

 

说明:先过滤出first_name值为ke的,然后再从这里面筛选出last_name为 shou的。

等同于

>>> Person.objects.filter(first_name='ke', last_name='shou')

 

 

>>> Person.objects.filter(first_name='ke').exclude(last_name='shou')

 

说明:先过滤出first_name值为ke的,然后再从这里面筛选出last_name不为 shou的

 

当然,也可以拆分成多条语句

>>> q1 = Person.objects.filter(first_name='ke')

>>> q2 = q1.exclude(last_name='shou')

>>> print(q2)

 

注意:只有真正用到查询结果时,才会执行数据库操作,比如上述代码,只有执行最后一行,print(q2)时才会真正去数据库查询。

 

注意:exclude并不是filter的完全实现。如下:

SELECT * FROM `myapp_person`

显示:

id  first_name last_name

1   ke             shou

2   yu             lai

3   ke             lai

4   yu             laiyu

 

SELECT * FROM `myapp_book`

显示:

id  book_name  borrower_id

1   yueding    2

2   duzhe      1

3   zhazhi     2

4   shenghuo   3

5   qinglv      4

 

获取myapp_person表记录,排除在myapp_book表中存在外键引用,且对应记录book_name为yueding, id=1的。

>>> persons = Person.objects.exclude(book__book_name='yueding', book__id=1)

>>> for person in persons:

...     print(person.first_name, person.last_name, person.id)

...

ke shou 1

ke lai 3

yu laiyu 4

 

注意:

1、“反向查找”,条件左侧表达式填写格式:model名称__field名称

2、“正向查找”,不能按以上方式查找,比如以下,会报错:

>>> books = Book.objects.exclude(Person__first_name='ke', Person__last_name='shou')

 

反向查找和正向查找是我个人定义。定义不太好表达,具体参考以上例子。

 

如果结合filter方法来实现,则可采用“嵌套”的方式

>>> persons = Person.objects.exclude(book__in=Book.objects.filter(book_name='yueding', id=1))

>>> for person in persons:

...     print(person.first_name, person.last_name, person.id)

...

ke shou 1

ke lai 3

yu laiyu 4

>>> 

 

 

检索单个对象

例:

>>> 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/

 

posted @ 2020-08-02 20:52  授客  阅读(441)  评论(0编辑  收藏  举报