1.5 与QuertSet和管理器协同工作

  前述内容设置了一个全功能管理站点,并可对博客内容进行处理.本节将讨论如何从数据库中获取信息并与其进行交互.Django设置了强大的数据库抽象API,并以此方便地创建、获取、更新以及删除对象.同时,Django中的对象关系映射器兼容于MySQL、PostgreSQL、SQLite、Oracle.需要注意的是,我们可在项目的settings.py文件中的DATABASES设置项中定义当前项目的数据库.Django可一次与多个数据库协同工作,用户可以对数据库路由器进行编程,以创建自定义路由方案.

  

  在数据库模型创建完毕后,Django提供了相应的API可与其进行交互.

 

1.5.1 创建对象

  打开终端并运行以下命令启动Python shell:

python manage.py shell

  随后输入下列代码行

from django.contrib.auth.models import User
from blog.models import Post
user = User.objects.get(username='admin')
post = Post(title='Another post',slug='another-post',body='Post body',author=user)
post.save()

  

  下面对上述代码的执行内容进行简要分析.首先,我们通过用户名admin获取user对象,如下所示:

user = User.objects.get(username='admin')

  这里,get()方法可从数据库中获取单一对象.注意,该方法期望得到与查询匹配的结果.如果数据库未返回任何结果,该方法将会抛出DoesNotExist异常:如果数据库返回多条结果,则会抛出MultipleObjectsReturned异常.这两个异常均为与执行查询对应的、模型类的属性.

  随后利用定制title、slug以及body创建Post实例,并将之前检索到的用户设置为帖子的作者,如下所示:

post = Post(title='Another post',slug='another-post',body='Post body',author=user)

 

 

  !!!注意:

    此类对象位于内存中,且未实现数据库的持久化操作.

    最后,利用save()方法将Post对象保存至数据库,如下所示:

post.save()

  上述操作在后台执行了INSERT SQL语句.前述内容讨论了在内存中创建对象,并于随后将其持久化至数据库中.除此之外,还可通过create()这一单一操作方式创建对象,并将其持久化至数据库中,如下所示:

Post.objects.create(title='One more post',slug='one-more-post',body='Post body.',author=user)

 

1.5.2 更新对象

  下面修改帖子的标题,并再次保存对象,对应代码如下所示:

post.title = 'New title'
post.save()

  此处,save()方法执行UPDATE SQL语句.

  !!!注意:

    当调用了save()方法后,对象的变化内容方能持久化至数据库中.

 

1.5.3 获取对象

  Django对象关系映射机制(ORM)基于Queryset.Queryset表示为一个源自数据库的对象集合,其中包含了多个过滤器以对结果进行限制.我们了解到通过get()方法获取数据库中的单一对象,并利用Post.objects.get()访问该方法.相应的,每个Django模型至少包含一个管理器,且默认管理器称作objects.通过模型管理器,用户可得到一个Queryset对象.当从某个表中获取所有的对象时,仅需使用默认对象管理器上的all()方法即可,如下:

all_posts = Post.objects.all()

  上述代码显示了Queryset的创建方式,并返回数据库中的全部对象.注意,该Queryset尚未被执行.Django中的Queryset具有延迟性,仅在强制操作下方得以被执行,这种行为使得Queryset更加高效.如果未将Queryset设置为某个变量,而是直接将其写入python shell中,Queryset的SQL语句将被执行--此处将其强制至输出结果中,如下:

Post.objects.all()

  

  1.使用filter()方法

  当过滤Queryset时,可食用管理器的filter()方法.例如,可利用下列Queryset获得2019年中发布的全部帖子:

Post.objects.filter(publisher__year=2019)

  除此之外,还可通过多个字段进行过滤.例如,可通过包含用户名admin的作者获取发布于2019年的所有帖子,如下:

Post.objects.filter(publisher__year=2019,author__username='admin')

  这相当于构建链接多个过滤器的同一Queryset,如下:

Post.objects.filter(publish__year=2017).filter(author__username='admin')

 

  !!!注意:

    包含字段查找方法的查询操作可采用两个下划线予以构建,如publish__year;但同一标记也可用于相关模型的访问字段,如author__username.

 

  2.使用exclude()方法

  利用过滤器的exclude()方法,可从Queryset中排除特定的结果.例如,可获取标题不包含test的、发布于2019年的全部帖子,如下:

Post.objects.filter(publish__year=2019).exclude(title__startswith='test')

 

  3.使用order_by()方法

  利用管理器的order_by()方法,可通过不同的字段对结果进行排序.例如,可获取以标题排序的全部对象,如下:

Post.objects.order_by('title')

  这里,默认为升序操作.当然还可通过负号前缀进行降序排序,如下:

Post.objects.order_by('-title')

 

 

  1.5.4 删除对象

  如果希望删除某个对象,可通过delete()方法在对象实例中执行这一操作,如下:

post = Post.objects.get(id=1)
post.delete()

  !!!注意:

    删除对象也会删除ForeignKey对象(on_delete设置为CASCADE)的依赖关系.

 

  1.5.5 评估Queryset

  我们可以连接多个过滤器到一个Queryset上,在Queryset计算之前并不会访问数据库.Queryset仅在以下场合被计算.

  • 首次迭代时
  • 当对Queryset访问时,如Post.objects.all()[:3]
  • 当对Queryset缓存时
  • 当在Queryset上调用repr()或len()时
  • 当在Queryset上显示调用list()时
  • 当在某个语句中对Queryset进行测试时,如bool()、or、and或者if

 

  1.5.6 创建模型管理器

  如前所述,对象表示为每个模型的默认管理器(可检索数据库中的全部对象).然而,我们还可针对模型定义定制管理器.下面将创建定制管理器并检索包含published状态的全部帖子.

  对此,存在两种方式可向模型中添加管理器,即添加额外的管理器方法,或者修改初始管理器Queryset.其中,第一个方法提供了相应的Queryset API,如Post.objects.my_manager();而第二个方法则提供了Post.my_manager.all().该管理器可通过Post.published.all()检索帖子.

  下面编辑models.py文件并添加定制管理器,如下:

class PublishedManager(models.Manager):
    def get_queryset(self):
        return super(PublishedManager,self).get_queryset().filer(status='published')

class Post(models.Model):
    # ...
    objects = models.Manager()  # 默认manager
    published = PublishedManager()  # 定制的manager

  管理器的get_queryset()方法返回将被执行的Queryset.我们将覆写该方法,以在最终的Queryset中包含自定义过滤器.之前曾定义了定制管理器,并将其添加值Post模型中,此处可对其加以使用并执行查询.

  通过以下命令再次启用开发服务器:

python manage.py shell

  此处可利用下列命令检索所有发布的帖子,其对应的标题以test开始

Post.published.filter(title__startswith='test')

 

posted @ 2019-08-25 23:47  周亚澄  阅读(220)  评论(0编辑  收藏  举报