django中扩展model的几种方式(Manager及Proxy)

某些情况下,我们需要对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)

Written by

说点什么

欢迎讨论

avatar

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据

  Subscribe  
提醒