Django的models实现分析彩民之家论坛9066777

2019-10-12 20:10 来源:未知

元类是python高阶语法. 合理的应用能够减小大气重复性的代码.

2      数据校验

 

1.1     神奇的Django中的models

咱俩先来看一段在Django项目中常用的代码:

安装数据库models代码:

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

此处有多少个美妙的地点,涉及到了python中最神秘的多少个特点。

先看下有哪些神奇的地点:

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

此地用的是python的八个语法特性:

  • 呈报符公约
  • 元类

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

python元类:**type()   **

2.1     数据校验难题

Python就算是强类型的脚本语言,不过在概念变量时却力不能够支钦点变量的档案的次序。

比如说,大家在Student类中定义多个age字段,合法值通常为包涵0的正整数,但是在python中无正整数的门类,只好本身来校验。

class Student:
    def __init__(self, name, age):
        if isinstance(name,str):
            self.name = name
        else:
            raise TypeError("Must be a string")

        if isinstance(int, age):
            self.age = age
        else:
            raise TypeError("Must be an int")

 

不过,假使更新岁龄时就能够遇见难点,无法重用校验逻辑。

有未有简短的议程吧?

 

3.2     版本二

不用输入变量名称。

彩民之家论坛9066777 1彩民之家论坛9066777 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值。顾客阅览这几个时,不能判别是万分变量出了难点。

 

元类实际上做了以下三地方的干活:

4      使用元类

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

为什么使用元类?

 

怎么要动用元类这种歪曲且便于出错的效应?
平日景观下,大家并不会利用元类,99%的开采者并不会用到元类,所以日常不要缅怀那个标题。
元类主用用于创制API,一个头名的例证正是Django的ORM。
它让大家得以如此定义三个类:

 

class Person(models.Model):
  name = models.CharField(max_length=30)
  age = models.IntegerField()

 

运营上边包车型客车代码:

guy = Person(name='bob', age='35')
print(guy.age)

回去的结果是int品种并非IntegerField对象。那是因为models.Model应用了元类,它会将Python中定义的字段转变来数据库中的字段。
通过运用元类,Django将复杂的接口转变来轻易的接口。

 

原型:type(类名,基类元组(可以为空,用于继承), 包含属性或函数的字典)

 以下二种写法都能够:

type('Class',(object,),dict(hello=fun()))

type('Class',(object,),{"hello":fun()})

1、class 自定义的类名称

2、(object,)是承继类,的元组,若是独有一个就写这种形势(object,);五个(object,xxxx,)

3、dict(hello=fun()) 或 {"hello":fun()} 第七个参数,是一个字典等号左是 自定义的不二等秘书籍名,侧边是已写好的情势名,那几个要注意,有参数且尚未暗中同意值的处境下,要加括号;

 

def fun():
    print('hello world!')


if __name__=="__main__":

    h = type('Hello',(object,),dict(hello=fun()))
    tc = h()
    tc.hello

 

引用:

h 也就是收到Hello类;tc = h()实例化类;tc.hello方法,调用的其实是我们定义的fun方法。

    Hello = type('Hello',(object,),dict(hello=fun()))
    tc = Hello()
    tc.hello

 type()动态创立类后,还足以增进越多的不二秘诀和性质:

def mysql():
    conn = pymysql.connect(host='127.0.0.1',port=3306 ,user='root' ,passwd='q123456' ,db='amsql' )
    cur = conn.cursor()
    sql = "SELECT * FROM amt_case_interface_table"
    ret = cur.execute(sql)
    print(cur.fetchmany(3))
    #conn.commit()

    cur.close()
    conn.close()

Hello.mysql = mysql()

调用:

tc.mysql

 

Linux and python学习交换1,2群已满.

Linux and python学习调换3群新开,应接参与,一同学习.qq 3群:563227894

不前进,不倒退,截至的场地是未曾的.

一只发展,与君共勉,

 

3      Python描述符

叙述符提供了高尚、简洁、强健和可采用的缓和方案。简单的讲,二个描述符便是二个指标,该指标表示了三个性情的值。

那就象征一旦贰个Student对象有八个属性“name”,那么描述符就是另二个能够用来代表属性“name”持有值的指标。

陈述符合同中“定义了__get__”、“__set__”或”__delete__” 那些特出格局,描述符是完毕此中三个或多少个主意的指标。

  • 干预成立类的历程
  • 修改类
  • 回到修改之后的类

5.1     属性读取顺序

由此实例读取属性时,常常再次回到的是实例中定义的性质。读取顺序如下:          

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

先记住这些顺序,前边通晓描述要求。属性描述符都以概念在类中的,并不是在对象中。

 

6  其余案例

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

7      参照他事他说加以考察资料

编号

标题

链接

1

元类

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

2

描述符

http://python.jobbole.com/81899/

3

《流畅的python》

元类部分

5      原驾驭释

4.2     版本四—模仿django的models

模仿Django的models实现:

彩民之家论坛9066777 3彩民之家论坛9066777 4

import abc

class NameProperty:
    index = 0

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

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

    def __set__(self, instance, value):
        # instance.__dict__[self.storage_name] = value
        setattr(instance, self.storage_name, value)


class Validated(abc.ABC, NameProperty):
    def __set__(self, instance, value):
        value = self.validate(instance, value)
        super().__set__(instance, value)

    @abc.abstractclassmethod
    def validate(self, instance, value):
        """return validated value or raise ValueError"""


class ChartField(Validated):
    def validate(self, instance, value):
        if not isinstance(value, str):
            raise TypeError("{} must be str".format(self.storage_name))
        return value


class IntegerField(Validated):
    def __init__(self, min_value=None):
        self.min_value = min_value

    def validate(self, instance, value):
        if not isinstance(value, int):
            raise TypeError("{} must be int".format(self.storage_name))
        if self.min_value and value < self.min_value:
            raise ValueError("{} must larger min_value".format(self.storage_name))
        return value


class EntityMeta(type):
    def __init__(cls, name, bases, attr_dict):
        super().__init__(name, bases, attr_dict)
        for key, attr in attr_dict.items():
            if isinstance(attr, Validated):
                type_name = type(attr).__name__
                attr.storage_name = "{} property {}".format(type_name, key)


class Entity(metaclass=EntityMeta):
    pass


class Student(Entity):
    name = ChartField()
    age = IntegerField(min_value=0)
    nicky_name = ChartField()

    def __init__(self, name, age, nicky_name):
        self.name = name
        self.age = age
        self.nicky_name = nicky_name

    def __str__(self):
        return self.name

s2 = Student("test", 12, "toddy")
s2.age = -1
print(s2.nicky_name)
s2.nicky_name = 4444

View Code

 

推行结果:

 

raise ValueError("{} must larger min_value".format(self.storage_name))

ValueError: IntegerField property age must larger min_value 

 

 

那般,完全效仿了models的定义。

类的早先化和三番七回属性赋值,都会自行调用__set__来安装并校验。

 

4.1     版本三

运用元类来调控类的作为:

彩民之家论坛9066777 5彩民之家论坛9066777 6

class NameProperty:
    index = 0

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

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

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


class EntityMeta(type):
    def __init__(cls, name, bases, attr_dict):
        super().__init__(name, bases, attr_dict)
        for key, attr in attr_dict.items():
            if isinstance(attr, NameProperty):
                type_name = type(attr).__name__
                attr.storage_name = '{} property {}'.format(type_name, key)


class Student(metaclass=EntityMeta):
    name = NameProperty()
    age = None
    nicky_name = NameProperty()

    def __str__(self):
        return self.name

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

s2 = Student()
s2.name = "test"
s2.nicky_name = 4444
print(s2)
print(s2.nicky_name)

View Code

 

进行输出为:

 

raise TypeError("%s must be string" % self.storage_name)

TypeError: NameProperty property nicky_name must be st

 

语法解释:

本子三对照版本二,最大的更改在于Student类继承了自定义元类EntityMeta。

一经对于python面向对象编制程序有通晓的话,python的具有类都承袭自type,type是全体类的元类。。

在那处,我们自定义的元类EntityMeta,具有一个效果便是判别类属性是或不是为NameProperty类型,借使为这么些连串,则那一个类其他实例属性storage_name值赋值为类名和属性名

 

 

5.2     描述符

某些类,只尽管中间定义了点子 __get__, __set__, __delete__ 中的贰个或七个(set,delete必需有一个),就足以称作描述符。

办法的原型为:

  ① __get__(self, instance, owner)

  ② __set__(self, instance, value)

  ③ __del__(self, instance)

 

汇报符只绑定到类上,在实例上不见效。

汇报的调用实质为:type(objectA).__dict__[“key”].__get__(None, objectB),objectB为描述符,objectA为定义类。

3.1     版本一

 

彩民之家论坛9066777 7彩民之家论坛9066777 8

 1 class NameProperty:
 2     def __init__(self, name=""):
 3         self.name = name
 4 
 5     def __get__(self, instance, owner):
 6         if instance is None:
 7             return self
 8         return instance.__dict__.get(self.name)
 9 
10     def __set__(self, instance, value):
11         if not isinstance(value, str):
12             raise TypeError("name must be string")
13         instance.__dict__[self.name] = value
14         
15 
16 class Student:
17     name = NameProperty('name')
18     age = None
19     heghth = None
20     weight = None
21 
22     def __init__(self, name):
23         self.name = name
24 
25     def __str__(self):
26         return self.name
27 
28     @property
29     def age(self):
30         return self.age
31 
32     @age.setter
33     def age(self, value):
34         if not isinstance(value, int):
35             raise ValueError("must be int")
36         self.age = value
37 
38 s = Student("Stitch")
39 print(s)
40 s.name = 'name'
41 print(s.name)

View Code

 

以此版本存在二个主题材料,便是name = NameProperty("sss"),必得安装贰个称谓,才足以利用。那一个与大家接纳django的models时不太一致,在运用models时,不写参数也能够的。

 

2.2     使用property装饰器

使用property也是二个方式,能够本着每一个属性来安装,可是借使二个类有多少个天性,代码就能够不菲,而且发生大批量的冗余,就像那样。

彩民之家论坛9066777 9彩民之家论坛9066777 10

class Student:
    def __init__(self, name, age, class_no, address, phone):
        self._name = None
        self._age = None
        self.__class_no = None
        self._address = None
        self._phone = None

        self.name = name
        self.age = age
        self.class_no = class_no
        self.address = address
        self.phone = phone

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, value):
        if not isinstance(value, str):
            raise ValueError("Must be string")
        self._name = value

    @property
    def age(self):
        return self._age

    @age.setter
    def age(self, value):
        if isinstance(value, int) and value > 0:
            self._age = value
        else:
            raise ValueError("age value error")

    @property
    def address(self):
        return self._address

    @address.setter
    def address(self, value):
        if not isinstance(value, str):
            raise ValueError("Must be string")
        self._address = value

View Code

 

代码冗余太多,各个检查str的都要复制叁遍代码。

1      引子

5.3     元类

元类,便是成立类的类。常常类都三回九转自object类,暗许会成立一些艺术。

元类决定了类出最早化后有怎样特色和行为。如若我们想自定义四个类,具备某种特殊的一举一动,则要求自定义元类。

  • 类也是目的,全部的类都以type的实例
  • 元类(Meta Classes)是类的类
  • __metaclass__ = Meta 是 Meta(name, bases, dict) 的语法糖
  • 能够通过重载元类的 __new__ 方法,修改定义的一颦一笑

 

TAG标签: Python Django
版权声明:本文由彩民之家高手论坛发布于编程技术,转载请注明出处:Django的models实现分析彩民之家论坛9066777