1.自定义管理器(Manager)

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

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

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

添加额外的manager

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

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

#medols.py
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中的查询方法
2
>>> Books.objects.filter(title__icontains='django').count() # 默认的查询方法依然可用
2

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

修改初始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.'})
...

参考: https://djangobook.com/advanced-models/
   http://python.usyiyi.cn/translate/django_182/ref/models/instances.html

Django 模型中自定义Manager和模型方法的更多相关文章

  1. dede新建模型中自定义联动类别调用及修改方法

    搜索了好久,没找到一个好的方法,就凑活用这个方法吧.也许只有这个方法比较好 先在后台的“联动类别管理”里新增“类别组”,“类 别 名”填中文,“缓存组名”填英文字母. 在“分类名称”后面增加分类 然后 ...

  2. YII2中自定义用户认证模型,完成登陆和注册

    有些时候我们需要自已定义用户类,操作自已建的用户表,来完成登陆和注册功能. 用户表结构如下,当然可以根据自已的需要添加或删除: CREATE TABLE `tb_user` ( `id` int(11 ...

  3. python3开发进阶-Django框架中form的查看校验方法is_valid()的源码,自定义验证方法

    form表单的校验方法is_valid() 点开我们发现这个函数里面只有两个方法方法,最终返回True or False 我们点进.is_bound属性,里面判断传输的数据不是空和上传文件不是空 点进 ...

  4. [django]模板中自定义变量&django模板中的变量

    django自定义模板变量 context_processors.py def mysetings(request): return { 'NAME': 'maotai' } settings.py ...

  5. Django 模板中 变量 过滤器的使用方法

    一.变量       1.变量的形式是:{{variable}}, 当模板引擎碰到变量的时候,引擎使用变量的值代替变量.    2.使用dot(.)能够访问变量的属性    3.当模板引擎碰到dot的 ...

  6. 在django项目中自定义manage命令(转)

    add by zhj 是我增加的注释 原文:http://www.cnblogs.com/holbrook/archive/2012/03/09/2387679.html 我们都用过Django的dj ...

  7. django框架中form表单Post方法无法提交 Forbidden (403) CSRF verification failed. Request aborted.

    问题如图: 解决方法: 在视图函数中引入并使用装饰器 from django.views.decorators.csrf import csrf_exempt @csrf_exempt

  8. 转--Android中自定义字体的实现方法

    1.Android系统默认支持三种字体,分别为:“sans”, “serif”, “monospace 2.在Android中可以引入其他字体 . 复制代码 代码如下: <?xml versio ...

  9. 竞价拍卖理论的介绍(RTB模型中使用第二竞价模型,为的是纳什平衡,保护所有多方利益)

    英式拍卖 是最普通的拍卖方式,其形式是拍卖过程中,竞价按阶梯,从低到高,依次递增.最终由出价最高者获得拍卖物品(竞买人变成买受人). The first price auction: a form o ...

随机推荐

  1. 结对编程-四则运算-GUI

     201421123022 王若凡        201421123026  欧阳勇 https://git.coding.net/ttoyy/sizeyunsuan-GUI.git a.需求分析: ...

  2. 201521123074 《Java程序设计》第6周学习总结

    1. 本周学习总结 1.1 面向对象学习暂告一段落,请使用思维导图,以封装.继承.多态为核心概念画一张思维导图,对面向对象思想进行一个总结. 注1:关键词与内容不求多,但概念之间的联系要清晰,内容覆盖 ...

  3. Java课程设计—学生成绩管理系统(201521123004-林艺如)

    1.团队课程设计博客 团队课程设计博客链接 2.个人负责模块或任务说明 ①.Menu Menu.jsp 在页面中给出提示,用HTML的 MenuTeacher.jsp 利用Menu.jsp进行具体化完 ...

  4. expect实现scp/ssh-copy-id非交互

    expect工具可以实现自动应答,从而达到非交互的目的. expect具体使用用法比较复杂,中文手册我正在翻译中,以后翻译完了做了整理再补.本文只有几个ssh相关最可能用上的示例. yum -y in ...

  5. webservice06#异常#Handler

    1, 异常处理 package com.yangw.soap.service; public class UserException extends Exception { public UserEx ...

  6. Java简单实用方法一

    整理以前的笔记,在学习Java时候,经常会用到一些方法.虽然简单但是经常使用.因此做成笔记,方便以后查阅 这篇博文先说明构造和使用这些方法. 1,判断String类型数据是否为空 String类型的数 ...

  7. datepickerpopup时间限制选取

    使用popup组件的过程中遇到时间选取的问题 官方文档大致说使用date和mode 可以解决,奈何老夫是看不懂,写的时候参考的有 官方文档.echo2016的博文.liumang361的博文 先看图 ...

  8. Sublimetext3安装Emmet插件步骤

    看清楚哦~~这是Sublime text 3不是2的版本,两者的安装还是有区别的,下面的方法是我感觉比较简单的,其他的要命令什么的感觉太复杂了,经测试是OK的. 先关闭Sublime text 3: ...

  9. 组合 Lucas定理

    组合 Time Limit: 1000MS   Memory Limit: 32768KB   64bit IO Format: %I64d & %I64u [Submit]   [Go Ba ...

  10. 【codevs1001】[bzoj1050]舒适的路线

    给你一个无向图,N(N<=500)个顶点, M(M<=5000)条边,每条边有一个权值Vi(Vi<30000).给你两个顶点S和T,求 一条路径,使得路径上最大边和最小边的比值最小. ...