Django中自定义模型管理器(Manager)及方法


Posted in Python onSeptember 23, 2019

1.自定义管理器(Manager)

在语句Book.objects.all()中, objects 是一个特殊的属性,通过它来查询数据库,它就是模型的一个Manager.

每个Django模型至少有一个manager,你可以创建自定义manager以定制数据库的访问.

这里有两个方法创建自定义manager:添加额外的manager;修改manager返回的初始Queryset.

添加额外的manager

增加额外的manager是为模块添加 表级功能 的首选办法.(至于 行级功能 ,也就是只作用于模型实例对象的函数,则通过自定义模型方法实现).

例如,为Book模型添加一个 title_count() 的manger方法,它接收一个 keyword ,并返回标题中包含 keyword 的书的数量.

from django.db import models
# 自定义模型管理器类
class BookManager(models.Manager):
  #自定义模型管理器中的方法
  def title_count(self, keyword):
    return self.filter(title_icountains=keyword).count()
class Book(models.Model):
  title = models.CharField(max_length=100)
  authors = models.ManyToManyField(Author)
  ...
  objects = BookManager()
  def __str__(self):
    return self.title

1.我们创建一个BookManager类,继承自 django.db.models.Manager .它只有一个方法 title_count() ,来进行统计.注意,这个方法使用了 self.filter() ,这个self指manager本身.

2.将BookManager()赋值给模型的objects属性.它将取代模型的默认manager(objects).把它命名为objects是为了与默认的manager保持一致.

现在我们可以进行下面的操作:

>>> Books.objects.title_count('django')  #这是我们自定义的manager中的查询方法

>>> Books.objects.filter(title__icontains='django').count()  # 默认的查询方法依然可用

这样我们可以将经常使用的查询进行封装,就不必重复写代码了.

修改初始Manager Queryset

manager的基础Queryset返回系统中的所有对象.例如, Book.objects.all() 返回book数据库中的所有书籍.你而已通过覆盖 Manager.get_queryset() 方法来重写manager的基础Queryset. get_queryset() 应该按照你的需求返回一个Queryset.

例如,下面的模型有两个manger--一个返回所有对象,另一个仅返回作者是Roald Dahl的书

from django.db import models

#首先,定义一个Manager的子类
class DahlBookManager(models.Manager):
  def get_queryset(self):
    return super(DahlBookManager, self).get_queryset().filter(author='Roald Dahl')


# 然后,将它显式地插入到Book模型中
class Book(models.Model):
  title = models.CharField(max_length=100)
  author = models.CharField(max_length=50)
  ...
  objects = models.Manager()  # 默认Manager
  dahl_objects = DahlBookManager()  # 自定义的特殊Manager

在这个示例模型中, Book.objects.all() 将返回数据库中的所有书籍,而 Book.dahl_objects.all() 只返回作者是Roald Dahl的书籍.注意我们明确的将 objects 设置为默认 Manger 的一个实例,因为如果我们不这样做,那么dahl_objects将成为唯一一个可用的manager.

由于 get_queryset() 返回一个Queryset对象,所以你可以使用 filter() , exclude() 和其他所有的Queryset方法.

如果你使用自定义的Manager对象,请注意,Django遇到的第一个Manager(以它在模型中被定义的位置为准)会有一个特殊状态。 Django将会把第一个Manager 定义为默认Manager ,Django的许多部分(但是不包括admin应用)将会明确地为模型使用这个manager。 结论是,你应该小心地选择你的默认manager。因为覆盖 get_queryset() 了,你可能接受到一个无用的返回对像,你必须避免这种情况.

2.自定义模型方法

为了给你的对像添加一个行级功能,那就定义一个自定义方法.鉴于manager经常被用来用一些整表操作(table-wide).模型方法应该只对特殊模型实例起作用.

from django.db import models

class Person(models.Model):
  first_name = models.CharField(max_length=50)
  last_name = models.CharField(max_length=50)
  birth_date = models.DateField()

  def baby_boomer_status(self):
    # Returns the person's baby_boomer status
    import datetime
    if self.birth_date < datetime.date(1945, 8, 1):
      return 'Pre-boomer'
    elif self.birth_date < datetime.date(1965, 1, 1):
      return 'Baby boomer'
    else:
      return 'Post-boomer'

  def _get_full_name(self):
    # Return the person's full name
    return f'{self.first_name} {self.last_name}'
  full_name = property(_get_full_name)  # 将类方法包装为属性

这些方法的使用:

>>> p = Person.objects.get(first_name='Barack', last_name='Obama')
>>> p.birth_date
datetime.date(1961, 8, 4)
>>> p.baby_boomer_status()
'Baby boomer'
>>> p.full_name # 注意这不是一个方法 -- 它被视为一个属性
'Barack Obama'

3.重写预定义的模型方法

还有一组模型方法了封装了一些你可能想要自定义的数据库行为.特别是你可能想要修改 save() 和 delete() 的工作方式.你可以自由的重写这些方法(以及其他的模型方法)来改变行为.重写内置方法的经典用例就是你想要在保存一个对象是做些其他的什么.例如:

from django.db import models

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

  def save(self, *args, **kwargs):
    do_something()
    super(Blog, self).save(*args, **kwargs)  #Call the "real" save() method.
    do_something_else()

你也可以阻止保存行为:

from django.db import models
class Blog(models.Model):
  name = models.CharField(max_length=100)
  tagline = models.TextField()
  def save(self, *args, **kwargs):
    if self.name == 'Yoko Ono's Blog':
      return  # Yoko shall never have her own blog!
    else:
      super(Blog, self).save(*args, **kwargs)  #Call the "real" save() method

记住,继承超类的方法非常重要,即 super(Blog, self).save(*args, **kwargs) ,它确保该对象仍被保存到数据库中.如果你忘记调用超类方法,那么默认的行为将不会发生,也不会发生数据库操作.

同样重要的是,您要传递可以传递给模型方法的参数——这就是 *args, **kwargs 所做的事情。Django将不时扩展内置模型方法的功能,并添加新的参数。如果您在方法定义中使用了 *args, **kwargs ,您将保证您的代码在添加时将自动支持这些参数。

Model.clean()

应用这个方法来提供自定义的模型验证,以及修改模型的属性.例如,你可以使用它来给一个字段自动提供值,或者用于多个字段需要一起验证的情形:

import detetime
from django.core.exceptions import ValidationError
from django.db import models

class Article(models.Model):
  ...
  def clean(self):
    # Don't allow draft entries to have a pub_date
    if self.status == 'draft' and self.pub_date is not done:
      raise ValidationEroor('Draft entries may not have a publication date')
    #Set the pub_date for published items if it hasn't been set already
    if self.status == 'published' and self.pub_date is None:
      self.pub_date = datetime.date.today()

注意,调用模型的 save() 方法时,不会自动调用 clean() 方法,需要views手动调用.

上面的示例中, clean() 引发的ValidationError异常通过一个字符串实例化,所以它将被保存在一个特殊的错误字典中,键为 NON_FIELD_ERRORS .这个键用于整个模型出现的错误而不是一个特定字段穿线的错误:

from django.core.exceptions import ValidationError, NON_FIELD_ERRORS
try:
  article.full_clean()
except ValidationError as e:
  non_field_errors = e.message_dict[NON_FIELD_ERRORS]

若要引发一个特定字段的异常,可以使用一个字典实例化 ValidationError ,其中字典的键为字段名.我们可以更新前面的例子,只引发 pub_date 字段上的异常:

class Article(models.Model):
  ...
  def clean(self):
    # Don't allow draft entries to have a pub_date.
    if self.status == 'draft' and self.pub_date is not None:
      raise ValidationError({'pub_date': 'Draft entries may not have a publication date.'})
    ...

总结

以上所述是小编给大家介绍的Django中自定义模型管理器(Manager)及方法,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

Python 相关文章推荐
Python中文件遍历的两种方法
Jun 16 Python
Python中的数据对象持久化存储模块pickle的使用示例
Mar 03 Python
Python实现网络端口转发和重定向的方法
Sep 19 Python
python 与GO中操作slice,list的方式实例代码
Mar 20 Python
Python爬虫实现简单的爬取有道翻译功能示例
Jul 13 Python
python 循环读取txt文档 并转换成csv的方法
Oct 26 Python
对Python subprocess.Popen子进程管道阻塞详解
Oct 29 Python
Django中ORM外键和表的关系详解
May 20 Python
自适应线性神经网络Adaline的python实现详解
Sep 30 Python
对tensorflow中cifar-10文档的Read操作详解
Feb 10 Python
利用Python实现某OA系统的自动定位功能
May 27 Python
Django QuerySet查询集原理及代码实例
Jun 13 Python
Python 异常的捕获、异常的传递与主动抛出异常操作示例
Sep 23 #Python
Python 单例设计模式用法实例分析
Sep 23 #Python
Python3使用xml.dom.minidom和xml.etree模块儿解析xml文件封装函数的方法
Sep 23 #Python
详解Python3定时器任务代码
Sep 23 #Python
python应用文件读取与登录注册功能
Sep 23 #Python
Python爬虫 urllib2的使用方法详解
Sep 23 #Python
Python 正则表达式爬虫使用案例解析
Sep 23 #Python
You might like
建立文件交换功能的脚本(二)
2006/10/09 PHP
PHP识别二维码的方法(php-zbarcode安装与使用)
2016/07/07 PHP
PHP面向对象程序设计中的self、static、parent关键字用法分析
2019/08/14 PHP
JQuery优缺点分析说明
2010/06/09 Javascript
jquery自动填充勾选框即把勾选框打上true
2014/03/24 Javascript
用js格式化金额可设置保留的小数位数
2014/05/09 Javascript
常用的jquery模板插件——jQuery Boilerplate介绍
2014/09/23 Javascript
angular.element方法汇总
2015/01/07 Javascript
JavaScript实现将xml转换成html table表格的方法
2015/04/17 Javascript
JS+CSS实现经典的左侧竖向滑动菜单效果
2015/09/23 Javascript
checkbox:click事件触发span元素内容改变的方法
2017/09/11 Javascript
微信小程序实现折叠面板
2018/01/31 Javascript
Vue的双向数据绑定实现原理解析
2020/02/17 Javascript
vue中实现回车键登录功能
2020/02/19 Javascript
vue打开其他项目页面并传入数据详解
2020/11/25 Vue.js
[05:31]DOTA2英雄梦之声_第08期_莉娜
2014/06/23 DOTA
python 实现一个贴吧图片爬虫的示例
2017/10/12 Python
python 时间信息“2018-02-04 18:23:35“ 解析成字典形式的结果代码详解
2018/04/19 Python
利用Anaconda简单安装scrapy框架的方法
2018/06/13 Python
解决python3中cv2读取中文路径的问题
2018/12/05 Python
使用Python画出小人发射爱心的代码
2019/11/23 Python
Python实现CNN的多通道输入实例
2020/01/17 Python
Pycharm同步远程服务器调试的方法步骤
2020/11/04 Python
python中封包建立过程实例
2021/02/18 Python
Html5百叶窗效果的示例代码
2017/12/11 HTML / CSS
ProBikeKit美国官网:自行车套件,跑步和铁人三项套件
2016/10/13 全球购物
英国最大的在线运动补充剂商店:Discount Supplements
2017/06/03 全球购物
资产评估专业学生的自我鉴定
2013/11/14 职场文书
发展部经理职责规定
2014/02/22 职场文书
高三学习决心书
2014/03/11 职场文书
冰峪沟导游词
2015/02/09 职场文书
工作时间调整通知
2015/04/24 职场文书
大学生村官入党自传
2015/06/26 职场文书
妇联2016年六一国际儿童节活动总结
2016/04/06 职场文书
基于Python实现的购物商城管理系统
2021/04/27 Python
Debian11 Xfce终端光标的颜色怎么设置?
2022/08/14 数码科技