django数据库查询总结

2017-02-07 16:23

博客应用数据库:

from django.db import models

class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()

    def __str__(self):              # __unicode__ on Python 2
        return self.name

class Author(models.Model):
    name = models.CharField(max_length=50)
    email = models.EmailField()

    def __str__(self):              # __unicode__ on Python 2
        return self.name

class Entry(models.Model):
    blog = models.ForeignKey(Blog)
    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):              # __unicode__ on Python 2
        return self.headline

创建对象(INSERT)

  • save() 对应的INSERT 数据,没有返回对象
>>> from blog.models import Blog
>>> b = Blog(name='Beatles Blog', tagline='All the latest Beatles news.')
>>> b.save()
  • create() 创建并保存一个对象
Blog.objects.create(name='Beatles Blog', tagline='All the latest Beatles news.')

等价下面
p = Blog.objects.create(name='Beatles Blog', tagline='All the latest Beatles news.')
p.save(force_insert=True)

更新数据(update)

假设Blog 的一个实例b5 已经被保存在数据库中,下面这个例子将更改它的name 并且更新数据库中的记录:

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

ForeignKey外键更新

from blog.models import Entry
entry = Entry.objects.get(pk=1)
cheese_blog = Blog.objects.get(name="Cheddar Talk")
entry.blog = cheese_blog
entry.save()

ManyToManyField字段,多数据级联更新

  • 需要使用字段的add()方法来增加关联关系的一条记录。
from blog.models import Author
joe = Author.objects.create(name="Joe")
entry.authors.add(joe)
  • 看看更新多条记录:
john = Author.objects.create(name="John")
 paul = Author.objects.create(name="Paul")
george = Author.objects.create(name="George")
ringo = Author.objects.create(name="Ringo")
entry.authors.add(john, paul, george, ringo)

获取对象 select

你可以从模型的管理器那里取得查询集。每个模型都至少有一个管理器,它默认命名为objects。通过模型类来直接访问它,你也可以自定义一个管理器,如:

from django.db import models
class Person(models.Model):
    #...
    people = models.Manager()

使用例子中的模型, Person.objects会抛出AttributeError异常,而Person.people.all()会返回一个包含所有Person对象的列表。

  • 用默认管理器获取对象
Blog.objects 
b = Blog(name='Foo', tagline='Bar')
b.objects  # 这样会报错

管理器只可以通过模型的类访问,而不可以通过模型的实例访问,目的是为了强制区分“表级别”的操作和“记录级别”的操作

  • 获取所有对象all_entries = Entry.objects.all()

  • 通过get 获取一个单一的对象

one_entry = Entry.objects.get(pk=1

还可以使用all()、 get()、filter() 和exclude()。等方法查询

使用过滤器获取特定对象

  • all() 方法返回了一个包含数据库表中所有记录查询集。但在通常情况下,你往往想要获取的是完整数据集的一个子集。
  • filter(**kwargs)
    返回一个新的查询集,它包含满足查询参数的对象。

  • exclude(**kwargs)
    返回一个新的查询集,它包含不满足查询参数的对象。

例子

  • 要获取年份为2006的所有文章的查询集:
Entry.objects.filter(pub_date__year=2006)

利用默认的管理器,它相当于:

Entry.objects.all().filter(pub_date__year=2006)
  • 链式过滤 对应于WHERE 查询集的筛选结果本身还是查询集,所以可以将筛选语句链接在一起。像下面的例子,最开始获取数据库中所有对象的一个查询集,之后增加一个过滤器,然后又增加一个排除,再之后又是另外一个过滤器。最后的结果仍然是一个查询集,它包含标题以”What“开头、发布日期在2005年1月30日至当天之间的所有记录:
Entry.objects.filter(headline__startswith='What').exclude(pub_date__gte=datetime.date.today()).filter(pub_date__gte=datetime(2005, 1, 30))
  • 过滤后的查询是独立

    这三个查询集都是独立的。第一个是一个基础的查询集,包含所有标题以“What”开头的记录。第二个查询集是第一个的子集,它增加另外一个限制条件,排除pub_date 为今天和将来的记录。第三个查询集同样是第一个的子集,它增加另外一个限制条件,只选择pub_date 为今天或将来的记录。原始的查询集(q1)不会受到筛选过程的影响。

q1 = Entry.objects.filter(headline__startswith="What")
q2 = q1.exclude(pub_date__gte=datetime.date.today())
q3 = q1.filter(pub_date__gte=datetime.date.today())

限制查询

  • 返回前面5 个对象(LIMIT 5):
Entry.objects.all()[:5]
  • 下面这条语句返回第6 至第10 个对象(OFFSET 5 LIMIT 5):
Entry.objects.all()[5:10]
  • 下面的语句返回数据库中根据标题排序后的第一条Entry
Entry.objects.order_by('headline')[0]

字段查询 对应于where

上面说的它们通过查询集方法filter()、exclude() 和 get() 的关键字参数指定。

  • exact 精确查找
Entry.objects.get(headline__exact="Man bites dog")
Blog.objects.get(id__exact=14)  
Blog.objects.get(id=14) 这个和上面的相等
  • iexact 大小写不敏感
Blog.objects.get(name__iexact="beatles blog")
  • contains 大小写敏感的包含关系测试
Entry.objects.get(headline__contains='Lennon')

当然也可以使用icontains,大小写不敏感

  • startswith, endswith 以某某开头,某某结尾,当然还有大小写不敏感的版本,叫做istartswithiendswith

跨关联关系的查询

Django 提供一种强大而又直观的方式来“处理”查询中的关联关系,它在后台自动帮你处理JOIN。 若要跨越关联关系,只需使用关联的模型字段的名称,并使用双下划线分隔,直至你想要的字段:
下面这个例子获取所有Blog 的name 为'Beatles Blog' 的Entry 对象:

Entry.objects.filter(blog__name='Beatles Blog')
  • 下面的示例获取所有的Blog 对象,它们至少有一个Entry 的headline 包含'Lennon':
Blog.objects.filter(entry__headline__contains='Lennon')
  • 返回的Blog 对象包括author name 为空的Blog对象,以及authorname不为空但author__name关联的entry __author 为空的对象。如果你不需要后者,你可以这样写:
Blog.objects.filter(entry__authors__isnull=False,
        entry__authors__name__isnull=True)

Filter 可以引用模型的字段

  • F() 将模型的一个字段与同一个模型的另外一个字段进行比较
  • 为了查找comments 数目多于pingbacks 的Entry,我们将构造一个F() 对象来引用pingback 数目,并在查询中使用该F() 对象
from django.db.models import F
 Entry.objects.filter(n_comments__gt=F('n_pingbacks)
  • Django 支持对F() 对象使用加法、减法、乘法、除法、取模以及幂计算等算术操作,两个操作数可以都是常数和其它F() 对象。为了查找comments 数目比pingbacks 两倍还要多的Entry,我们将查询修改为:
Entry.objects.filter(n_comments__gt=F('n_pingbacks') * 2)
  • 为了查询rating 比pingback 和comment 数目总和要小的Entry,我们将这样查询:
Entry.objects.filter(rating__lt=F('n_comments') + F('n_pingbacks'))
  • 你还可以在F() 对象中使用双下划线标记来跨越关联关,如要获取author 的名字与blog 名字相同的Entry,我们可以这样查询
Entry.objects.filter(authors__name=F('blog__name'))
  • 对于date 和date/time 字段,你可以给它们加上或减去一个timedelta 对象。下面的例子将返回发布超过3天后被修改的所有Entry
from datetime import timedelta Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3))
  • 查询的快捷方式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
  • pk 的使用不仅限于__exact 查询 —— 任何查询类型都可以与pk 结合来完成一个模型上对主键的查询
Blog.objects.filter(pk__in=[1,4,7])
Blog.objects.filter(pk__gt=14)
  • pk查询在join 中也可以工作。例如,下面三个语句是等同的:
Entry.objects.filter(blog__id__exact=3) 
Entry.objects.filter(blog__id=3)       
Entry.objects.filter(blog__pk=3)    

使用Q 对象进行复杂的查询

  • Q 对象 (django.db.models.Q) 对象用于封装一组关键字参数,Q 对象可以使用& 和| 操作符组合起来
  • 下面的Q 对象封装一个LIKE 查询:
from django.db.models import Q
Q(question__startswith='What')
  • 下面的语句产生一个Q 对象,表示两个"question__startswith" 查询的“OR” :
Q(question__startswith='Who') | Q(question__startswith='What')
它等同于下面的SQL WHERE 子句:
WHERE question LIKE 'Who%' OR question LIKE 'What%'
  • 可以组合& 和| 操作符查询
Q(question__startswith='Who') | ~Q(pub_date__year=2005)
  • 如果一个查询函数有多个Q 对象参数,这些参数的逻辑关系为“AND"
Poll.objects.get(
    Q(question__startswith='Who'),
    Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
)

等同于:

SELECT * from polls WHERE question LIKE 'Who%'
    AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06')
  • 查询函数可以混合使用Q 对象和关键字参数。所有提供给查询函数的参数(关键字参数或Q 对象)都将"AND”在一起。但是,如果出现Q 对象,它必须位于所有关键字参数的前面。例如
Poll.objects.get(Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)),question__startswith='Who')

下面的例子就是错误的:

Poll.objects.get(question__startswith='Who',Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)))

删除对象

  • 删除方法,为了方便,就取名为delete()。这个方法将立即删除对象且没有返回值。例如: e.delete()
  • 下面的语句批量删除pub_date 为2005 的所有Entry 对象
Entry.objects.filter(pub_date__year=2005).delete()
  • 有外键的删除
b = Blog.objects.get(pk=1)
# This will delete the Blog and all of its Entry objects.
b.delete()
  • 注意,delete() 是唯一没有在管理器 上暴露出来的查询集方法。这是一个安全机制来防止你意外地请求Entry.objects.delete(),而删除所有 的条目。如果你确实想删除所有的对象,你必须明确地请求一个完全的查询集:
Entry.objects.all().delete()

拷贝模型实例

  • 虽然没有内建的方法用于拷贝模型实例,但还是很容易创建一个新的实例并让它的所有字段都拷贝过来。最简单的方法是,只需要将pk 设置为None。利用我们的Blog 示例:
blog = Blog(name='My blog', tagline='Blogging is easy')
blog.save() # blog.pk == 1

blog.pk = None
blog.save() # blog.pk == 2
  • 如果你用继承,那么会复杂一些。考虑下面Blog 的子类:
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

由于继承的工作方式,你必须设置pk 和 id 都为None:

django_blog.pk = None
django_blog.id = None
django_blog.save() # django_blog.pk == 4

这个过程不会拷贝关联的对象。如果你想拷贝关联关系,你必须编写一些更多的代码。在我们的例子中,Entry 有一个到Author 的多对多字段:

entry = Entry.objects.all()[0] # some previous entry
old_authors = entry.authors.all()
entry.pk = None
entry.save()
entry.authors = old_authors # saves new many2many relations

一次更新多个对象

  • 有时你想为一个查询集中所有对象的某个字段都设置一个特定的值。这时你可以使用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)
  • 对update 的调用也可以使用F 表达式 来根据模型中的一个字段更新另外一个字段
Entry.objects.all().update(n_pingbacks=F('n_pingbacks') + 1)

然而,与filter 和exclude 子句中的F() 对象不同,在update 中你不可以使用F() 对象引入join —— 你只可以引用正在更新的模型的字段。如果你尝试使用F() 对象引入一个join,将引发一个FieldError:

# THIS WILL RAISE A FieldError
 Entry.objects.update(headline=F('blog__name'))

关联的对象

  • 当你在一个模型中定义一个关联关系时(例如,ForeignKey、 OneToOneField 或ManyToManyField),该模型的实例将带有一个方便的API 来访问关联的对象。 利用本页顶部的模型,一个Entry 对象e 可以通过blog 属性e.blog 获取关联的Blog 对象。Blog 对象b 可以通过entry_set 属性 b.entry_set.all()访问与它关联的所有Entry 对象。

一对多关系

前向查询

  • 如果一个模型具有ForeignKey,那么该模型的实例将可以通过属性访问关联的(外部)对象
 e = Entry.objects.get(id=2)
 e.blog # Returns the related Blog object.
  • 如果ForeignKey 字段有null=True 设置(即它允许NULL 值),你可以分配None 来删除对应的关联性
>>> e = Entry.objects.get(id=2)
>>> e.blog = None
>>> e.save() # "UPDATE blog_entry SET blog_id = NULL ...;"
  • 注意select_related() 查询集方法递归地预填充所有的一对多关系到缓存中。例如
>>> e = Entry.objects.select_related().get(id=2)
>>> print(e.blog)  # Doesn't hit the database; uses cached version.
>>> print(e.blog)  # Doesn't hit the database; uses cached version.

反向查询

  • 如果模型有一个ForeignKey,那么该ForeignKey 所指的模型实例可以通过一个管理器返回前一个模型的所有实例。默认情况下,这个管理器的名字为foo_set,其中foo 是源模型的小写名称:
>>> 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 的名称。例如,如果Entry 模型改成blog = ForeignKey(Blog, 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()

多对多关系¶

多对多关系的两端都会自动获得访问另一端的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。在上面的例子中,如果Entry 中的ManyToManyField 指定related_name='entries',那么Author 实例将使用 entries 属性而不是entry_set。

一对一关系¶

一对一关系与多对一关系非常相似。如果你在模型中定义一个OneToOneField,该模型的实例将可以通过该模型的一个简单属性访问关联的模型。

例如:

class EntryDetail(models.Model):
    entry = models.OneToOneField(Entry)
    details = models.TextField()

ed = EntryDetail.objects.get(id=2)
ed.entry # Returns the related Entry object.

在“反向”查询中有所不同。一对一关系中的关联模型同样具有一个管理器对象,但是该管理器表示一个单一的对象而不是对象的集合:

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

如果没有对象赋值给这个关联关系,Django 将引发一个DoesNotExist 异常。

实例可以赋值给反向的关联关系,方法和正向的关联关系一样:

e.entrydetail = ed

通过关联的对象进行查询¶

在关联对象字段上的查询与正常字段的查询遵循同样的规则。当你指定查询需要匹配的一个值时,你可以使用一个对象实例或者对象的主键的值。

例如,如果你有一个id=5 的Blog 对象b,下面的三个查询将是完全一样的:

Entry.objects.filter(blog=b) # Query using object instance
Entry.objects.filter(blog=b.id) # Query using id from instance
Entry.objects.filter(blog=5) # Query using id directly


  • Rachael G. Richardson 2019-12-17 22:51:52

    Invitation - invite you to fill out paid surveys and earn cash

    Hello,

    Do you lack an extra stable income outside of your business or job?

    Thelocation3 provides an opportunity for you to earn extra cash.
    Join our paid survey site, and you can earn cash by sharing your opinion.

    Now take 5 minutes to fill out a survey,
    and you will see that $ 3 or $ 5 is credited to your account: https://www.thelocation3.com/paidsurvey

    Costs Nothing To Join.

    Best Regards,
    Rachael G. Richardson


    Company: Thelocation3
    Address: 210-2 Horace Harding Expy, Bayside, NY 11364, United States

    Unsubscribe: https://www.thelocation3.com/unsubscribe

  • Concepcion Stimson 2020-02-11 05:52:08

    Invite you to review our services

    Hello,

    I'm is Stimson from Centtip.
    Centtip cooperates with Automattic, Google adsense, etc., provides them with data that webmasters' opinions on their product.

    I am contacting you because we are looking for webmaster/blogger/business owner like you to test our service.

    The companies spend billions of dollars each year to obtain survey data.
    Centtip pays each member *** dollars daily for their opinions.

    If you are interested, please visit: https://centtip.com/cashsurvey/

    Thank you for your time,

    Concepcion Stimson

    ^^^^^^^^^^^^
    Unsubscribe: https://www.centtip.com/unsubscribe/