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生成器(Generator)详解
Apr 13 Python
python线程、进程和协程详解
Jul 19 Python
Python贪吃蛇游戏编写代码
Oct 26 Python
Python实现从log日志中提取ip的方法【正则提取】
Mar 31 Python
pandas.DataFrame 根据条件新建列并赋值的方法
Apr 08 Python
django 开发忘记密码通过邮箱找回功能示例
Apr 17 Python
关于Python作用域自学总结
Jun 10 Python
Python绘制全球疫情变化地图的实例代码
Apr 20 Python
PyQt5 控件字体样式等设置的实现
May 13 Python
python实现一次性封装多条sql语句(begin end)
Jun 06 Python
python批量修改文件名的示例
Sep 27 Python
如何用python插入独创性声明
Mar 31 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
解决MySQL中文输出变成问号的问题
2008/06/05 PHP
php打印一个边长为N的实心和空心菱型的方法
2015/03/02 PHP
PHP利用DWZ.CN服务生成短网址
2019/08/11 PHP
Laravel5.4简单实现app接口Api Token认证方法
2019/08/29 PHP
js重写alert控件(适合学习js的新手朋友)
2014/08/24 Javascript
分享两个手机访问pc网站自动跳转手机端网站代码
2020/12/24 Javascript
js验证上传图片的方法
2015/05/12 Javascript
JavaScript返回上一页的三种方法及区别介绍
2015/07/04 Javascript
jQuery入门基础知识学习指南
2015/08/14 Javascript
JavaScript基于DOM操作实现简单的数学运算功能示例
2017/01/16 Javascript
Angular2使用jQuery的方法教程
2017/05/28 jQuery
JS FormData上传文件的设置方法
2017/07/05 Javascript
微信小程序左右滑动的实现代码
2017/12/15 Javascript
jquery.onoff实现简单的开关按钮功能(推荐)
2018/05/24 jQuery
JavaScript使用类似break机制中断forEach循环的方法
2018/11/13 Javascript
javascript单张多张图无缝滚动实例代码
2020/05/10 Javascript
[01:04]DOTA2上海特锦赛现场采访 FreeAgain遭众解说围攻
2016/03/25 DOTA
python3 与python2 异常处理的区别与联系
2016/06/19 Python
python直接访问私有属性的简单方法
2016/07/25 Python
Python中类型检查的详细介绍
2017/02/13 Python
Python实现复杂对象转JSON的方法示例
2017/06/22 Python
Pyorch之numpy与torch之间相互转换方式
2019/12/31 Python
Python异常继承关系和自定义异常实现代码实例
2020/02/20 Python
Python接口自动化判断元素原理解析
2020/02/24 Python
Django多数据库配置及逆向生成model教程
2020/03/28 Python
Python实现动态循环输出文字功能
2020/05/07 Python
用HTML5制作视频拼图的教程
2015/05/13 HTML / CSS
美国精品地毯网站:Boutique Rugs
2020/03/04 全球购物
运动会跳远加油稿
2014/02/20 职场文书
关于晚自习早退的检讨书
2014/09/13 职场文书
2015年重阳节活动总结
2015/03/24 职场文书
2015年度服装销售工作总结
2015/03/31 职场文书
员工加薪申请报告
2015/05/15 职场文书
深入解析NumPy中的Broadcasting广播机制
2021/05/30 Python
Python中递归以及递归遍历目录详解
2021/10/24 Python
高通2023 年将发布高性能PC处理器
2022/04/29 数码科技