60数据库5_元编程_ORM框架

 

创新互联公司专注于网站建设,为客户提供成都网站建设、成都网站设计、网页设计开发服务,多年建网站服务经验,各类网站都可以开发,成都品牌网站建设,公司官网,公司展示网站,网站设计,建网站费用,建网站多少钱,价格优惠,收费合理。

 

目录

meta  programming,元编程:...1

ORM:...5

字段类的实现:...5

session类的实现:...8

自定义Model类的实现:...9

使用元类改造Model:...10

引擎类:...12

总结:...13

 

 

 

meta programming,元编程:

概念来自LISP和smaltalk;

我们写程序是直接写代码,是否能用代码来生成未来我们需要的代码?这就是元编程;

用来生成代码的程序称为元程序meta program,编写这种程序就称为元编程meta programming;

py能通过反射实现元编程(元语言);

在框架中用的多;

 

 

type类:

type与object纠缠在一起,关系复杂;

所有自定义的类都是type的子类,即type类的实例;

 

class type(object):

    def __init__(cls, what, bases=None, dict=None): # known special case of type.__init__   #what类名字,bases继承列表,dict类对象(类属性字典),这些与class定义的类相关

        """

        type(object_or_name, bases, dict)   #借助type构造任何类,用代码来生成代码即元编程

        type(object) -> the object's type   #返回对象的类型,如type(10)

        type(name, bases, dict) -> a new type   #返回一个新的类型

        # (copied from class doc)

        """

        pass

 

总结:

元类是制造类的工厂,是生成类的类;

定义一个元类,需要使用type(name,bases,dict),也可以继承type;

构造好元类,就可以在类定义时使用关键字参数metaclass指定元类,可使用最原始的metatype(name, bases, dict)方式构造一个元类;

元类的__new__()方法中,可获取元类信息、当前类、基类、类变量信息;

元编程一般用于框架开发中;

开发中,除非明确知道自己在干什么,否则不要随便使用元编程,99%的情况下用不到元类,可能有些程序员一辈子都不会使用元类;

Django、SQLAlchemy使用了元类,这样我们使用起来很方便;

 

 

例:

XClass = type('myclass', (object,), {'a': 100, 'b': 'string'})

print(XClass)

print(type(XClass))

print(XClass.__dict__)

print(XClass.mro())

输出:

{'b': 'string', '__dict__': , '__weakref__': , 'a': 100, '__module__': '__main__', '__doc__': None}

[, ]

 

例1:

def __init__(self):

    self.x = 100

 

def show(self):

    print(self.__dict__)

 

XClass = type('myclass', (object,), {'a': 100, 'b': 'string', '__init__': __init__, 'show': show})

print(XClass)

print(type(XClass))

print(XClass.__dict__)

print(XClass.mro())

输出:

{'a': 100, '__weakref__': , '__dict__': , 'b': 'string', 'show': , '__module__': '__main__', '__doc__': None, '__init__': }

[, ]

 

例2:

同例1;

class MyClass:

    def __init__(self):

        print('init')

 

    def show(self):

        print('show')

 

print(MyClass)

print(type(MyClass))

print(MyClass.__dict__)

print(MyClass.mro())

输出:

{'show': , '__module__': '__main__', '__doc__': None, '__dict__': , '__weakref__': , '__init__': }

[, ]

 

例:

class XMeta(type):

    # pass

    def __new__(cls, *args, **kwargs):   #__new__()可用作拦截;cls,不是普通的类,而是与type类等同,称为元类;参数(cls,*args,**kwargs)等同于(cls,what,bases,dict)

        print(1, cls)   #打印元类

        print(2, args)   #what,打印三元组(what,bases,dict)

        print(3, kwargs)   #bases,{}

        print(4, super())   #dict

        print(5, type(cls))

        return super().__new__(cls, *args, **kwargs)

        # return 1

        # return super().__new__(cls, what, bases, dict)

 

    # def __new__(cls, name, bases, dict):

    #     print(1, cls)

    #     print(2, name)

    #     print(3, bases)

    #     print(4, dict)

    #     return super().__new__(cls, name, bases, dict)

 

print(XMeta)

print(type(XMeta))

print(XMeta.__dict__)

# print(XMeta.mro())

 

class A(metaclass=XMeta):   #方式一,通过metaclass关键字指定元类,此处不是继承而是替换元类

    # pass

    id = 100

    def __init__(self):

        print('###A init###')

 

A()

print(A)

print(type(A))

 

class B(A):   #方式二,通过继承方式得到元类

    # pass

    def __init__(self):

        super().__init__()

        print('###B init###')

 

B()

 

C = XMeta('tom', (), {})   #方式三,用元类构建其它类

 

print(type(A), type(B))

输出:

{'__doc__': None, '__module__': '__main__', '__new__': }

1

2 ('A', (), {'__init__': , '__module__': '__main__', '__qualname__': 'A', 'id': 100})

3 {}

4 , >

5

###A init###

1

2 ('B', (,), {'__init__': , '__module__': '__main__', '__qualname__': 'B'})

3 {}

4 , >

5

###A init###

###B init###

1

2 ('tom', (), {})

3 {}

4 , >

5

 

 

 

ORM:

object relation map,对象关系映射,对象和关系之间的映射,使用面向对象的方式来操作DB;

关系模型和py对象之间的映射;

 

table-->class   #表映射为类

row-->object   #行映射为实例

column-->property   #字段映射为类属性

 

例:

实现ORM框架;

表由字段构成,表对应类,类属性对应字段;

 

 

字段类的实现:

字段特征有:name,column,type,pk,uk(unique key),index,nullable,default,auto_increment,所以字段可用类来描述;

字段类要提供对数据的校验功能,如声明字段是int类型,应要判断数据是不是整型;

字段有多种类型,不同类型有差异,使用继承的方式实现;

字段现定义为类属性,而这个类属性适合使用类来描述,即描述器;

 

例:

class Field:

    def __init__(self, name, column=None, pk=False, unique=False, index=False, nullable=True, default=None):

        self.name = name

        if column is None:

            self.column = name

        else:

            self.column = column

        self.pk = pk

        self.unique = unique

        self.index = index

        self.nullable = nullable

        self.default = default

 

    def validate(self, value):

        raise NotImplementedError

 

    def __get__(self, instance, owner):

        if instance is None:

            return self

        return instance.__dict__[self.name]

 

    def __set__(self, instance, value):

        self.validate(value)

        instance.__dict__[self.name] = value

 

    def __str__(self):

        return '{} <{}>'.format(self.__class__.__name__, self.name)

 

    __repr__ = __str__

 

class IntField(Field):

    def __init__(self, name, column=None, pk=False, unique=False, index=False, nullable=True, default=None, auto_increment=False):

        super().__init__(name, column, pk, unique, index, nullable, default)

        self.auto_increment = auto_increment

 

    def validate(self, value):

        # pass

        if value is None:

            if self.pk:

                raise TypeError('{} is pk, not None'.format(self.name))

            if not self.nullable:

                raise TypeError('{} required'.format(self.name))

        else:

            if not isinstance(value, int):

                raise TypeError('{} should be integer'.format(self.name))

 

class StringField(Field):

    def __init__(self, name, column=None, pk=False, unique=False, index=False, nullable=True, default=None, length=False):

        super().__init__(name, column, pk, unique, index, nullable, default)

        self.length = length

 

    def validate(self, value):

        # pass

        if value is None:

            if self.pk:

                raise TypeError('{} is pk, not None'.format(self.name))

            if not self.nullable:

                raise TypeError('{} required'.format(self.name))

        else:

            if not isinstance(value, str):

                raise TypeError('{} should be string'.format(self.name))

            if len(value) > self.length:

                raise ValueError('{} is too long'.format(value))

 

例:

Student类的操作对应表的CRUD操作,若使用pyMySQL,应用cursor对象的execute方法;

增加、修改数据自定义为save()方法,数据库连接从外部传入,这应是一个全局变量;

class Student:

    id = IntField('id', 'id', True, nullable=False, auto_increment=True)

    name = StringField('name', nullable=False, length=64)

    age = IntField('age')

 

    def __init__(self, id, name, age):

        self.id = id

        self.name = name

        self.age = age

 

    def __str__(self):

        return 'Student({}, {}, {})'.format(self.id, self.name, self.age)

 

    __repr__ = __str__

 

    def save(self, conn:pymysql.connections.Connection):

        sql = 'insert into student (id, name, age) values (%s, %s, %s)'

        with conn as cursor:

            cursor.execute(sql, (self.id, self.name, self.age))

 

 

session类的实现:

每一次数据库操作都是在一个会话中完成,将cursor的操作封装到会话中;

 

例:

class Session:

    def __init__(self, conn: pymysql.connections.connection):

        self.conn = conn

        self.cursor = None

 

    def execute(self, query, *args):

        if self.cursor is None:

            self.cursor = self.conn.cursor()

        self.cursor.execute(query, args)

 

    def __enter__(self):

        self.cursor = self.conn.cursor()

        return self

 

    def __exit__(self, exc_type, exc_val, exc_tb):

        self.cursor.close()

        if exc_type:

            self.conn.rollback()

        else:

            self.conn.commit()

 

class Student:

    id = IntField('id', 'id', True, nullable=False, auto_increment=True)

    name = StringField('name', nullable=False, length=64)

    age = IntField('age')

 

    def __init__(self, id, name, age):

        self.id = id

        self.name = name

        self.age = age

 

    def __str__(self):

        return 'Student({}, {}, {})'.format(self.id, self.name, self.age)

 

    __repr__ = __str__

 

    def save(self, session: Session):

        sql = 'insert into student (id, name, age) values (%s, %s, %s)'

        session.execute(sql, self.id, self.name, self.age)

 

 

自定义Model类的实现:

Student这样的类,若多建几个,可发现千篇一律,每一个这样的类,得定义一个名称,对应不同的表,都要先定义好类属性,再__init__()初始化值,而这些值正好是定义好的类属性;

CRUD操作也一样;

 

设计一个Model类,增加一个__table__类属性用来保存不同的表名;

 

例:

class Model:

    def save(self, session: Session):

        names = []

        values = []

 

        for k, v in self.__class__.__dict__.items():

            if isinstance(v, Field):

                if k in self.__dict__.keys():

                    names.append(k)

                    values.append(v)

 

        query = 'insert into {} ({}) values({})'.format(

            self.__table__,

            ','.join(names),

            ','.join(['%s']*len(values)))

        print(query)

        print(values)

 

class Student(Model):

    __table__ = 'student'

    id = IntField('id', 'id', True, nullable=False, auto_increment=True)

    name = StringField('name', nullable=False, length=64)

    age = IntField('age')

 

    def __init__(self, id, name, age):

        self.id = id

        self.name = name

        self.age = age

 

    def __str__(self):

        return 'Student({}, {}, {})'.format(self.id, self.name, self.age)

 

    __repr__ = __str__

 

s = Student(1, 'tom', 20)

s.save(None)

输出:

insert into student (name,age,id) values(%s,%s,%s)

[StringField , IntField , IntField ]

 

 

使用元类改造Model:

通过元类编程,增加了mapping类变量,里面存储着已经过滤出来的,字段类变量名=>Field对象映射;

例:

class ModelMeta(type):

    def __new__(cls, name, bases, attrs: dict):   #name类名,attrs类属性字典

        if '__table__' not in attrs.keys():

            attrs['__table__'] = name   #默认添加表名为类名

 

        mapping = {}   #方便以后查询属性名和字段实例

        primarykey = []

        for k, v in attrs.items():   #k,类变量名称字符串;v,对象

            if isinstance(v, Field):

                print(k, v)

                v.name = k

                if v.column is None:

                    v.column = k   #没有给字段名

 

                mapping[k] = v

 

                if v.pk:

                    primarykey.append(v)

 

        attrs['__mapping__'] = mapping   #增加属性

        attrs['__primary__'] = primarykey

 

        return super().__new__(cls, name, bases, attrs)

 

class Model(metaclass=ModelMeta):

    def save(self, session: Session):

        names = []

        values = []

 

        for k, v in self.__class__.__dict__.items():

            if isinstance(v, Field):

                if k in self.__dict__.keys():

                    names.append(k)

                    values.append(v)

 

        query = 'insert into {} ({}) values({})'.format(

            self.__table__,

            ','.join(names),

            ','.join(['%s']*len(values)))

        print(query)

        print(values)

        # session.execute(query, *values)

 

class Student(Model):

    __table__ = 'student'   #有了元类,此句可省

    id = IntField('id', 'id', True, nullable=False, auto_increment=True)

    name = StringField('name', nullable=False, length=64)

    age = IntField('age')

 

    def __init__(self, id, name, age):

        self.id = id

        self.name = name

        self.age = age

 

    def __str__(self):

        return 'Student({}, {}, {})'.format(self.id, self.name, self.age)

 

    __repr__ = __str__

 

s = Student(1, 'tom', 20)

s.save(None)

print(Student.__dict__)

print(Model.__dict__)

输出:

insert into student (id,age,name) values(%s,%s,%s)

[IntField , IntField , StringField ]

{'__doc__': None, '__primary__': [IntField ], 'id': IntField , '__init__': , '__table__': 'student', '__repr__': , '__module__': '__main__', 'age': IntField , '__str__': , 'name': StringField , '__mapping__': {'id': IntField , 'age': IntField , 'name': StringField }}

{'__doc__': None, '__module__': '__main__', '__dict__': , '__weakref__': , '__primary__': [], '__table__': 'Model', 'save': , '__mapping__': {}}

 

 

 

引擎类:

实体类没有提供数据库连接,它也不应该提供,实例类就应只完成表和类的映射;

提供一个数据库的包装类:

1、负责数据库连接;

2、负责CRUD操作,取代实体类的CRUD方法;

 

例:

class Engine:

    def __init__(self, *args, **kwargs):

        self.conn = pymysql.connect(*args, **kwargs)

 

    def save(self, instance: Student):

        names = []

        values = []

        for k, v in instance.__mapping__.items():

            names.append('`{}`'.format(k))

            values.append(instance.__dict__[k])

 

        query = "insert into {} ({}) values ({})".format(

            instance.__table__,

            ','.join(names),

            ','.join(['%s']*len(values))

            )

        print(query)

        print(values)

 

        # with self.conn as cursor:

        #     with cursor:

        #         cursor.execute(query, values)

 

s = Student(1, 'tom', 20)

# s.save(None)

print(Student.__dict__)

print(Model.__dict__)

print(s.__dict__)

 

engine = Engine('10.113.129.2', 'root', 'rootqazwsx', 'test1')

engine.save(s)

输出:

id IntField

age IntField

name StringField

{'__primary__': [IntField ], '__doc__': None, '__repr__': , 'name': StringField , '__table__': 'student', '__init__': , '__module__': '__main__', '__str__': , 'id': IntField , 'age': IntField , '__mapping__': {'id': IntField , 'age': IntField , 'name': StringField }}

{'save': , '__dict__': , '__module__': '__main__', '__doc__': None, '__mapping__': {}, '__primary__': [], '__weakref__': , '__table__': 'Model'}

{'id': 1, 'name': 'tom', 'age': 20}

insert into student (`id`,`age`,`name`) values (%s,%s,%s)

[1, 20, 'tom']

 

 

总结:

这是一个ORM框架的雏形,从这个例子就可以明白ORM框架的内部原理;

学习一个ORM框架:

1、看Model类如何描述表、属性和字段如何映射;

2、增删改查方法调用如何转化为SQL语句并执行;

 

 

 

 

 


当前文章:60数据库5_元编程_ORM框架
链接URL:http://myzitong.com/article/ipggih.html