参考文档:
django1.8.2–模型继承–抽象基类
django1.8.2—模型管理器
通常,你想使用父模型类来持有一些字段,如创建时间、更新时间等,而不想在每个子模型中都敲一遍。这个父模型类永远不会单独使用,这就是抽象基类。
from django.db import models
class BaseModel(models.Model):
"""模型抽象基类"""
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
is_delete = models.BooleanField(default=False, verbose_name='删除标记')
class Meta:
# 说明是一个抽象模型类
abstract = True
编写完基类之后,在 Meta类中设置 abstract=True
,这个模型就不会被用来创建任何数据表。取而代之的是,当它被用来作为一个其他model的基类时,它的字段将被加入那些子类中。
如:下面的模型类设置了两个字段,但是继承于BsaeModel
基类在产生数据表时会有额外三个字段。
from django.db import models
from db.base_model import BaseModel
class Goods(BaseModel):
"""商品SPU模型类"""
name = models.CharField(max_length=20, verbose_name='商品SPU名称')
# 富文本类型:带有格式的文本
detail = HTMLField(blank=True, verbose_name='商品详情')
class Meta:
db_table = 'df_goods'
verbose_name = '商品SPU'
verbose_name_plural = verbose_name
mysql> desc df_goods;
+-------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| create_time | datetime(6) | NO | | NULL | |
| update_time | datetime(6) | NO | | NULL | |
| is_delete | tinyint(1) | NO | | NULL | |
| name | varchar(20) | NO | | NULL | |
| detail | longtext | NO | | NULL | |
+-------------+-------------+------+-----+---------+----------------+
6 rows in set (0.04 sec)
注意:如果抽象基类和它的子类有相同的字段名,那么将会出现error(并且Django将抛出一个exception)。
管理器是Django 的模型进行数据库查询操作的接口。Django 应用的每个模型都拥有至少一个管理器。
默认情况下,Django 为每个模型类添加一个名为objects的管理器。
可在模型中定义一个值为models.Manager()
的属性,来重命名管理器。例如:
from django.db import models
class Person(models.Model):
#...
people = models.Manager()
使用例子中的模型, Person.objects
会抛出AttributeError异常,而Person.people.all()
会返回一个包含所有Person对象的列表。
你可以在模型中使用自定义的管理器,方法是继承Manager
基类并实例化你的自定义管理器。
你有两个原因可能会自己定义管理器:向管理器类中添加额外的方法,或者修改管理器返回的原始查询集。
例如:在应用的models.py中编写
class AddressManager(models.Manager):
"""地址模型管理器类"""
# 1.改变原有查询的结果集:all()
# 2.封装方法:用于操作模型类对应的数据表(增删改查)
def get_default_address(self, user):
"""获取用户默认收货地址"""
try:
# 这里其实可以看成self=Address.objects
address = self.get(user=user, is_default=True)
except self.model.DoesNotExist:
# 不存在默认收货地址
address = None
return address
class Address(BaseModel):
"""地址模型类"""
user = models.ForeignKey('User', verbose_name='所属账户')
receiver = models.CharField(max_length=20, verbose_name='收件人')
addr = models.CharField(max_length=256, verbose_name='收件地址')
zip_code = models.CharField(max_length=6, null=True, verbose_name='邮政编码')
phone = models.CharField(max_length=11, verbose_name='联系电话')
is_default = models.BooleanField(default=False, verbose_name='是否默认')
# 实例化模型管理器对象
objects = AddressManager()
之后在视图层便可以简化代码(注释部分为未使用自定义管理器时的代码):
# try:
# address = Address.objects.get(user=user, is_default=True)
# except Address.DoesNotExist:
# address = None
address = Address.objects.get_default_address(user)
这个例子的目的其实就是为了后台在查询用户的默认地址时,不会抛出异常,而返回None,我们把这个步骤封装在模型管理器类里面了。
除以上内容外还需注意:
同一模型使用多个管理器。你可以依据你自己的偏好在一个模型里面添加多个 Manager() 实例。
你可以通过重写Manager.get_queryset()
方法来覆盖管理器自带的Queryset.get_queryset()
会根据你所需要的属性返回查询集。
例如,下面的模型有两个管理器,一个返回所有的对象,另一个则只返回作者是Roald Dahl 的对象:
# First, define the Manager subclass.
class DahlBookManager(models.Manager):
def get_queryset(self):
return super(DahlBookManager, self).get_queryset().filter(author='Roald Dahl')
# Then hook it into the Book model explicitly.
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.CharField(max_length=50)
objects = models.Manager() # The default manager.
dahl_objects = DahlBookManager() # The Dahl-specific manager.
在这个例子中,Book.objects.all()
将返回数据库中所有的图书。而 Book.dahl_objects.all()
只返回作者是 Roald Dahl 的图书。
当然,因为get_queryset()
返回QuerySet对象,可以使用filter()
,exclude()
和所有其他QuerySet方法。所以下面这些例子都是可用的:
Book.dahl_objects.all()
Book.dahl_objects.filter(title='Matilda')
Book.dahl_objects.count()