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 相关文章推荐
Python3基础之条件与循环控制实例解析
Aug 13 Python
一个计算身份证号码校验位的Python小程序
Aug 15 Python
python计算方程式根的方法
May 07 Python
Python 专题二 条件语句和循环语句的基础知识
Mar 19 Python
python实现简易版计算器
Jun 22 Python
python3实现163邮箱SMTP发送邮件
May 22 Python
对python3中, print横向输出的方法详解
Jan 28 Python
python 弹窗提示警告框MessageBox的实例
Jun 18 Python
远程部署工具Fabric详解(支持Python3)
Jul 04 Python
利用Python实现手机短信监控通知的方法
Jul 22 Python
Python 保持登录状态进行接口测试的方法示例
Aug 06 Python
详解解Django 多对多表关系的三种创建方式
Aug 23 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
雄兵连:天使彦天使彦为爱折翼,彦和炙心同时念动的誓言!
2020/03/02 国漫
10个实用的PHP正则表达式汇总
2014/10/23 PHP
ThinkPHP独立分组使用的注意事项
2014/11/25 PHP
php获取从百度、谷歌等搜索引擎进入网站关键词的方法
2015/07/08 PHP
PHP7 新增功能
2021/03/09 PHP
用js实现的一个Flash滚动轮换显示图片代码生成器
2007/03/14 Javascript
jQuery EasyUI API 中文文档 - EasyLoader 加载器
2011/09/29 Javascript
基于jquery点击自以外任意处,关闭自身的代码
2012/02/10 Javascript
Javascript基础教程之变量
2015/01/18 Javascript
js使用Array.prototype.sort()对数组对象排序的方法
2015/01/28 Javascript
深入理解JavaScript系列(45):代码复用模式(避免篇)详解
2015/03/04 Javascript
JAVASCRIPT代码编写俄罗斯方块网页版
2015/11/26 Javascript
jQuery表格插件datatables用法详解
2020/11/23 Javascript
JavaScript正则表达式匹配 div  style标签
2016/03/15 Javascript
jQuery+php实时获取及响应文本框输入内容的方法
2016/05/24 Javascript
JS实现密码框的显示密码和隐藏密码功能示例
2016/12/26 Javascript
Jquery鼠标放上去显示全名的实现方法
2017/02/06 Javascript
用nodeJS搭建本地文件服务器的几种方法小结
2017/03/16 NodeJs
vue-cli+webpack记事本项目创建
2017/04/01 Javascript
在knockoutjs 上自己实现的flux(实例讲解)
2017/12/18 Javascript
Angular利用内容投射向组件输入ngForOf模板的方法
2018/03/05 Javascript
QQ跳转支付宝并自动领红包脚本(最新)
2018/06/22 Javascript
Angular4 组件通讯方法大全(推荐)
2018/07/12 Javascript
JQuery中的常用事件、对象属性与使用方法分析
2019/12/23 jQuery
jQuery实现小火箭返回顶部特效
2020/02/03 jQuery
python生成不重复随机数和对list乱序的解决方法
2018/04/09 Python
在python里协程使用同步锁Lock的实例
2019/02/19 Python
python读取并写入mat文件的方法
2019/07/12 Python
python利用google翻译方法实例(翻译字幕文件)
2020/09/21 Python
canvas绘制圆角头像的实现方法
2019/01/17 HTML / CSS
澳大利亚音乐商店:Bava’s Music City
2019/05/05 全球购物
King Apparel官网:英国街头服饰品牌
2019/09/05 全球购物
Java程序员面试题
2013/07/15 面试题
班会关于环保演讲稿
2013/12/29 职场文书
公司股权转让协议书
2014/04/12 职场文书
建房合同协议书
2016/03/21 职场文书