Contents
某些情况下,我们需要对model进行自定义扩展,如增加某些方法,或者某些需要计算才能得出的字段。这个时候我们可以进行model的扩展,扩展大致有以下几种实现方式:
1.代理model (Proxy models)的方式
如果你想扩展model的方法,或者更改其排序方式或者获取哪些字段,可以使用代理,只更改model 在 Python 层的行为实现。比如:更改默认的 manager ,或是添加一个新方法。
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
class OrderedPerson(Person):
class Meta:
ordering = ["last_name"]
proxy = True
def get_max_age():
pass
# 原来:
p = Person.objects.all()
# 更改后:
p = OrderedPerson.objects.all()
如上,新定义一个OrderedPerson类,继承自原Person类,并且指定proxy=True之后,OrderedPerson就成为了一个代理类,可以更改排序方式,或者新增特定的方法
- 优点:做到最少的侵入,不破坏任何原有model的格式。
-
缺点:使用上会有一些限制
2.使用一对一映射的方式
通过接受django的singals,监测user这个model的变化,同步更新Profile这个model
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.TextField(max_length=500, blank=True)
location = models.CharField(max_length=30, blank=True)
birth_date = models.DateField(null=True, blank=True)
@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
3.使用AbstractBaseUser抽象基类进行扩展
如果你想把某些公共信息添加到很多 model 中,抽象基类就显得非常有用。你编写完基类之后,在 Meta内嵌类中设置 abstract=True,该类就不能创建任何数据表。
然而如果将它做为其他 model 的基类,那么该类的字段就会被添加到子类中。抽象基类和子类如果含有同名字段,就会导致错误,Django 将抛出异常。
class CommonInfo(models.Model):
name = models.CharField(max_length=100)
age = models.PositiveIntegerField()
class Meta:
abstract = True
class Student(CommonInfo):
home_group = models.CharField(max_length=5)
CommonInfo不能做为普通的 Django model 使用,因为它是一个抽象基类。他即不生成数据表,也没有 manager ,更不能直接被实例化和保存。
很多应用来说,这种继承方式正是你想要的。它提供一种在 Python 语言层级上提取公共信息的方式,但在数据库层级上,每个子类仍然只创建一个数据表,在JPA中称作TABLE_PER_CLASS。
这种方式下,每张表都包含具体类和继承树上所有父类的字段。因为多个表中有重复字段,从整个继承树上来说,字段是冗余的。
4.使用Manager方法
例如,我们为Book模型定义了一个title_count()方法,它需要一个关键字,返回包含这个关键字的书的数量。 (这个例子有点牵强,不过它可以说明managers如何工作。)
# models.py
from django.db import models
# ... Author and Publisher models here ...
class BookManager(models.Manager):**
def title_count(self, keyword):**
return self.filter(title__icontains=keyword).count()**
class Book(models.Model):
title = models.CharField(max_length=100)
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher)
publication_date = models.DateField()
num_pages = models.IntegerField(blank=True, null=True)
**objects = BookManager()**
def __unicode__(self):
return self.title
有了这个manager,我们现在可以这样做:
>>> Book.objects.title_count('django')
4
>>> Book.objects.title_count('python')
18
5.自定义的modelproxy
# 基本类:
class Order(models.Model):
name= models.CharField(max_length=100, null=True, blank=True)
address= models.CharField(max_length=200, null=True, blank=True)
# 使用proxy:
class OrderProxy(ModelProxy):
@classmethod
def get_order_list(cls):
try:
order_list = Order.objects.all()
except Exception:
raise Exception('find exception when get all order_list ')
return map(lambda x: cls(x), order_list )
# Proxy
class ModelProxy(object):
__slots__ = ['_model_object']
def __init__(self, _obj):
if not isinstance(_obj, models.Model):
raise RuntimeError(
'This class can only proxy class that '
'inherit from django.db.models.Model.'
)
object.__setattr__(self, '_model_object', _obj)
def __getattribute__(self, attr_name):
try:
return super(ModelProxy, self).__getattribute__(attr_name)
except AttributeError as e:
try:
return getattr(self._model_object, attr_name)
except AttributeError:
raise e
def __setattr__(self, attr_name, attr_value):
if hasattr(self._model_object, attr_name):
self._setattr_hook(attr_name, attr_value, True)
else:
super(ModelProxy, self).__setattr__(attr_name, attr_value)
def _setattr_hook(self, key, val, save=False):
setattr(self._model_object, key, val)
if save:
self._model_object.save(update_fields=[key])
def setvals(self, **kwargs):
for k, v in kwargs.items():
if not hasattr(self._model_object, k):
raise AttributeError(
'<0>Unknown proxy attribute {1}'.format(
self.__class__.__name__, k)
)
map(lambda (k, v): self._setattr_hook(k, v), kwargs.items())
self._model_object.save(update_fields=kwargs.keys())
def _get_proxy_obj(self):
return self._model_object
def _update_proxy_obj(self, value):
object.__setattr__(self, '_model_object', value)
说点什么
欢迎讨论