Making Qeries

一旦创建了数据模型,Django就会自动为您提供一个数据库抽象API,允许您创建、检索、更新和删除对象。本文档解释了如何使用这个API。

The models

一个class代表一个数据库中的table

from django.db import models

class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField() def __str__(self):
return self.name class Author(models.Model):
name = models.CharField(max_length=200)
email = models.EmailField() def __str__(self):
return self.name class Entry(models.Model):
blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
headline = models.CharField(max_length=255)
body_text = models.TextField()
pub_date = models.DateField()
mod_date = models.DateField()
authors = models.ManyToManyField(Author)
n_comments = models.IntegerField()
n_pingbacks = models.IntegerField()
rating = models.IntegerField() def __str__(self):
return self.headline

Creating objects

insert a record in table

>>> from blog.models import Blog
>>> b = Blog(name='Beatles Blog', tagline='All the latest Beatles news.')
>>> b.save()

Saving changes to objects

update a record in table

>>> b5.name = 'New name'
>>> b5.save()

Saving ForeignKey and ManyToManyField fields

保存一对多关系和多对多关系

# The ForeignKey
>>> from blog.models import Blog, Entry
>>> entry = Entry.objects.get(pk=1)
>>> cheese_blog = Blog.objects.get(name="Cheddar Talk")
>>> entry.blog = cheese_blog
>>> entry.save()
# The ManyToManyField
>>> from blog.models import Author
>>> joe = Author.objects.create(name="Joe")
>>> entry.authors.add(joe,...)

Retrieving objects

检查对象请通过模型类上的Manager构造一个QuerySet。

每个模型至少拥有一个Manager管理器,默认为objects,可以通过类来获取它。

>>> Blog.objects
<django.db.models.manager.Manager object at ...>
>>> b = Blog(name='Foo', tagline='Bar')
>>> b.objects
Traceback:
...
AttributeError: "Manager isn't accessible via Blog instances."

注:管理器只能通过模型类访问,而不是从模型实例访问,以强制“表级”操作和“记录级”操作之间的分离

       管理器是模型的查询集的主要来源。

Retrieving all objects

获取所有对象

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

Retrieving specific objects with filters

获取过滤后的对象

Entry.objects.filter(pub_date__year=2006)
# is the same as
Entry.objects.all().filter(pub_date__year=2006)

Chaining filters

提炼一个QuerySet的结果本身就是一个QuerySet,所以可以将提炼链接在一起。

>>> Entry.objects.filter(
... headline__startswith='What'
... ).exclude(
... pub_date__gte=datetime.date.today()
... ).filter(
... pub_date__gte=datetime.date(2005, 1, 30)
... )

Filtered QuerySets are unique

每次提炼一次QuerySet,就会得到一个全新的QeurySet,与之前的QeurySet没有任何联系

QuerySets are lazy

提炼QuerySet是惰性查询,本身不涉及到任何数据库查询,所以可以随意拼接它

Retrieving a single object with get()

filter()将得到一个QuerySet,如果您想得到一个单独的实体对象 可以使用get()

>>> one_entry = Entry.objects.get(pk=1)

Retrieving QuerySets with exclude

排除条件

Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3), headline='Hello')

# transform SQL
SELECT ... WHERE NOT (pub_date > '2005-1-3' AND headline = 'Hello')

Limiting QuerySets

截取QuerySet,与python list类似 不支持使用负数

>>> Entry.objects.all()[:5]

>>> Entry.objects.all()[5:10]

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

to get a single object

>>> Entry.objects.order_by('headline')[0]
# is the same as
>>> Entry.objects.order_by('headline')[0:1].get()

Field lookups

字段查找是指定SQL WHERE子句的核心内容的方式。它们被指定为QuerySet方法filter()、exclude()和get()的关键字参数。

基本查找关键字参数的形式为field__lookuptype=value。(这是一个双下划线)。

__lte

>>> Entry.objects.filter(pub_date__lte='2006-01-01')
# transform SQL
SELECT * FROM blog_entry WHERE pub_date <= '2006-01-01'

_id

查找中指定的字段必须是模型字段的名称。但是有一个例外,对于ForeignKey,您可以指定以_id为后缀的字段名。在这种情况下,值参数应该包含外部模型主键的原始值。

>>> Entry.objects.filter(blog_id=4)

exact 默认的,可以忽略

>>> Blog.objects.get(id__exact=14)
# is the same as
>>> Blog.objects.get(id=14)
# transform SQL
SELECT ... WHERE id = 14;

iexact 与exact类似,不区分大小写

contains

Entry.objects.get(headline__contains='Lennon')
# transform SQL
SELECT ... WHERE headline LIKE '%Lennon%';

Lookups that span relationships

跨域查找

要跨越关系,只需跨模型使用相关字段的字段名(用双下划线分隔),直到到达您想要的字段为止

>>> Entry.objects.filter(blog__name='Beatles Blog')

它也可以反向工作。要引用“反向”关系,只需使用模型的小写名称。

>>> Blog.objects.filter(entry__headline__contains='Lennon')

spanning multi-value relationships

跨越多值关系

Blog.objects.filter(entry__headline__contains='Lennon', entry__pub_date__year=2008)
# is the same as
Blog.objects.filter(entry__headline__contains='Lennon').filter(entry__pub_date__year=2008)

Filters can reference fields on the model

过滤器可以引用模型上的字段 F表达式

>>> from django.db.models import F
>>> Entry.objects.filter(n_comments__gt=F('n_pingbacks')) >>> Entry.objects.filter(n_comments__gt=F('n_pingbacks') * 2) >>> Entry.objects.filter(rating__lt=F('n_comments') + F('n_pingbacks')) >>> Entry.objects.filter(authors__name=F('blog__name')) #跨域 >>> from datetime import timedelta
>>> Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3))

The pk lookup shortcut

为了方便起见,Django提供了一个pk查找快捷方式,它表示“主键”。

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

Caching and QuerySets

每个QuerySet包含一个缓存,以最小化数据库访问。了解它的工作原理将使您能够编写最高效的代码。

在新创建的QuerySet中,缓存是空的。当第一次计算QuerySet时(因此发生了数据库查询),Django将查询结果保存在QuerySet的缓存中,并返回显式请求的结果(例如,下一个元素,如果迭代QuerySet的话)。QuerySet的后续评估重用缓存的结果。

请记住这种缓存行为,因为如果您没有正确使用queryset,它可能会影响您。例如,下面将创建两个查询集,计算它们,并将它们丢弃:

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

When QuerySets are not cached

查询集并不总是缓存它们的结果。当只计算queryset的一部分时,会检查缓存,但是如果它没有被填充,那么后续查询返回的项就不会被缓存。具体来说,这意味着使用数组切片或索引限制queryset不会填充缓存。

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

other methods to populate the cache

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

简单地打印queryset不会填充缓存。这是因为对__repr__()的调用只返回整个queryset的一部分。

Complex lookups with Q objects

复杂查询使用Q方法

Q对象(django.db.models.Q)是用来封装关键字参数集合的对象。这些关键字参数在上面的“字段查找”中指定。

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

Q对象可以使用&和|运算符组合。当对两个Q对象使用运算符时,它会生成一个新的Q对象。

例如,该语句生成一个Q对象,该对象表示两个“question__startswith”查询的“或”

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

通过将Q对象与&和|运算符组合起来并使用括号分组,您可以组合任意复杂的语句。同样,可以使用~操作符来否定Q对象,允许组合查找,将普通查询和否定(非)查询结合起来

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

每个获取关键字参数的查找函数(例如filter()、exclude()、get())也可以作为位置(非命名)参数传递一个或多个Q对象。如果您为查找函数提供多个Q对象参数,这些参数将“AND”ed一起使用。例如:

Poll.objects.get(
Q(question__startswith='Who'),
Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
)
# transform SQL
SELECT * from polls WHERE question LIKE 'Who%'
AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06')

查找函数可以混合使用Q对象和关键字参数。为查找函数提供的所有参数(无论是关键字参数还是Q对象)都是“ed”在一起的。但是,如果提供了Q对象,那么它必须先于任何关键字参数的定义。

# INVALID QUERY. Q() is must be before keyword arguments
Poll.objects.get(
question__startswith='Who',
Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
)

Comparing objects

要比较两个模型实例,只需使用标准的Python比较运算符:==。在幕后,比较两个模型的主要关键值。

>>> some_entry == other_entry
>>> some_entry.id == other_entry.id

Deleting objects

>>> e.delete()

>>> Entry.objects.filter(pub_date__year=2005).delete()
(5, {'webapp.Entry': 5})

当Django删除一个对象时,默认情况下它会模拟SQL约束在DELETE CASCADE上的行为——换句话说,任何有外键指向要删除的对象的对象都将被删除。这个级联行为可以通过对ForeignKey的on_delete参数进行定制。

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

注意,delete()是唯一没有在管理器本身上公开的QuerySet方法。这是一种安全机制,可以防止您意外地请求Entry.objects.delete()和删除所有条目。如果您确实想删除所有对象,那么您必须显式地请求完整的查询集:

Entry.objects.all().delete()

Copying model instances

blog = Blog(name='My blog', tagline='Blogging is easy')
blog.save() # blog.pk == 1 blog.pk = None
blog.save() # blog.pk == 2

如果使用继承,事情会变得更加复杂。考虑博客的一个子类

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
django_blog.pk = None
django_blog.id = None
django_blog.save() # django_blog.pk == 4

这个过程不会复制不属于模型数据库表的关系。例如,Entry有很多个要编写的字段。复制条目后,必须为新Entry设置多对多关系:

entry = Entry.objects.all()[0] # some previous entry
old_authors = entry.authors.all()
entry.pk = None
entry.save()
entry.authors.set(old_authors)

对于一个OneToOneField,必须复制相关对象并将其分配给新对象的字段,以避免违反一对一唯一约束。例如,假设Entry已经复制到上面:

detail = EntryDetail.objects.all()[0]
detail.pk = None
detail.entry = entry
detail.save()

Updating multiple objects at once

有时,您希望为QuerySet中的所有对象将字段设置为特定值。您可以使用update()方法来做到这一点。

# Update all the headlines with pub_date in 2007.
Entry.objects.filter(pub_date__year=2007).update(headline='Everything is the same')
>>> Entry.objects.all().update(n_pingbacks=F('n_pingbacks') + 1)

注:update操作中的F()不能进行join连接 否则会报错FieldError

Related objects

当建立了一个关系在模型中(ForeignKey,OneToOneField,ManyToManyField),该模型的实例将具有方便的API来访问相关对象。

>>> e = Entry.objects.get(id=2)
>>> e.blog # Returns the related Blog object.
>>> e = Entry.objects.get(id=2)
>>> e.blog = some_blog
>>> e.save()
>>> e = Entry.objects.get(id=2)
>>> e.blog = None
>>> e.save() # "UPDATE blog_entry SET blog_id = NULL ...;"

Following relationships “backward”

如果模型有一个ForeignKey,那么外键模型的实例将访问返回第一个模型的所有实例的管理器。默认情况下,该管理器名为FOO_set,其中FOO是源模型名,小写。此管理器返回QuerySets,可以按照上面“检索对象”一节的描述对其进行过滤和操作。

>>> b = Blog.objects.get(id=1)
>>> b.entry_set.all() # Returns all Entry objects related to Blog. # b.entry_set is a Manager that returns QuerySets.
>>> b.entry_set.filter(headline__contains='Lennon')
>>> b.entry_set.count()

通过在ForeignKey定义中设置related_name参数,可以覆盖FOO_set名称。

Using a custom reverse manager

默认情况下,用于反向关系的RelatedManager是该模型的默认管理器的子类。如果您想为给定的查询指定不同的管理器,可以使用以下语法

from django.db import models

class Entry(models.Model):
#...
objects = models.Manager() # Default Manager
entries = EntryManager() # Custom Manager b = Blog.objects.get(id=1)
b.entry_set(manager='entries').all()

如果EntryManager在其get_queryset()方法中执行默认过滤,则该过滤将应用于all()调用。

当然,指定一个定制的反向管理器还可以调用它的定制方法:

b.entry_set(manager='entries').is_published()

Additional methods to handle related objects

add(obj1, obj2, ...) 添加模型实例

create(**kwargs) 创建一个新的模型对象

remove(obj1, obj2, ...) 从相关对象集中删除指定的模型对象

clear() 清空所有对象

set(objs) 替换相关对象集。

Many-to-many relationships

多对多关系的两端都可以自动访问另一端的API。该API的工作方式类似于上面的“向后”一对多关系。

一个区别是属性命名:定义ManyToManyField的模型使用的是该字段本身的属性名,而“反转”模型使用的是原始模型的小写模型名加上“_set”(就像反向一对多关系一样)。

e = Entry.objects.get(id=3)
e.authors.all() # Returns all Author objects for this Entry.
e.authors.count()
e.authors.filter(name__contains='John') a = Author.objects.get(id=5)
a.entry_set.all() # Returns all Entry objects for this Author.

与ForeignKey一样,ManyToManyField可以指定related_name。在上面的例子中,如果条目中的ManyToManyField指定了related_name='entries',那么每个Author实例都有一个entries属性,而不是entry_set。

与一对多关系的另一个区别是,除了模型实例之外,多对多关系上的add()、set()和remove()方法还接受主键值。例如,如果e1和e2是条目实例,那么这些set()调用的工作方式相同:

a = Author.objects.get(id=5)
a.entry_set.set([e1, e2])
a.entry_set.set([e1.pk, e2.pk])

One-to-one relationships

一对一关系非常类似于多对一关系。如果您在模型上定义了一个OneToOneField,该模型的实例将通过模型的简单属性访问相关对象。

class EntryDetail(models.Model):
entry = models.OneToOneField(Entry, on_delete=models.CASCADE)
details = models.TextField() ed = EntryDetail.objects.get(id=2)
ed.entry # Returns the related Entry object.

区别在于“反向”查询。一对一关系中的相关模型也可以访问Manager对象,但该管理器表示单个对象,而不是对象的集合:

e = Entry.objects.get(id=2)
e.entrydetail # returns the related EntryDetail object

如果没有给这个关系分配对象,Django将会引发一个DoesNotExist异常。

实例可以以与分配正向关系相同的方式分配反向关系

e.entrydetail = ed

Performing raw SQL queries

class Person(models.Model):
first_name = models.CharField(...)
last_name = models.CharField(...)
birth_date = models.DateField(...) >>> for p in Person.objects.raw('SELECT * FROM myapp_person'):
... print(p)
John Smith
Jane Jones

raw()自动将查询中的字段映射到模型中的字段。与查询字段的顺序无关,下列的例子相同

>>> Person.objects.raw('SELECT id, first_name, last_name, birth_date FROM myapp_person')
...
>>> Person.objects.raw('SELECT last_name, birth_date, first_name, id FROM myapp_person')
...

匹配是通过名称来完成的。这意味着您可以使用SQL AS子句将查询中的字段映射到建模字段。所以如果你有另外一个表里面有Person数据,你可以很容易地把它映射到Person实例

>>> Person.objects.raw('''SELECT first AS first_name,
... last AS last_name,
... bd AS birth_date,
... pk AS id,
... FROM some_other_table''')

raw()支持索引,所以如果你只需要第一个结果,你可以写:

>>> first_person = Person.objects.raw('SELECT * FROM myapp_person')[0]

然而,索引和切片并不是在数据库级别执行的。如果您的数据库中有大量Person对象,那么在SQL级别上限制查询会更有效:

>>> first_person = Person.objects.raw('SELECT * FROM myapp_person LIMIT 1')[0]

# Deferring model fields

这个查询返回的Person对象将是deferred model实例(参见defer())。这意味着查询中省略的字段将根据需要加载。例如

您还可以执行包含模型中未定义的字段的查询。例如,我们可以使用PostgreSQL 's age()函数得到数据库计算出的年龄人员列表:

>>> people = Person.objects.raw('SELECT *, age(birth_date) AS age FROM myapp_person')
>>> for p in people:
... print("%s is %s." % (p.first_name, p.age))
John is 37.
Jane is 42.

#Passing parameters into raw()

>>> lname = 'Doe'
>>> Person.objects.raw('SELECT * FROM myapp_person WHERE last_name = %s', [lname])

    * 不要对原始查询使用字符串格式,也不要在SQL字符串中引用占位符!这有sql注入风险

Executing custom SQL directly

有时候,甚至Manager.raw()也不够:您可能需要执行不能清晰映射到模型的查询,或者直接执行更新、插入或删除查询。

在这些情况下,您总是可以直接访问数据库,完全围绕模型层路由。

对象django.db。连接表示默认的数据库连接。要使用数据库连接,请调用connection.cursor()来获得一个游标对象。然后,叫游标。执行(sql, [params])以执行sql和cursor.fetchone()或cursor.fetchall()以返回结果行。

如果您在MySQL上执行查询,请注意MySQL的静默类型强制可能会在混合类型时导致意外结果。如果查询字符串类型列,但使用整数值,MySQL将强制表中所有值的类型为整数,然后再执行比较。例如,如果表中包含值“abc”、“def”,并且查询mycolumn=0的位置,那么这两行都将匹配。为了防止这种情况,在使用查询中的值之前执行正确的类型转换。

Django Query的更多相关文章

  1. Django – query not equal

    The simpliest way to retrieve data from tables is take them all. To do this,  you can write: 1 all_e ...

  2. python3环境搭建(uWSGI+django+nginx+python+MySQL)

    1.系统环境,必要知识 #cat /etc/redhat-release CentOS Linux release (Core) #uname -r -.el7.x86_64 暂时关闭防护墙,关闭se ...

  3. Django调试models输出的SQL语句

    django1.3在shell下,调试models变得更为简单了,不用像之前的版本,手工去调用django query,才能打印出之前的代码是执行的什么SQL语句. 1.3开始只需在settings. ...

  4. Django 调试models 输出的SQL语句 定位查看结果

    django 调试models变得更为简单了,不用像之前的版本, 手工去调用django query, 才能打印出之前的代码是执行的什么SQL语句. 1.3开始只需在settings.py里,配置如下 ...

  5. python中的 __repr__和__str__

    __repr__,被内置函数repr用于把一个对象用"官方"的字符串形式表示出来(终端友好)    1.值传给eval()来返回一个对象的字符串表示形式    2.否则返回一个尖括 ...

  6. python代码的那些设计

    一.Django的ORM 1.类QuerySet (django) :QuerySet 可以被构造,过滤,切片,做为参数传递,这些行为都不会对数据库进行操作.只要你查询的时候才真正的操作数据库. 2. ...

  7. 第6模块 web框架口述题

    状态码如200 OK,以3位数字和原因 成.数字中的 一位指定了响应 别,后两位无分 .响应 别有以下5种. 重定向:客户端像服务器端发送请求,服务器告诉客户端你去重定向(状态码302,响应头loca ...

  8. 测试同学动手搭个简易web开发项目

    技术栈 node.js, vue.js, axios, python, django, orm, restful api, djangorestframework, mysql, nginx, jen ...

  9. get_or_create update_or_create

    django/query.py at master · django/django https://github.com/django/django/blob/master/django/db/mod ...

随机推荐

  1. java内存区域的分布

    读了<深入理解Java虚拟机>之后,当时理解了,过段时间又忘记了,在此做下记录,方便自我回顾,也希望能帮到想要学习虚拟机的同学. Java虚拟机在执行java程序时会把它所管理的内存分为5 ...

  2. 什么时候必须使用UI相机? 多个相机的作用原理?

    首先,要从主画布说起,maincanvas,这个有什么限制?主画布是一张默认用来绘制UI的地方,这些UI必须是系统提供的UI组件,在画面下挂一个3D物体或非UI的2D物品是不会被绘制到画布上的,但是仍 ...

  3. 在MyEclipse中用debug调试应用程序

    F5:单步测试,作用是跳入,比如说一大步中分为10小步,单击F5一次就会走完一小步,走完这一大步则需要单步10次.F6:与F5一样也是单步测试.只不过与F5不同的是F5追求的是过程,而F6追求的是结果 ...

  4. NormalMapping

    [NormalMapping] 法线贴图内的数据是法线,高度贴图内的数据是高度,不是一个东西.在ShaderLab中,UnpackNormal()分析的是法线贴图(注意不是高度贴图). 可以看到,在G ...

  5. 编码总结,以及对BOM的理解

    一.前言 在跨平台.跨操作系统或者跨区域之间,经常会涉及到编码的问题,因为前段时间在项目中,遇到了因为编码而产生乱码的问题,以前对编码也是一知半解,所以决定对编码有一个更为深入的了解,因此才有了这篇自 ...

  6. Java基础知识常识总结

    Java基础知识常识总结 1.面向对象的特征以及对他的理解? 封装,继承,多态,如果再增加一条就是抽象. 2.Object有几种方法,分别是什么? 在JDK1.5中的11种方法,分别有: toStri ...

  7. style多次设置行内样式

    语法 style="font-size:32px;background-color:#aaa"

  8. 浅谈利用PLSQL的多线程处理机制,加快处理大数据表的效率

    我们在处理大数据表的时候经常会感觉的处理速度不够快,效率不够高,那么今天下面我就来简单实现下PLSQL的多线程编程处理数据: 我模拟一个简单的场景,把某一张表中的数据(当然这张表的数据非常大)同步到目 ...

  9. [C++] c Struct VS c++ Struct

    c Struct c语言生命变量要加上struct c语言结构体内部不能有函数 C语言结构体没有共有,私有和继承

  10. 洛谷P1186 玛丽卡 spfa+删边

    洛谷P1186 玛丽卡http://blog.csdn.net/huihao123456/article/details/73414139题目描述 麦克找了个新女朋友,玛丽卡对他非常恼火并伺机报复. ...