首页 > 其他分享 >Django学习小记[3] —— Query

Django学习小记[3] —— Query

时间:2023-08-27 10:36:19浏览次数:40  
标签:models QuerySet Django filter objects Entry Query Model 小记


今天学习的是Django的Model Query,前一篇已经学习过Model了,讲述的主要是Django中是如何处理关系型数据的模型的,一对一,多对一,多对多等,这篇则主要是描述的查询,能够将数据存进去,还得取出来,Django给每一个Model自动提供了丰富的查询接口,而且能够进行关联查询,基本上,能够满足绝大多数的查询需求。

在Django的文档中,有一句话说的非常好:

A model class represents a database table, and an instance of that class represents a particular record in the database table.

Model类的实例代表的是数据库中的记录,我们在进行查询的时候,得到结果一般都是一个数据库记录集合,这对应到Django里,就是QuerySet,Django提供的查询接口全都封装在QuerySet中,比如filter(), order_by()等,这对应到SQL语句,就是SELECT语句。这里我们只对经常用到的QuerySet方法进行简单描述,起到一个勾起回忆的作用,至于更多的内容还请参考QuerySet详细的API:QuerySet API

先构造几个有关联关系的Model:

from django.db import models

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

    def __unicode__(self):
        return self.name


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

    def __unicode__(self):
        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 __unicode__(self):
        return self.headline


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

这几个Model中,Blog和Entry是一对多的关系,Entry和Author是多对多的关系,Entry和EntryDetail是一对一的关系,分别通过ForeignKey, ManyToManyField, OneToOneField进行关联。现在我们来看看怎么对这些进行查询。

1. 首先来看最简单的,all(),一次获取所有的数据库记录:
>>> all_entries = Entry.objects.all()

objects是什么呢?每一个Model都有一个Manager对象,Manager也是Model的查询接口,那它和QuerySet之间是什么关系呢?其实Manager可以看成是QuerySet的代理类,最开始的QuerySet对象就是通过Manager类来得到的,objects就是Manager对象在Model类中的属性名。至于为什么有Manager类,为什么QuerySet需要代理,我现在还不是很清楚,这些需要看Django的源码才能知道,现在我们先知道怎么用就可以了。

调用objects.all()方法,得到就是一个QuerySet对象,如:

>>> type(Entry.objects.all())
<class 'django.db.models.query.QuerySet'>
>>> Entry.objects.all()
[<Entry: headline1>, <Entry: headline2>]
2. filters,过滤结果

即给Select语句加上Where查询条件,选出符合条件的子集,QuerySet中提供了两个方法:

  • filter(**kwargs)
    Returns a new QuerySet containing objects that match the given lookup parameters.
  • exclude(**kwargs)
    Returns a new QuerySet containing objects that do not match the given lookup parameters.

field__lookuptype=value这样的约束,field是Model的属性名,然后是两个下划线,然后是由Django定义的查询条件,比如exact, contains, startwith, gte, lt等,更多详细的内容,可以查看QuerySet API。下面给出两个例子:

>>> Blog.objects.filter(tagline__startswith="All")
[<Blog: Beatles>]
>>> Entry.objects.filter(pub_date__gte="2014-04-20")
[<Entry: headline1>, <Entry: headline2>]

需要注意的是,filter()和exclude()返回的结果仍然是一个QuerySet对象,所以,可以在一个语句中串联的使用filter,如:

>>> Entry.objects.filter(
...     headline__startswith='What'
... ).exclude(
...     pub_date__gte=datetime.date.today()
... ).filter(
...     pub_date__gte=datetime(2005, 1, 30)
... )
3. 接下来,来说一说QuerySet的一些特性
  • 首先,QuerySet是各自独立的,上面例子中,使用了3个串联的filter方法,每一个filter都会得到一个QuerySet,这3个QuerySet之间并没有什么直接的关系,相互之间也没有影响。
  • 其次,是QuerySet有一个很重要的特性就是延迟加载,并不是每一次调用filter()方法就会去查询一次数据库,只有在真正需要数据的时候才会去查询数据库,比如:
>>> q = Entry.objects.filter(pub_date__gte="2014-04-20")
>>> q = q.filter(mod_date__gte="2014-05-20")
>>> q
[<Entry: headline1>, <Entry: headline2>]
  • 然后,就是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])
>>> print([p.pub_date for p in queryset])
4. 直接得到一个Model对象:get()

filter()的返回结果总是得到一个QuerySet对象,QuerySet是Model对象集合,当你明确知道Model对象的集合只有一个时,可能就没必要用filter这么“麻烦”了,可以使用get()方法直接得到Model对象。如:

>>> one_entry = Entry.objects.get(pk=1)
>>> type(one_entry)
<class 'query_test.models.Entry'>

需要注意的是,当get()查询的结果没有,会抛出 DoesNotExist 异常,或者是有多个时,会抛出MultipleObjectsReturned 异常。

5. limit and offset

当我们在做分页时,会用到SQL的limit和offset特性,从哪开始读取多少个记录。那这个在Django里是怎么用的呢?很简单,就是直接使用python中array-slicing的语法就行,如:

>>> Blog.objects.all()
[<Blog: Beatles>, <Blog: blog2>, <Blog: blog3>]
>>> Blog.objects.all()[0]
<Blog: Beatles>
>>> Blog.objects.all()[1:3]
[<Blog: blog2>, <Blog: blog3>]

第2个例子,通过[0]得到的就直接是一个Model对象,而第三个例子,通过[1:3]得到的是一个QuerySet对象,也就是说是一个Model对象的集合。第三个例子中,可以理解为limit等于2,offset等于1。


上面简单地介绍了一下Django提供的Model查询接口,虽然简单,但是基本上也就是我们平常经常用到的方法。下面我们再从一对一,多对一,多对多这三个方面,来看看怎么进行稍微复杂点的联合查询。

1. One-to-many relationships

一对多,从“多”的这一方,也就是ForeignKey所在的Model,向“一”的这一方查,比较容易,只需要指定属性名称就可以了,比如:

>>> e = Entry.objects.get(id=2)
>>> e.blog

如果从“一”的这一方向“多”的那一方查,那么“多”的一方是“一”的一方的一个集合,Django会自动为“一”的这一方创建一个叫做"Foo_set"的Manager,它就是“多”的这一方的集合,可以直接通过这个Manager对关联的Model进行增删查改操作。比如:

>>> 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()
2. Many-to-many relationships

多对多,其实和一对多非常的像,定义ManyToManyField的Model访问和它关联的Model时,直接访问该Model的名字就可以了,反过来,就需要加上“_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.

是不是和一对多非常的像?

3. One-to-one relationships

一对一,和前两个也是非常像的,唯一不同的地方,还是在“反过来”,也就是由没有定义OneToOneField的那一方向定义了OneToOneField的那一方进行的查询,如:

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

好,最后说一点,在没有定义xxxToxxxField的那一方,是怎么知道都是有谁和它关联呢?也就是是在什么时候,它的Model里面多了一个xxx_set属性呢?答案就是任何一个Model呗加载的时候,Django会遍历INSTALLED_APPS中的所有APP的所有Model,根据xxxToxxxField来对应的给关联类对象创建xxx_set这样的Manager属性。

这样虽然有些暴力,但是使得Django程序看上去更简洁,没有无用的重复的代码了。

好,Model Query就介绍到这里,这里只是一个入门的流水而已,更加丰富的内容,还需要移步到Django的文档中去:

标签:models,QuerySet,Django,filter,objects,Entry,Query,Model,小记
From: https://blog.51cto.com/u_5173797/7251080

相关文章

  • Django学习小记[6]——Class-based View
    这节我们讲一下Class-basedView,为什么要有这个Class-basedView呢?view不都是一个方法吗?跟类有啥关系?其实答案很明显,用类其实是为了抽象,抽象出通用的,将可变的暴露出来,这样我们就可以用最少的代码实现复杂的功能了。Django中,对那些我们平时经常用的View进行了封装,比如用于渲染一......
  • Django学习小记[4]——URL Dispatcher
    URLdispatcher简单点理解就是根据URL,将请求分发到相应的方法中去处理,它是对URL和View的一个映射,它的实现其实也很简单,就是一个正则匹配的过程,事先定义好正则表达式和该正则表达式对应的view方法,如果请求的URL符合这个正则表达式,那么就分发这个请求到这个view方法中。有了这个bas......
  • Django学习小记[2] —— Model
    开始学习django的model了,学习django的目的很简单,就是我想用django搭建一个自己的博客,现在开源的已经有django-zinnia这个博客引擎了,但是想要看懂它,并且修改它,就必须过django这一关。之前对django的了解,仅仅限于用到了什么,就知道什么,缺乏系统的学习,所以要把django的文档都过一遍,做......
  • Django学习小记[1] —— Start
    Part1Part1通过举例,从整体上过了一遍django的基本内容,包括project,app,database,model等内容。有几下内容需要注意:projectvs.appapp是一个web应用程序,它是实际用来做事的,比如zinnia这个用django写的博客引擎就是一个app,但是一个project是配置文件和app的集合,相当于一个......
  • 「Log」2023.8.26 小记
    序幕起晚了,干脆破罐子破摔,晚点到。八点前到校,被教练投喂雪糕。水两道红题,选笔记本巴拉巴拉。讲题讲题胡题。吃饭。讲题讲题胡题。摆。摆。摆。摆。摆。摆。摆。摆。摆。......
  • 【补充】Django中的信号
    【一】Django中的信号Django中的信号是一种机制,用于在特定事件发生时自动触发相关的操作或函数。通过使用信号,可以实现模块间的解耦和事件驱动的编程。在Django中,有两种类型的信号:内置信号和自定义信号。【二】内置信号Django提供了许多内置信号,以便我们在与数据库交互......
  • Kruskal重构树小记
    模拟赛考了,简单贺一下oi-wiki引入定义在跑\(\rmKruskal\)的过程中我们会从小到大加入若干条边。现在我们仍然按照这个顺序。首先新建\(n\)个集合,每个集合恰有一个节点,点权为\(0\)。每一次加边会合并两个集合,我们可以新建一个点,点权为加入边的边权,同时将两个集合的根节......
  • Django 中实现上传图片配置
    models文件创建的字段模型,类型为ImageField,在ImageField中添加以下代码(如果该文件夹不存在则自动创建) settings文件代码如下  url配置 ......
  • 「Log」2023.8.25 小记
    序幕到校同学都没来,先摆。写博客,写啊,写啊。改费用流板子。\(\color{royalblue}{P3381\【模板】最小费用最大流}\)板子。痛心疾首,建边的时候费用边反边为负权边。\(\text{Link}\)间幕\(1\)水一道平衡树加强版,直接复制粘贴板子,无意义的。网络流。\(\color{royalblue}{P......
  • django配置swagger自动生成接口文档以及自定义参数设置
    首先安装swagger所用的包pipinstalldrf-yasg然后再settings.py中注册app     接口采用的token认证,在settings.py配置认证方式SWAGGER_SETTINGS={'USE_SESSION_AUTH':False,'SECURITY_DEFINITIONS':{......