前言
在上一篇文章中juejin.cn/post/729974…, 介绍了python元类的概念以及几个应用的比如,这次来介绍一下ORM以及解答之前的问题【为什么搜索元类应用,总是呈现ORM】
关于ORM
ORM是指目标联系映射(Object Relational Mapping,简称ORM),从全称上能够看出这是处理【目标】和【联系型数据库】之间的映射的技能。
咱们先来看看一个数据表:
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL,
email VARCHAR(100) NOT NULL,
birthdate DATE,
is_active BOOLEAN DEFAULT TRUE
);
当咱们进行查询或许刺进的时分,需求这么写:
# 查询
SELECT id, username FROM users;
# 刺进
INSERT INTO users (id, username) VALUES (1,"Tom");
能够发现,假如要进行数据库操作,需求拼接许多sql语句,这对于代码的保护和更新来说,将会增加许多工作量。
假如能够有一个办法,把面向目标和联系型数据结合起来,那将会便利很多。 比如,上述的操作,能够变成相似这样:
class User:
id = INT(auto_increment = True, primary_key = True)
username = VARCHAR(50)
email = VARCHAR(100)
# 创立数据表
create(User)
# 或许:
User.create()
# 查询
result = User.select().where(id = 1)
# 或许:
result = select(User).where(id = 1)
# 刺进
User.insert(id = 1, name = "Tom")
# 或许
insert(User(id = 1, name = "Tom"))
以上是举个比如,实践的ORM类库纷歧定是这样的风格。 首要是表达出ORM的观点:将目标和联系型数据库进行映射,目标的特点便是数据库的字段,目标的方法便是数据库的操作。
经过这样的映射,能够省去拼接sql语句,也能够进行更多杂乱的操作,例如数据校验等等。
* ORM的两种风格
具体介绍元类和ORM之前,先聊一下ORM的两种形式:active record 和 data mapper。
1.Active Record形式:
把数据模型和数据耐久化结合,每个描述数据表的模型都包含访问数据库的方法,能够直接操作数据库。例如上面的:
User.create()
result = User.select().where(id = 1)
在python中,Django的ORM,Peewee等便是运用active record形式。
2. Data Mapper形式:
这个形式把数据模型和数据耐久化分隔,由一个独立的数据映射来处理数据耐久化,数据模型只是担任业务逻辑。
例如上面的:
create(User)
result = select(User).where(id = 1)
SQLAlchemy便是运用data mapper形式。
这里不去讨论两种的好坏,只是简单介绍有这两种形式。后面的代码将经过active record来完结ORM.
元类的ORM的联系
在介绍了元类和ORM之后,二者到底有什么联系?
咱们看这样一个比如:
在一个user
表里面,写入id =1,username="Tom";id = 2, username = "Jack"
。
假如创立一个函数:
def insert_user(id:int,username:str):
sql = f'INSERT INTO user (id,username) VALUES ({id},{username})'
这将和拼接sql是没有不同的,并不是orm。 实践上咱们是不知道创立表的时分,会有什么字段,这些字段都是运用者自己创立的。 因而,咱们要有这样的映射:
【类】——>【数据表】
【类特点】——>【数据表字段】
【目标】——>【数据记录】
【目标特点值】——>【数据表字段值】
咱们期望运用的方法相似这样:
Class User:
# 类特点映射到数据表
id = IntegerField('id')
username = VarcharField('username')
# 实例化一条数据记录
user1 = User(id = 1, username = 'Tom')
# insert
user1.insert()
user2 = User(id = 2,username = 'Jack')
user2.insert()
能够看到,在创立User类的时分,需求进行字段映射,因而,这里便是运用元类的原因:【拦截和修正类的创立】,让运用者在界说类的时分,完结对数据表的界说;在实例化一个类的时分,完结一条数据记录的界说。
运用元类完结ORM
先完结字段类型的界说:
class Field:
def __init__(self, name):
self.name = name
class IntegerField(Field):
def __init__(self, name):
super().__init__(name)
self.type = 'int'
class VarcharField(Field):
def __init__(self, name):
super().__init__(name)
self.type = 'varchar(100)'
元类的界说:
class MetaModel(type):
def __new__(cls, name, bases, attrs):
# 排除Model类
if name == 'Model':
return type.__new__(cls, name, bases, attrs)
# 将类特点隔离,防止后续实例化被掩盖。 类界说了id=IntegerField(),实例化id = 1会因为同名被掩盖
mapper = {}
for k, v in attrs.items():
if isinstance(v, Field):
mapper[k] = v
for k in mapper:
attrs.pop(k)
attrs['__mapper__'] = mapper
if '__table__' not in attrs:
attrs['__table__'] = name
return type.__new__(cls, name, bases, attrs)
Model类的界说:
class Model(metaclass=MetaModel):
# 实例化设置具体值
def __init__(self, **kwargs):
for k, v in kwargs.items():
self.__setattr__(k, v)
def insert(self):
column_li = []
val_li = []
# 读取元类设置好的__mapper__
for k, v in self.__mapper__.items():
val = getattr(self, k, None)
if val is not None:
# 留意这个v.name,这是Field字段的name,实践数据表里面的字段名
column_li.append(v.name)
val_li.append(f'"{val}"')
print(column_li)
print(val_li)
sql = f'INSERT INTO {self.__table__} ({",".join(column_li)}) VALUES ({",".join(val_li)})'
print(sql)
@classmethod
def create(cls):
cloumn_li = []
for k, v in cls.__mapper__.items():
cloumn_li.append(f'{v.name} {v.type}')
sql = f'CREATE {cls.__table__} {",".join(cloumn_li)}'
print(sql)
调用状况:
class User(Model):
__table__ = 'user'
id = IntegerField('id')
username = VarcharField('username')
User.create()
user = User(id=1, username='tom')
user.insert()
输出:
CREATE user id int,username varchar(100)
['id']
['"1"']
INSERT INTO user (id) VALUES ("1")
元类首要做了几件事:
1.收集类特点,判别类特点是否是属于File(),也便是是否是数据表字段。将这些字段存入__mapper__,【防止实例化被具体数值掩盖】。
2.表名设置。经过对类特点的查看,设置表名。 因而,假如需求其他额定特点的设置,也能够在元类界说加入。
这便是核心的把类、目标和数据表进行映射。还能够加上其他操作方法、增加不同的数据库语法、字段查看等等。
总结
运用ORM能够便利的进行联系型数据库库操作,减少开发担负。要完结ORM的要点,便是如何把类和实例与联系型数据库进行映射。 python的元类,能够在拦截和修正类的创立,因而能够经过元类,在类的界说时,进行映射。
回到最初的问题:ORM必定要用元类来完结吗? 经过上述的内容能够得知,只要能够完结映射,就纷歧定需求用元类。 后续会展现不运用元类完结的ORM。