u库
ORM:对象关系映射
类 》》》 数据库的一张表
对象 》》》 表的一条记录
对象点属性 》》》 记录某一个字段对应的值
'''
对象就是一个容器,里面装着属性和方法,而类就是盛放对象的一堆属性和方法,对象点来获取属性和方法。表就是装着一堆记录数据,通过对象点属性去获取某一个字段对应的值
'''
'''
一个字段有字段名,字段类型,字段是否是主键,字段的默认值,通过类返回的对象去点这些属性
'''
the first step:
先将表中的字段的各个属性字段名,字段类型,字段是否是主键,字段的默认值存放在类中,对象可以通过点来获取到某一个字段对应的值
the second step:
针对字段类型来写类,字段类型就有字符串类型(varchar,char),int
先写一个可以定义varchar类型字段的类,再写定义一个整型字段的类
the thrid step:
定义三个类User,Notice,Movie,这三个类不能写__init__,用父类来写一个__init__,在实列化对象的时候,不管传多少个关键字实参我都可以满足,不同的对象传入的关键字实参不等,但都可以实例化被同一个父类实例化出来,能想到的是字典的实例化d=dict(),父类中就继承这个字典这个类就可以实现
the fourth step:
d=dict(name='json',password='123')
name='json',password='123'这些都是对象属性对应的值,并不是对象的属性,所以d.name就是报错,(dict.get('name')这是字典的取值方式)
'''
有双杆的都表示自动触发
'''
__getattr__:在对象获取它没有的属性和方法的时候自动触发
__setattr__:在对象点属性设置属性值的时候自动触发
getattr():根据字符串获取对象对应属性和方法
所以要在父类中加入def __getattr__和__setattr__来获取数据和修改数据
the fifth step:
一张表要有表名,一个主键,好多字段,说明这个类里面有表名,有字段,有主键,元类就是用来创建类,而现在的类就是对应的一张表,所以创建类就是创建表,在类创建之前就哟啊拦截这个类,将这些塞给这个类,才算是一张表
user,notice,movie三个类是就是三个表,三个表都是继承models,所以models中继承元类。
'''
当类名为models时,就不需要拦截,原本该怎么创建就怎么创建,因为models并不是一张表,只是在创表的那个类继承models的方法,也就是用到字典类传参的那个功能而定义的类,之所以要models继承这个元类是因为是所有表类继承的父类,为了图方便就直接继承
'''
'''
元类中的__new__就是造一个名称空间,为了拦截的是创建表格的创建,创建的空间需要类名和基类和类体。
'''
'''
创建user这个类的时候,右键运行,会先跳到元类中,而user类中的代码也就是元类中class_attrs中的属性代码,所以元类中的class_attrs中已包含user中的代码还有自己为class_attrs这个字典自加的代码
'''
orm.py
from mysql_singlenton import Mysql
class Field(object):
def __init__(self, name, column_type, primary_key, default):
self.name = name
self.column_type = column_type
self.primary_key = primary_key
self.default = default
# 先定义一个字符串类型,默认为varchar,默认主键为False,默认值为None
class StringField(Field):
def __init__(self, name, column_type='varchar(32)', primary_key=False, default=None):
# 调用父类的__init__
super().__init__(name, column_type, primary_key, default)
# 定义一个整型字段的类
class IntegerField(Field):
def __init__(self, name, column_type='int', primary_key=False, default=None):
super().__init__(name, column_type, primary_key, default)
class MyMetaclass(type):
def __new__(cls, class_name, class_bases, class_attrs):
# 我们定义的元类是用来拦截模型表的创建过程,
# 而models并不是一张模型表,所以不需要它的创建过程
if class_name == 'Models':
return type.__new__(cls, class_name, class_bases, class_attrs)
# 获取类体代码中的属性,如果没有表名,就用类名来作为表名
# 注意:get获取key,返回vaule,如果没有就返回None,如果写了第二个参数就返回第二个参数
table_name = class_attrs.get('table_name', class_name)
primary_key = None
# 只要是表的字段的,我都放在字典中去,就不需要一个个去拿,直接拿到一个字典就有所有的字段了
mappings = {}
for k, v in class_attrs.items():
# 拿出所有自己定义的表属性,过滤掉那个table_name
# v是对象,Field是类,判断这个对象是否属于这个类
if isinstance(v, Field):
mappings[k] = v
if v.primary_key:
# 以防创建两个主键
if primary_key:
raise TypeError('一张表只能有一个主键')
primary_key = k
for k in mappings.keys():
# 将单个的字段删除,for循环mappings拿到所有的字段
class_attrs.pop(k)
# 创建一个表有且要有一个主键,如果没有这个主键,那么就要报错
if not primary_key:
raise TypeError('一张表必须要有主键')
# 以下是将所有在名称空间中的属性数据都保存起来,调用父类的__new__进行保存到名称空间中
class_attrs['table_name'] = table_name
class_attrs['primary_key'] = primary_key
class_attrs['mappings'] = mappings
return type.__new__(cls, class_name, class_bases, class_attrs)
class Models(dict, metaclass= MyMetaclass):
def __init__(self, **kwargs):
super().__init__(**kwargs)
# self是字典的对象,
def __getattr__(self, item):
self.get(item, '没有该键') # 在没有这个item这个值的时候才会触发后面的‘没有该键’
def __setattr__(self, key, value):
try:
self[key] = value
except BaseException as e:
print(e)
@classmethod
def select(cls, **kwargs):
# 调用Mysql的查询方法
ms = Mysql.singleton()
# select * from %s
# 当没有参数传入,代表查询所有的数据
if not kwargs:
sql = 'select * from %s' % cls.table_name
res = ms.select(sql)
else:
k = list(kwargs.keys())[0]
v = kwargs.get(k)
sql = "select * from %s where %s=?" % (cls.table_name, k)
sql = sql.replace('?', '%s')
res = ms.select(sql, v)
if res:
print(res)
return [cls(**r) for r in res]
# select * from %s where %s=%s
if __name__ == '__main__':
class User(Models):
table_name = 'userinfo'
id = IntegerField(name='id', primary_key=True)
name = StringField(name='name')
rs = User.select(password='123')
print(rs)
class Notice(Models):
pass
class Movie(Models):
pass
mysql_singlenton.py
import pymysql
class Mysql(object):
_instance = None
def __init__(self):
self.conn = pymysql.connect(
host='127.0.0.1',
port=3306,
user='root',
password='123',
database='day41',
charset='utf8',
autocommit=True
)
self.cursor = self.conn.cursor(pymysql.cursors.DictCursor)
def close_db(self):
self.cursor.close()
self.conn.close()
def select(self,sql,args=None):
self.cursor.execute(sql,args)
# 注意一点:fetchall拿到的数据结构是一个列表套字典[{},{},{}]
res = self.cursor.fetchall()
return res
def execute(self, sql,args):
#insert into user(name,password) values('jason','123')
#update user set name='jason',password='456' where id=1
try:
self.cursor.execute(sql,args)
except BaseException as e:
print(e)
@classmethod
def singleton(cls):
if not cls._instance:
cls._instance = cls()
return cls._instance