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

所属分类: 脚本专栏 / python 阅读数: 966
收藏 0 赞 0 分享

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常见加密模块用法分析【MD5,sha,crypt模块】

这篇文章主要介绍了Python常见加密模块用法,结合实例形式较为详细的分析了MD5,sha与crypt模块加密的相关实现方法与操作技巧,需要的朋友可以参考下
收藏 0 赞 0 分享

Python向日志输出中添加上下文信息

这篇文章主要介绍了Python向日志输出中添加上下文信息的方法,非常不错,具有参考借鉴价值,需要的朋友可以参考下
收藏 0 赞 0 分享

Python实现的简单dns查询功能示例

这篇文章主要介绍了Python实现的简单dns查询功能,结合实例形式分析了Python基于socket模块的dns信息查询实现技巧,需要的朋友可以参考下
收藏 0 赞 0 分享

利用Anaconda完美解决Python 2与python 3的共存问题

Anaconda 是 Python 的一个发行版,如果把 Python 比作 Linux,那么 Anancoda 就是 CentOS 或者 Ubuntu,下面这篇文章主要给大家介绍了利用Anaconda完美解决Python 2与python 3共存问题的相关资料,文中介绍的非常详
收藏 0 赞 0 分享

Python随机读取文件实现实例

这篇文章主要介绍了Python随机读取文件的相关资料,需要的朋友可以参考下
收藏 0 赞 0 分享

用生成器来改写直接返回列表的函数方法

下面小编就为大家带来一篇用生成器来改写直接返回列表的函数方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
收藏 0 赞 0 分享

python爬虫入门教程--快速理解HTTP协议(一)

http协议是互联网里面最重要,最基础的协议之一,我们的爬虫需要经常和http协议打交道。下面这篇文章主要给大家介绍了关于python爬虫入门之快速理解HTTP协议的相关资料,文中介绍的非常详细,需要的朋友可以参考借鉴,下面来一起看看吧。
收藏 0 赞 0 分享

老生常谈Python进阶之装饰器

下面小编就为大家带来一篇老生常谈Python进阶之装饰器。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
收藏 0 赞 0 分享

浅谈Python基础之I/O模型

下面小编就为大家带来一篇浅谈Python基础之I/O模型。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
收藏 0 赞 0 分享

python如何获取服务器硬件信息

这篇文章主要为大家详细介绍了python获取服务器硬件信息的相关代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
收藏 0 赞 0 分享
查看更多