创建对象
为了在Python对象中表示数据库表数据,Django使用直观的系统:模型类表示数据库表,该类的实例表示数据库表中的特定记录。
要创建对象,请使用模型类的关键字参数对其进行实例化,然后调用save()
以将其保存到数据库中。
>>> from blog.models import Blog>>> b = Blog(name='Beatles Blog', tagline='All the latest Beatles news.') >>> b.save() 要在单个步骤中创建和保存对象,请使用该 create()方法。 要保存对已存在于数据库中的对象的更改,请使用 save()。 更新ManyToManyField工作的方式略有不同 - 使用 add()字段上的方法向关系添加记录,而不是用save()。
检索对象
要从数据库中检索对象,请在模型类上构建一个 QuerySet
via a Manager
。
A QuerySet
表示数据库中的对象集合。它可以有零个,一个或多个过滤器。过滤器根据给定的参数缩小查询结果范围。在SQL术语中,a QuerySet
等于SELECT
语句,过滤器是限制子句,如WHERE
或LIMIT
。
从表中检索对象的最简单方法是获取所有这些对象。为此,请使用以下all()
方法
要创建此类子集,请优化初始 QuerySet
添加过滤条件。两种最常见的改进方法QuerySet
是:
- 返回
QuerySet
包含与给定查找参数 匹配的新对象。 - 返回
QuerySet
包含与给定查找参数 不 匹配的新对象。
filter(**kwargs)
exclude(**kwargs)
查找参数(**kwargs
在上面的函数定义中)应采用下面的字段查找中描述的格式。
例如,要获取QuerySet
2006年的博客条目,请使用filter()
如下:
Entry.objects.filter(pub_date__year=2006)
用检索单个对象get()
filter()
QuerySet
即使只有一个对象与查询匹配,它总会给你一个 - 在这种情况下,它将QuerySet
包含一个元素。
如果您知道只有一个对象与您的查询匹配,则可以使用直接返回对象的 get()
方法 Manager
:
>>> one_entry = Entry.objects.get(pk=1)
请注意,使用get()和使用 filter()切片之间存在差异 [0]。如果没有与查询匹配的结果, get()则会引发DoesNotExist 异常。
使用Python的数组切片语法的子集将您限制 QuerySet
为一定数量的结果。这相当于SQL LIMIT
和OFFSET
子句。
例如,这将返回前5个对象():LIMIT 5
>>> Entry.objects.order_by('headline')[0]
这大致相当于:
>>> Entry.objects.order_by('headline')[0:1].get()
>>> Entry.objects.all()[:5]
字段查找
字段查找是指定SQL WHERE
子句的内容的方式。它们被指定为QuerySet
方法的关键字参数filter()
, exclude()
并且 get()
。
基本查找关键字参数采用表单field__lookuptype=value
。(这是一个双重下划线)。例如:
>>> Entry.objects.filter(pub_date__lte='2006-01-01')
查找中指定的字段必须是模型字段的名称。但是有一个例外,如果ForeignKey
你可以指定后缀的字段名称_id
。在这种情况下,value参数应包含外部模型主键的原始值。例如:
>>> Entry.objects.filter(blog_id=4)
exact
“精确”匹配。例如:
>>> Entry.objects.get(headline__exact="Cat bites dog")
将沿这些行生成SQL:
SELECT ... WHERE headline = 'Cat bites dog';
如果您不提供查找类型 - 也就是说,如果您的关键字参数不包含双下划线 - 则假定查找类型为 exact
。
例如,以下两个语句是等效的:
>>> Blog.objects.get(id__exact=14) # Explicit form >>> Blog.objects.get(id=14) # __exact is implied
这是为了方便,因为exact
查找是常见的情况。
iexact
不区分大小写的匹配项。所以,查询:
>>> Blog.objects.get(name__iexact="beatles blog")
跨越关系的查找
Django提供了一种强大而直观的方式来“跟踪”查找中的关系JOIN
,在后台自动为您处理SQL 。要跨越关系,只需使用跨模型的相关字段的字段名称,用双下划线分隔,直到到达所需的字段。
这个例子检索所有Entry
与对象Blog
,其name
为:'Beatles Blog'
>>> Entry.objects.filter(blog__name='Beatles Blog')
可能令人困惑的情况是你正在使用它isnull
。从而:
Blog.objects.filter(entry__authors__name__isnull=True)
将返回Blog
有一个空的对象name
上author
,以及那些具有空author
的entry
。如果你不想要那些后面的对象,你可以写:
Blog.objects.filter(entry__authors__isnull=False, entry__authors__name__isnull=True)
要选择包含标题中包含“Lennon”和2008年发布(并且的关系)的条目的所有博客(满足这两个条件的相同条目),我们会写:
Blog.objects.filter(entry__headline__contains='Lennon', entry__pub_date__year=2008)
要选择标题 中包含“Lennon”条目的所有博客以及这些博客在 2008年发布的条目,我们会写:
Blog.objects.filter(entry__headline__contains='Lennon').filter(entry__pub_date__year=2008)
假设只有一个博客的两个条目都包含“Lennon”和2008年的条目,但是2008年的条目都没有包含“Lennon”。第一个查询不会返回任何博客,但第二个查询将返回该博客。
F()
要查找包含比pingback更多注释的所有博客条目的列表,我们构造一个F()
对象来引用pingback计数,并F()
在查询中使用该对象:
>>> from django.db.models import F>>> Entry.objects.filter(n_comments__gt=F('n_pingbacks'))
Django支持对对象使用加法,减法,乘法,除法,模运算和幂运算F()
,包括常量和其他F()
对象。要查找所有博客条目的评论数量是pingback的两倍以上 ,我们会修改查询:
>>> Entry.objects.filter(n_comments__gt=F('n_pingbacks') * 2)
要查找条目评级小于pingback计数和评论计数总和的所有条目,我们将发出查询:
>>> Entry.objects.filter(rating__lt=F('n_comments') + F('n_pingbacks'))
您还可以使用双下划线表示法来跨越F()
对象中的关系。F()
具有双下划线的对象将引入访问相关对象所需的任何连接。例如,要检索作者姓名与博客名称相同的所有条目,我们可以发出查询:
>>> Entry.objects.filter(authors__name=F('blog__name'))
pk
查找快捷方式
为方便起见,Django提供了一个pk
查找快捷方式,代表“主键”。
在示例Blog
模型中,主键是id
字段,因此这三个语句是等效的:
>>> 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
使用pk
不仅限于__exact
查询 - 可以组合任何查询术语以pk
对模型的主键执行查询:
# 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)
pk
查找也适用于连接。例如,这三个陈述是等价的:
>>> Entry.objects.filter(blog__id__exact=3) # Explicit form >>> Entry.objects.filter(blog__id=3) # __exact is implied >>> Entry.objects.filter(blog__pk=3) # __pk implies __id__exact
在LIKE
语句中转义百分号和下划线
要检索包含百分号的所有条目,只需将百分号用作任何其他字符:
>>> Entry.objects.filter(headline__contains='%')
Django负责为你报价; 生成的SQL看起来像这样:
SELECT ... WHERE headline LIKE '%\%%';
下划线也是如此。百分号和下划线都是透明处理的。
使用Q
对象进行复杂查找
关键字参数查询 - 输入filter()
等 - 是“AND”编辑在一起。如果需要执行更复杂的查询(例如,带OR
语句的查询),则可以使用。Q objects
A ()是用于封装关键字参数集合的对象。这些关键字参数在上面的“字段查找”中指定。Qobject
django.db.models.Q
例如,此Q
对象封装了一个LIKE
查询:
from django.db.models import QQ(question__startswith='What')
Q
可以使用&
和|
运算符组合对象。当在两个Q
对象上使用运算符时,它会生成一个新Q
对象。
例如,此语句生成一个Q
表示两个"question__startswith"
查询的“OR”的对象:
Q(question__startswith='Who') | Q(question__startswith='What')
这相当于以下SQL WHERE
子句:
WHERE question LIKE 'Who%' OR question LIKE 'What%'
您可以通过合并组成任意复杂的报表Q
与对象&
和|
运营商,并使用括号分组。此外,Q
可以使用~
运算符取消对象,允许组合查找同时结合普通查询和negated(NOT
)查询:
Q(question__startswith='Who') | ~Q(pub_date__year=2005)
每个查找函数,采用关键字参数(例如filter()
, exclude()
, get()
)也可以通过一个或多个 Q
对象作为位置(未命名的)参数。如果为Q
查找函数提供多个 对象参数,则参数将“AND”编辑在一起。例如:
Poll.objects.get( Q(question__startswith='Who'), Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)) )
查找函数可以混合使用Q
对象和关键字参数。提供给查找函数的所有参数(无论是关键字参数还是Q
对象)都是“AND”编辑在一起的。但是,如果Q
提供了对象,则它必须位于任何关键字参数的定义之前。例如:
Poll.objects.get( Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)), question__startswith='Who', )
比较对象
要比较两个模型实例,只需使用标准的Python比较运算符,即双等号:==
。在幕后,比较两个模型的主键值。
使用Entry
上面的示例,以下两个语句是等效的:
>>> some_entry == other_entry>>> some_entry.id == other_entry.id
如果没有调用模型的主键id
,没问题。比较将始终使用主键,无论它是什么。例如,如果调用模型的主键字段name
,则这两个语句是等效的:
>>> some_obj == other_obj>>> some_obj.name == other_obj.name
删除对象
方便地命名删除方法 delete()
。此方法立即删除对象并返回已删除的对象数以及包含每个对象类型的删除数的字典。例:
>>> e.delete()(1, {'weblog.Entry': 1})
您也可以批量删除对象。每个人 QuerySet
都有一个 delete()
方法,删除所有成员QuerySet
。
例如,这会删除2005年的所有Entry
对象pub_date
:
>>> Entry.objects.filter(pub_date__year=2005).delete() (5, {'webapp.Entry': 5})
一次更新多个对象
有时您希望将字段设置为a中所有对象的特定值QuerySet
。您可以使用该update()
方法执行此操作。例如:
# Update all the headlines with pub_date in 2007.Entry.objects.filter(pub_date__year=2007).update(headline='Everything is the same')
您只能ForeignKey
使用此方法设置非关系字段和字段。要更新非关系字段,请将新值作为常量提供。要更新ForeignKey
字段,请将新值设置为要指向的新模型实例。例如:
>>> b = Blog.objects.get(pk=1) # Change every Entry so that it belongs to this Blog. >>> Entry.objects.all().update(blog=b)
一对多关系
如果模型具有a ForeignKey
,则该模型的实例将通过模型的简单属性访问相关(外部)对象。
举例:
>>> e = Entry.objects.get(id=2) >>> e.blog # Returns the related Blog object.
您可以通过外键属性获取和设置。正如您所料,在您调用之前,外键的更改不会保存到数据库中save()
。例:
>>> e = Entry.objects.get(id=2) >>> e.blog = some_blog >>> e.save()
如果ForeignKey
字段已null=True
设置(即,它允许NULL
值),则可以指定None
删除关系。例:
>>> e = Entry.objects.get(id=2) >>> e.blog = None >>> e.save() # "UPDATE blog_entry SET blog_id = NULL ...;"
以下关系“向后”
如果模型具有a ForeignKey
,则外键模型的实例将具有对其Manager
返回第一模型的所有实例的访问权。默认情况下,这 Manager
是命名的FOO_set
,其中FOO
是源模型名称,小写。此Manager
返回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()
您可以FOO_set
通过related_name
在ForeignKey
定义中设置参数 来覆盖名称 。例如,如果Entry
模型被更改为,上面的示例代码将如下所示:blog = ForeignKey(Blog,on_delete=models.CASCADE, related_name='entries')
>>> b = Blog.objects.get(id=1) >>> b.entries.all() # Returns all Entry objects related to Blog. # b.entries is a Manager that returns QuerySets. >>> b.entries.filter(headline__contains='Lennon') >>> b.entries.count()
处理相关对象的其他方法
除了QuerySet
上面“检索对象”中定义的方法之外,还有用于处理相关对象集的其他方法。
- 将特定的模型对象加入关联对象集合。
- 创建一个新对象,保存它并将其放入相关的对象集中。返回新创建的对象。
- 从相关对象集中删除指定的模型对象。
- 从相关对象集中删除所有对象。
- 替换相关对象集。
add(obj1, obj2, ...)
create(**kwargs)
remove(obj1, obj2, ...)
clear()
set(objs)
要分配相关集的成员,请将该set()
方法与可迭代的对象实例一起使用。例如,if e1
和e2
是Entry
实例:
b = Blog.objects.get(id=1) b.entry_set.set([e1, e2])
如果该clear()
方法可用,则将从entry_set
迭代中的所有对象(在本例中为列表)添加到集合之前删除任何预先存在的对象。如果该clear()
方法不可 用,则将添加iterable中的所有对象,而不删除任何现有元素。
本节中描述的每个“反向”操作都会立即对数据库产生影响。每次添加,创建和删除都会立即自动保存到数据库中。
多对多关系
多对多关系的两端都可以自动访问另一端的API。API的工作方式类似于上面的“向后”一对多关系。
一个区别在于属性命名:定义的模型 ManyToManyField
使用该字段本身的属性名称,而“反向”模型使用原始模型的小写模型名称,加上'_set'
(就像反向一对多关系一样) 。同样的也可以用related_name覆盖FOO_set
一对一的关系
一对一关系与多对一关系非常相似。如果OneToOneField
在模型上定义a ,则该模型的实例将通过模型的简单属性访问相关对象。
例如:
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
对象,但它Manager
表示单个对象,而不是对象集合:
e = Entry.objects.get(id=2) e.entrydetail # returns the related EntryDetail object
如果没有为此关系分配任何对象,Django将引发DoesNotExist
异常。
可以使用与分配前向关系相同的方式将实例分配给反向关系:
e.entrydetail = ed