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中列表和元祖的使用方法
Apr 25 Python
python获取当前日期和时间的方法
Apr 30 Python
python复制文件的方法实例详解
May 22 Python
python控制台中实现进度条功能
Nov 10 Python
python 中的int()函数怎么用
Oct 17 Python
python 不以科学计数法输出的方法
Jul 16 Python
攻击者是如何将PHP Phar包伪装成图像以绕过文件类型检测的(推荐)
Oct 11 Python
Appium+Python自动化测试之运行App程序示例
Jan 23 Python
Python内置random模块生成随机数的方法
May 31 Python
Python tornado上传文件的功能
Mar 26 Python
Python中的xlrd模块使用原理解析
May 21 Python
python实现数字炸弹游戏
Jul 17 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
Laravel 5.0 发布 新版本特性详解
2015/02/10 PHP
php利用事务处理转账问题
2015/04/22 PHP
反射调用private方法实践(php、java)
2015/12/21 PHP
php中的buffer缓冲区用法分析
2019/05/31 PHP
javascript判断单选框或复选框是否选中方法集锦
2007/04/04 Javascript
javascript通过class来获取元素实现代码
2013/02/20 Javascript
JS Replace()的高级使用方法介绍
2013/06/29 Javascript
ExtJS4中的requires使用方法示例介绍
2013/12/03 Javascript
javascript基础语法学习笔记
2016/01/04 Javascript
jQuery扩展实现text提示还能输入多少字节的方法
2016/11/28 Javascript
JS新包管理工具yarn和npm的对比与使用入门
2016/12/09 Javascript
写jQuery插件时的注意点
2017/02/20 Javascript
纯html+css+javascript实现楼层跳跃式的页面布局(实例代码)
2017/10/25 Javascript
在vscode中统一vue编码风格的方法
2018/02/22 Javascript
vue2.0 资源文件assets和static的区别详解
2018/04/08 Javascript
详解React 的几种条件渲染以及选择
2018/10/23 Javascript
vue 解决数组赋值无法渲染在页面的问题
2019/10/28 Javascript
使用kbone解决Vue项目同时支持小程序问题
2019/11/08 Javascript
js回调函数原理与用法案例分析
2020/03/04 Javascript
微信小程序中data-key属性之数据传输(经验总结)
2020/08/22 Javascript
Python字符串拼接六种方法介绍
2017/12/18 Python
python实战之实现excel读取、统计、写入的示例讲解
2018/05/02 Python
python中将zip压缩包转为gz.tar的方法
2018/10/18 Python
python 监测内存和cpu的使用率实例
2019/11/28 Python
django数据模型(Model)的字段类型解析
2019/12/25 Python
python如何实现word批量转HTML
2020/09/30 Python
python 元组和列表的区别
2020/12/30 Python
Python中的面向接口编程示例详解
2021/01/17 Python
CSS3条纹背景制作的实战攻略
2016/05/31 HTML / CSS
罗马尼亚购物网站:Vivantis.ro
2019/07/20 全球购物
澳大利亚最便宜的网上药房:Chemist Warehouse
2020/01/30 全球购物
如何估计一张表的大小(假设该表中有1万条数据)
2016/03/27 面试题
八一慰问活动方案
2014/02/07 职场文书
2014年消防工作实施方案
2014/02/20 职场文书
2015年党员创先争优公开承诺书
2015/04/27 职场文书
2015年全国保险公众宣传日活动方案
2015/05/06 职场文书