ORM是创建一个实例对象,用创建他的类名当做数据表名,用创建他的类属性对应数据表的字段,不需要在自己写复杂的sql语句,而是通过对实例对象的操作时,能让代码自动帮我们整理为对应的sql语句。
class User(父类):uid = ("uid", "int unsigned")name = ("username", "varchar(20)")password = ("password", "varchar(20)")...省略...
uid | username | password |
类似下图创建一个实例对象,把数据库的数据以参数入
u = User(nid=12345, name="laowang",password="123321")
通过调用某个方法,ORM自动帮我们整理为下面代码并执行:
insert into (uid,name,password) value (12345,"laowang","123321")
从而大大简化我们的工作,减少出错率!下面是完整代码(通过metaclass可以指定我们需要继承的元类):
class Mode_type(type):def __new__(cls, name, bases, attrs)mappings = dict()for k, v in attrs.items():if isinstanse(v, tuple):mappings[k] = v# 找到新字典接收完attrs的数据后,删掉attrs里的数据for k in mappings.keys[]:attrs.pop(k)# 将之前新字典保存的数据表的信息保存在attrs中attrs["__mappings__"] = mappings# name指向新创建的实例对象名,相当于保存数据表的名称attrs["__table__"] = name return type.__new__(cls, name, bases, attrs)class User(mateclass = Mode_type):uid = ("uid", "int unsigned")name = ("username", "varchar(20)")password = ("password", "varchar(20)")# 指定元类后,以上的类属性就不在类中,而是在__mapping__指定的字典中仓储# 类似于# __mapping__ = {# uid :("uid", "int unsigned")# name: ("username", "varchar(20)")# password :("password", "varchar(20)")# }# __table__ = "User"def __init__(self, **kwargs):# 取出字典里面的值for name, value in kwargs.items():setattr(self, name, value)# 这里不能用self.name = value ,这样只会让实例对象拥有name这个属性,而不是name形参背后真正替换的属性,所以用setattrdef save()# 数据表表头字段fields = []# 数据表字段对应的数据args = []for k, v in self.__mappings__.items():field.append(v[0])args.append(getattr(self, k, None))args_temp = list()# 区分参数的类型,防止写入数据表后报错for temp in args:if isinstence(temp, int):args_temp.append(str(temp)) # 类似temp = “123456”elif isinstence(temp, str):args_temp.append("""%s""" % temp) # 类似temp = """'123321'"""sql = "insert into %s (%s) value (%s)" % (self.__table__, ",".join(fields), ",".join(args_temp))# 下面就可以执行mysql的操作,只是说ORM,所以我只打印了这句话 print(sql)u = User(uid = 123456, name="laowang", password = "123321") u.save()
需要注意的点:
- Metaclass的父类:M
etaclass是类的模板,所以必须从`type`类型继承:
选择__new__函数作为实现"修改类"的函数:
- 函数__new__(cls, name,bases,attrs)中,"cls"类似于类中其他函数的self参数,例如__init__(self),只不过self代表创建的对象,而cls代表类本身(__init__作为实例初始化的函数,需要把实例本身作为参数传进去,这样我们才能保证被修改的是实例;同理,__new__函数需要把类本身作为参数传进去,才能保证被初始化的是当前类); name代表类的名称;bases代表当前类的父类集合;attrs代表当前类的属性,是狭义上属性和方法的集合,可以用字典dict的方式传入
- 其实我们看下用type创建一个类就很好理解这些参数了():
u = type('User', (object,),{uid:("uid", "int unsigned"),name :("username", "varchar(20)"),password:("password", "varchar(20)"})
- 对__new__的定义def __new__(cls, name,bases,attrs),实际上,“new”方法在Python中是真正的构造方法(创建并返回实例),通过这个方法可以产生一个”cls”对应的实例对象所以说”new”方法一定要有返回,要把创建的实例对象返回回去。在此,我们把对类的修改放到__new__方法中,然后返回修改过后的实例对象。另外,很简单的道理,选择type.__new__函数作为return的值,是因为我们的Mode_type继承自type,因此应该返回type的__new__函数创建的对象。