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/

Django model 层之Making Query总结的更多相关文章

  1. Django Model数据访问Making queries

    创建完Model之后, Django 自动为你提供一套数据库抽象层的API,利用它可以完成创建,提取,更新,删除对象的操作. 以下面的Model为例: class Blog(models.Model) ...

  2. django Model模型二及Model模型对数据库的操作

    在django模型中负责与数据库交互的为Model层,Model层提供了一个基于orm的交互框架 一:创建一个最基本的Model from __future__ import unicode_lite ...

  3. DjangoMTV模型之model层——ORM操作数据库(基本增删改查)

    Django的数据库相关操作 对象关系映射(英语:(Object Relational Mapping,简称ORM),是一种程序技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换.从效果上说 ...

  4. Django - 模型层 - 上

    一.ORM简介 MVC或者MVC框架中包括一个重要的部分,就是ORM,它实现了数据模型与数据库的解耦,即数据模型的设计不需要依赖于特定的数据库,通过简单的配置就可以轻松更换数据库,这极大的减轻了开发人 ...

  5. django模型层 关于单表的增删改查

    关于ORM MTV或者MVC框架中包括一个重要的部分,就是ORM,它实现了数据模型与数据库的解耦,即数据模型的设计不需要依赖于特定的数据库, 通过简单的配置就可以轻松更换数据库,这极大的减轻了开发人员 ...

  6. Django模型层(各种表及表数据的操作)

    目录 一.Django模型层 0. django模型层的级联关系 1. 配置django测试脚本 (1)方式一 (2)方式二 2. orm表数据的两种增删改 (1)方式一: (2)方式二: 3. pk ...

  7. day 70 Django基础五之django模型层(二)多表操作

    Django基础五之django模型层(二)多表操作   本节目录 一 创建模型 二 添加表记录 三 基于对象的跨表查询 四 基于双下划线的跨表查询 五 聚合查询.分组查询.F查询和Q查询 六 ORM ...

  8. day 56 Django基础五之django模型层(二)多表操作

    Django基础五之django模型层(二)多表操作   本节目录 一 创建模型 二 添加表记录 三 基于对象的跨表查询 四 基于双下划线的跨表查询 五 聚合查询.分组查询.F查询和Q查询 六 ORM ...

  9. Django model总结(上)

    Django model是django框架中处于比较核心的一个部位,准备分三个博客从不同的方面分别进行阐述,本文为<上篇>,主要对[a]Model的基本流程,比如它的创建,迁移等:默认行为 ...

  10. 【转】Django Model field reference学习总结

    Django Model field reference学习总结(一) 本文档包含所有字段选项(field options)的内部细节和Django已经提供的field types. Field 选项 ...

随机推荐

  1. nginx学习记录【二】nginx跟.net core结合,实现一个域名访问多个.net core应用

    1.实现转发 打开conf下的nginx.conf文件,如下图: 2.添加.net core网站的转发 按下面的进行修改,修改完后,就把localhost的80转发到了https://localhos ...

  2. kubernetes使用metrics-server进行资源监控

    kubernetes资源监控 1. 查看集群资源状况 ·k8s集群的master节点一般不会跑业务容器· kubectl get cs #查看master资源状态 kubectl get node # ...

  3. 前端项目报EISDIR: illegal operation on a directory, read这个错误

    背景: 我用webstorm开发前端页面时,项目用Vue3来开发,出现如下报错. 原因: 这个报错是由于代码中引入的一些组件或者模块路径不正确导致的,在vue2中,引入组件是下面这样写的: impor ...

  4. 算法金 | 再见,PCA 主成分分析!

    ​大侠幸会,在下全网同名[算法金] 0 基础转 AI 上岸,多个算法赛 Top [日更万日,让更多人享受智能乐趣] 1. 概念:数据降维的数学方法 定义 主成分分析(PCA)是一种统计方法,通过正交变 ...

  5. Java中try catch finally 关键字

    异常处理中的几个常用关键字(try catch finally throw throws) 异常处理 java中提供一套异常处理机制,在程序发生异常时,可以执行预先设定好的处理程序, 执行完成后,程序 ...

  6. 解决使用`npm install`或`npm i`命令之后报`Unexpected token in JSON at position`错误的问题

    网上大多数的教程都是以下几个步骤挨个试一遍,包括 stackoverflow 上也是这么说的 删除node_modules文件夹 删除package-lock.json文件 强制清除npm缓存 npm ...

  7. kettle从入门到精通 第五十九课 ETL之kettle 邮件发送多个附件,使用正则轻松解决

    问题场景: 一个朋友说他用kettle将生成好的多个文件(a.xls和b.xls,文件在data目录下)发送给客户,但是data目录下还有其他的文件,他如果指定data目录发送会把 data目录下面的 ...

  8. 微信实名认证申请单报错:请求中含有未在API文档中定义的参数

    完整错误: {"code":"PARAM_ERROR","detail":{"location":null," ...

  9. 基于SDF的光照效果

    基于SDF的光照效果 好久没写博客了,怠惰了,就当爬了一步 原神二次元风格面部渲染 效果 Show me the code Shader "Unlit/SDF" { Propert ...

  10. mapperTemp

    @Insert("INSERT INTO coxisolate.instanceinfo (instance_id, app_name, create_time, update_time, ...