1. 自定义管理器(Manager)
在语句 Book.objects.all()中, objects 是一个特殊的属性, 通过它来查询数据库, 它就是模型的一个 Manager.
每个 Django 模型至少有一个 manager, 你可以创建自定义 manager 以定制数据库的访问.
这里有两个方法创建自定义 manager: 添加额外的 manager; 修改 manager 返回的初始 Queryset.
添加额外的 manager
增加额外的 manager 是为模块添加表级功能的首选办法.(至于行级功能, 也就是只作用于模型实例对象的函数, 则通过自定义模型方法实现).
例如, 为 Book 模型添加一个 title_count()的 manger 方法, 它接收一个 keyword, 并返回标题中包含 keyword 的书的数量.
models.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://www.cnblogs.com/sui776265233/p/11571418.html