Django 模型中自定义Manager和模型方法
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和模型方法的更多相关文章
- dede新建模型中自定义联动类别调用及修改方法
搜索了好久,没找到一个好的方法,就凑活用这个方法吧.也许只有这个方法比较好 先在后台的“联动类别管理”里新增“类别组”,“类 别 名”填中文,“缓存组名”填英文字母. 在“分类名称”后面增加分类 然后 ...
- YII2中自定义用户认证模型,完成登陆和注册
有些时候我们需要自已定义用户类,操作自已建的用户表,来完成登陆和注册功能. 用户表结构如下,当然可以根据自已的需要添加或删除: CREATE TABLE `tb_user` ( `id` int(11 ...
- python3开发进阶-Django框架中form的查看校验方法is_valid()的源码,自定义验证方法
form表单的校验方法is_valid() 点开我们发现这个函数里面只有两个方法方法,最终返回True or False 我们点进.is_bound属性,里面判断传输的数据不是空和上传文件不是空 点进 ...
- [django]模板中自定义变量&django模板中的变量
django自定义模板变量 context_processors.py def mysetings(request): return { 'NAME': 'maotai' } settings.py ...
- Django 模板中 变量 过滤器的使用方法
一.变量 1.变量的形式是:{{variable}}, 当模板引擎碰到变量的时候,引擎使用变量的值代替变量. 2.使用dot(.)能够访问变量的属性 3.当模板引擎碰到dot的 ...
- 在django项目中自定义manage命令(转)
add by zhj 是我增加的注释 原文:http://www.cnblogs.com/holbrook/archive/2012/03/09/2387679.html 我们都用过Django的dj ...
- django框架中form表单Post方法无法提交 Forbidden (403) CSRF verification failed. Request aborted.
问题如图: 解决方法: 在视图函数中引入并使用装饰器 from django.views.decorators.csrf import csrf_exempt @csrf_exempt
- 转--Android中自定义字体的实现方法
1.Android系统默认支持三种字体,分别为:“sans”, “serif”, “monospace 2.在Android中可以引入其他字体 . 复制代码 代码如下: <?xml versio ...
- 竞价拍卖理论的介绍(RTB模型中使用第二竞价模型,为的是纳什平衡,保护所有多方利益)
英式拍卖 是最普通的拍卖方式,其形式是拍卖过程中,竞价按阶梯,从低到高,依次递增.最终由出价最高者获得拍卖物品(竞买人变成买受人). The first price auction: a form o ...
随机推荐
- JS学习一
js中的变量输出 [使用JS的三种方式] 1. 在HTML标签中,直接内嵌JS(并不提倡使用): <button onclick="alert('你真点啊!')"> ...
- 团队项目beta 汇总
一.冲刺计划安排 http://www.cnblogs.com/KKlist/p/6864124.html 二.七天的敏捷冲刺 第一天(2017.05.19) http://www.cnblogs.c ...
- 团队作业4----第一次项目冲刺(Alpha版本)4.29
a.站立式会议照片 会议内容: 会议讨论有一些内容要放在beta版本,所以燃尽图可能有些未完成.初级阶段我们只打算先写java代码的统计和显示.这是最基本的任务.在繁忙的任务中,不需要全部按照燃尽图. ...
- Java 课程设计 "Give it up"小游戏(团队)
JAVA课程设计 "永不言弃"小游戏(From :Niverse) 通过Swing技术创建游戏的登陆注册界面,使用mySQL数据库技术完成用户的各项信息保存和游戏完成后的成绩保存. ...
- hadoop运行wordcount实例,hdfs简单操作
1.查看hadoop版本 [hadoop@ltt1 sbin]$ hadoop version Hadoop -cdh5.12.0 Subversion http://github.com/cloud ...
- temp--达州银行
达州银行现场 服务器IP地址 192.168.1.234 ilink / ilink 自己电脑需要设置为固定IP 192.168.1.XXX 子网掩码 255.255.255.0 192.168.1. ...
- bind9的一些配置
/etc/bind/named.conf.options:options { listen-on port 53 { any; }; // 监听在主机的53端口上.any代表监听所有的主机 direc ...
- Linux配置SSH端口以及密钥登录
改端口后重启: vim /etc/ssh/sshd_config systemctl restart sshd
- 深入浅出数据结构C语言版(19)——堆排序
在介绍优先队列的博文中,我们提到了数据结构二叉堆,并且说明了二叉堆的一个特殊用途--排序,同时给出了其时间复杂度O(N*logN).这个时间界是目前我们看到最好的(使用Sedgewick序列的希尔排序 ...
- Flask-WTF CSRF 保护P3
使用FlaskForm处理请求的任何视图函数都已经获得了CSRF保护.如果有些视图函数还在使用FlaskForm或AJAX请求,请尽快使用FlaskForm提供的CSRF扩展来保护它们. 导入 想要为 ...