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__':
[
例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__':
[
例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':
[
例:
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__':
3 {}
4
5
###A init###
1
2 ('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
使用元类改造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
{'__doc__': None, '__primary__': [IntField
{'__doc__': None, '__module__': '__main__', '__dict__':
引擎类:
实体类没有提供数据库连接,它也不应该提供,实例类就应只完成表和类的映射;
提供一个数据库的包装类:
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
{'save':
{'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框架
当前路径:http://scjbc.cn/article/ipggih.html