Django Query

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

当建立了一个关系在模型中(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()

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的位置,那么这两行都将匹配。为了防止这种情况,在使用查询中的值之前执行正确的类型转换。

原文地址:https://www.cnblogs.com/LTEF/p/9619473.html