Django的models完结分析

  • 干预创立类的经过
  • 修改类
  • 回去修改今后的类

1.1     神奇的Django中的models

大家先来看1段在Django项目中常用的代码:

安装数据库models代码:

class Students(models.Model):
    name = models.CharField()
    age = models.IntegerField()

那边有几个神奇的地点,涉及到了python中最隐私的几个天性。

先看下有何样神奇的地点:

  • 字段名称name\age自动转换为了数据库中的字段名称
  • 电动校验数据类型,models.IntegerField(),会校验设置的数据类型

此间用的是python的多少个语法个性:

  • 讲述符协议
  • 元类

大家来一步一步解开神秘面纱。

1      引子

您恐怕感兴趣的作品:

  • Python中的Classes和Metaclasses详解

中 Meta Classes详解,pythonclasses 接触过
Django 的校友都应当丰硕熟悉它的 OQX5六M 系统。对于 python
新手而言,那是一项大概能够被称作“黑科…

 

3      Python描述符

讲述符提供了优雅、简洁、健壮和可采纳的消除方案。一句话来说,2个描述符正是1个指标,该指标表示了三特性质的值。

那就意味着1旦叁个Student对象有陆本性质“name”,那么描述符就是另一个可见用来代表属性“name”持有值的对象。

叙述符协议中“定义了__get__”、“__set__”或”__delete__”
这几个独特格局,描述符是完结当中3个或多少个措施的对象。

五.一     属性读取顺序

通超过实际例读取属性时,日常重回的是实例中定义的性质。读取顺序如下:          

  1. 实例属性
  2. 类属性
  3. 父类属性
  4. __getattr__()方法

先记住这些顺序,前边精晓描述要求。属性描述符都是概念在类中的,而不是在指标中。

Python 中 Meta Classes详解,pythonclasses

接触过 Django 的同学都应该十分熟习它的 OENCOREM 系统。对于 python
新手而言,那是一项差不多能够被称作“黑科技(science and technology)”的性格:只要您在models.py中不管定义一个Model的子类,Django
便得以:

  1. 取得它的字段定义,并转换到表结构
  2. 读取Meta内部类,并转化成相应的配备新闻。对于相当规的Model(如abstract、proxy),还要进行相应的转换
  3. 为未有定义objects的Model加上1个私下认可的Manager

开发之余,笔者也曾脑补过其幕后的规律。曾经,笔者以为是这么的:

运行时,遍历models.py中的全体属性,找到Model的子类,并对其进展上述的改动。
其时,笔者还以为自身触碰着了真理,并曾将其选拔到骨子里生产中——为 SAE 的 KVDB
写了二个类 O悍马H2M
系统。然则在完成的历程中,小编显然感受到了那种艺术的猥琐,而且质量并不理想(因为要遍历全体的概念模块)。

那么实际上,Django 是怎么落到实处的啊?

自古以来我们制造东西的方法都是“自上而下”的,是用切削、分割、组合的方法来制造。然而,生命是自下而上地,自发地建造起来的,这个过程极为低廉。
——王晋康 《水星播种》

那句话揭发了性命的神奇所在:真正的性命都以由基本物质自发组成的,而非造物主流水生产线式的加工。

那么,假使 类
也有生命来说,对它和谐的梳洗就不应该由调用者来成功,而相应是先性情的。

多亏,python 提供了上帝的接口——那就是 Meta Classes,恐怕叫做“元类”。

元类 是什么?

大约说:元类便是类的类。

率先,要有2个概念:

python 中,一切皆以对象。

没错,一切,包括 类 本身。

既然如此,类 是 对象,对象 是 类的实例,那么——类 也相应有 类 才对。

类的类:type

在 python 中,大家得以用type检查实验二个对象的类,如:

print type(1) # <type 'int'>

即使对一个类操作呢?

print type(int) # <type 'type'>

class MyClass(object): pass

print type(MyClass) # <type 'type'>

print type(type) # <type 'type'>

这表明:type其实是二个类型,全部类——包蕴type自个儿——的类都以type。

type 简介

从 官方文书档案 中,大家得以领略:

和 dict 类似,type 也是1个工厂构造函数,调用其将赶回
五个type类型的实例(即 类)。
type 有八个重载版本:

  • `type(object)`,即我们最常用的本子。
  • `type(name, bases, dict)`,二个越来越强大的版本。通过点名
    类名称(`name`)、父类列表(`bases`)和 属性字典(`dict`)
    动态合成1个类。

上面多个语句等价:

class Integer(int):

  name = 'my integer'

  def increase(self, num):
    return num + 1

  # -------------------

  Integer = type('Integer', (int, ), {
  'name': 'my integer',
  'increase': lambda self, num: \
          num + 1  # 很酷的写法,不是么
  })

也正是说:类的定义进程,其实是type类型实例化的经过。

而是那和修饰1个已定义的类有啥样关联吗?

本来有啦~既然“类的概念”就是“type类型的初阶化进程”,那里边必定会调用到type的构造函数(__new__()
或 __init__())。只要大家后续 type类 并修改其
__new__函数,在那之中入手脚就足以啦。

接下去我们将通过3个板栗感受 python
的黑魔法,不过以前,我们要先精晓二个语法糖。

__metaclass__ 属性

有没觉着上边第3段示例某些鬼畜呢?它勒令程序员将类的成员写成1个字典,几乎是反人类。若是大家确实是要经过改动
元类 来改变 类 的一言一动的话,就像就非得接纳那种措施了~~大致可怕~~

幸亏,python 贰.2 时引入了贰个语法糖:__metaclass__。

class Integer(int):

  __metaclass__ = IntMeta

前几天将会等价于:

Integer = IntMeta('Integer', (int, ), {})

由此一来,大家在动用守旧类定义的还要,也可以选择元类啦。

栗子:子类净化器

供给描述

您是3个有语言洁癖的开发者,平时容不得外人讲一句脏话,在支付时也是这么。以后,你写出了二个尤其棒的框架,并马上要将它公之于众了。但是,你的恐怖症又犯了:就算你的使用者在代码中写满了脏话,咋做?岂不是玷污了团结的高洁?
壹经你正是那个丧心病狂的开发者,你会怎么办?

在知情元类此前,你恐怕会无从入手。可是,这一个难题你可以用 元类
轻松解决——只要在类定义时过滤掉不到底的单词就好了(百度贴吧的干活~~)。

咱俩的元类看起来会是那样的:

sensitive_words_list = ['asshole', 'fuck', 'shit']

def detect_sensitive_words(string):
  '''检测敏感词汇'''
  words_detected = filter(lambda word: word in string.lower(), sensitive_words_list)

  if words_detected:
    raise NameError('Sensitive words {0} detected in the string "{1}".' \
      .format(
        ', '.join(map(lambda s: '"%s"' % s, words_detected)),
        string
      )
    )

class CleanerMeta(type):

  def __new__(cls, class_name, bases, attrs):
    detect_sensitive_words(class_name) # 检查类名
    map(detect_sensitive_words, attrs.iterkeys()) # 检查属性名

    print "Well done! You are a polite coder!" # 如无异常,输出祝贺消息

    return super(CleanerMeta, cls).__new__(cls, class_name, bases, attrs)
    # 重要!这行一定不能漏!!这回调用内建的类构造器来构造类,否则定义好的类将会变成 None
现在,只需这样定义基类:

class APIBase(object):

  __metaclass__ = CleanerMeta

  # ...
那么所有 APIBase 的派生类都会接受安全审查(奸笑~~):

class ImAGoodBoy(APIBase):

  a_polite_attribute = 1

# [Output] Well done! You are a polite coder!

class FuckMyBoss(APIBase):

  pass

# [Output] NameError: Sensitive words "fuck" detected in the string "FuckMyBoss".

class PretendToBePolite(APIBase):

  def __fuck_your_asshole(self):
    pass

# [Output] NameError: Sensitive words "asshole", "fuck" detected in the string "_PretendToBePolite__fuck_your_asshole".

看,固然像最终一个例证中的私有属性也难逃审查,因为它们本质皆以相同的。

依然,你还足以对有题指标属性实行私下的修改,比如
让不文明的函数在调用时打出一行警告 等等,那里就不多说了。

元类 在实质上开发中的应用

日常开支时,元类 常用吗?

本来,Django 的 O途锐M 便是五个例证,赫赫有名的 SQLAlchemy
也用了那种黑魔法。

别的,在有个别微型的库中,也有 元类 的人影。比如
abc(奇怪的名字~~)——那是 python 的三个内建库,用于模拟
抽象基类(Abstract Base Classes)。开发者能够动用 abc.abstractmethod
装饰器,将 钦定了 __metaclass__ = abc.ABCMeta 的类的章程定义成
抽象方法,同时那些类也成了
抽象基类,抽象基类是不可实例化的。那便达成了对 抽象基类 的模拟。

比方你也有亟待动态修改类定义的须求,不要紧也尝试那种“黑魔法”。

小结

  1. 类 也是 对象,全部的类都是type的实例
  2. 元类(Meta Classes)是类的类
  3. __metaclass__ = Meta 是 Meta(name, bases, dict) 的 语法糖
  4. 可以通过重载元类的 __new__ 方法,修改 类定义 的行为

元类实际上做了以下3上边的做事:

4      使用元类

元类是python的中2个难处,在多数景观下都不会用到。可是在编写框架方面却是必不可贫乏的利器。

3.2     版本二

不用输入变量名称。

图片 1图片 2

class NameProperty:
    index = 0

    def __init__(self):
        self.name = str(self.__class__.index)  # 使用类的变量
        self.__class__.index += 1

    def __get__(self, instance, owner):
        return getattr(instance, self.name)

    def __set__(self, instance, value):
        if not isinstance(value, str):
            raise TypeError("name must be string")
        instance.__dict__[self.name] = value


class Student:
    name = NameProperty()
    age = None

    def __str__(self):
        return self.name

s = Student()
s.name = "www"
print(s)

s2 = Student()
s2.name = "http"
print(s2)
print(s.name)

View Code

 

本条版本还设有八个难点,若是一个连串有三个字段使用了NameProperty时,错误提示时,不可能表示出此变量的称呼,只好表示出八个index值。用户看到这些时,不能判断是十三分变量出了难题。

 

 

七      参考资料

编号

标题

链接

1

元类

https://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python

2

描述符

解密 Python 的描述符(descriptor)

3

《流畅的python》

元类部分

二      数据校验

 

二      数据校验

陆  其余案例

Django的django-rest-framework框架的serializer 也是用的这一个语法达成的。

发表评论

电子邮件地址不会被公开。 必填项已用*标注