仿写简易orm

1.数据库连接池

 1from conf import setting
2import pymysql
3from DBUtils.PooledDB import PooledDB
4Pool = PooledDB(
5 creator=pymysql, # 连接使用的数据库模块
6 maxconnections=6, # 连接池允许的最大连接数
7 mincached=5, # 初始化时,连接池中至少创建的空闲连接,0表示不创建
8 maxcached=3, # 连接池中最多的闲置连接0表示不限制
9 maxusage=3, # 一个连接最多被重用的次数,None表示不限制
10 maxshared=3, # 连接池中做多共享的连接数量
11 setsession=[], # 开始会话前执行的命令列表
12 ping=0, # ping mysql服务端,检查连接是否可用
13 blocking=True, # 如果没有连接可用后,是否阻塞
14 host=setting.host,
15 port=setting.port,
16 user=setting.user,
17 password=setting.password,
18 database=setting.database,
19 charset=setting.charset,
20 autocommit=setting.autocommit,
21)


2.配置文件(setting.py)

1host = '127.0.0.1'
2port = 3306
3user = 'root'
4password = 'root'
5database = 'youku2'
6charset = 'utf8'
7autocommit = True


3.实例化数据库连接,获取游标(mysql_pool.py)

 1import db_pool
2import pymysql
3class Mysql:
4 def __init__(self):
5 self.conn = db_pool.Pool.connection()
6 self.cursor = self.conn.cursor(cursor=pymysql.cursors.DictCursor)
7 def close(self):
8 self.cursor.close()
9 self.conn.close()
10 def select(self, sql, args=None):
11 self.cursor.execute(sql, args)
12 res = self.cursor.fetchall()
13 return res
14 def execute(self,sql,args):
15 try:
16 self.cursor.execute(sql, args)
17 affected = self.cursor.rowcount
18 except Exception as e:
19 print(e)
20 finally:
21 self.close()
22 return affected


4.表的实例化,通过自定义元类实现对表生成的控制(orm.py)

  1import mysql_singleton
2#封装sql语句的复杂性,提供应用程序和数据库之间的封装接口,减少程序员的工作量
3#一:表的字段的创建
4class Field:
5 """
6 字段的定义
7 create table t1(id int primary key,
8 name varchar(200) primary key not null default);
9 """

10 def __init__(self,column_name,column_type,primary_key,default):
11 self.column_name = column_name #字段的名称
12 self.column_type = column_type #字段的类型
13 self.primary_key = primary_key #是否主键(约束条件)
14 self.default = default #默认值
15class StringField(Field):
16 """
17 数据字段类型为字符型的应用程序接口封装
18 """

19 def __init__(self,column_name = None,column_type = "varchar(200)",primary_key =False,default = None):
20 super().__init__(column_name,column_type,primary_key,default)
21class IntegerField(Field):
22 """
23 数据字段类型为整数型的应用程序接口封装
24 """

25 def __init__(self,column_name = None,column_type = "int",primary_key =False,default = 0):
26 super().__init__(column_name,column_type,primary_key,default)
27#把表看做一个类的对象,这个对象有着表名,字段名,主键所在列等等属性
28class ModelMetaclass(type):
29 """
30 把表看做一个类,通过自定义元类(类的类)的方式,从而可以控制表的创建
31 实例化出来一个类(也就是创建一张表)需要一个类的名称,类的基类(object),
32 类的名称空间(类体),类在定义阶段就会执行代码,获取类体的名字存入名称空间
33 通过这个类可以实例化出来一个表
34 表会有表名(元类实例化出来的类的类名),会有主键所在字段,会有字段名
35 """

36 def __new__(cls, class_name, bases,namespace):
37 if class_name=='Model':
38 return type.__new__(cls,class_name,bases,namespace)
39 t_name=namespace.get('t_name',None)#从类体代码中找到是否有定义表名(table)
40 if not t_name:
41 t_name = class_name #如果没有找到自定义的表名,那么以类名为表名(默认设置)
42 primary_key=None
43 mappings = dict()
44 for k,v in namespace.items():
45 if isinstance(v,Field):#v 是不是Field的对象
46 mappings[k]=v
47 if v.primary_key:
48 #找到主键
49 if primary_key:
50 raise TypeError('主键重复:%s'%k)
51 primary_key=k
52 for k in mappings.keys():
53 namespace.pop(k)
54 if not primary_key:
55 raise TypeError('没有主键')
56 namespace['t_name']=t_name
57 namespace['primary_key']=primary_key
58 namespace['mappings']=mappings
59 return type.__new__(cls,class_name,bases,namespace)
60class Model(dict, metaclass=ModelMetaclass):#元类是ModelMetaclass,父类是dict
61 """
62 一:
63 在类model的定义阶段(代码从上至下运行到这一段)元类的__new__会在
64 __init__执行之前截获执行步骤,转而执行__new__内的代码
65 运行了model的类体代码,获取了一系列的名字放入了类model的名称空间
66 二:
67 把表看做是一个类(Model)的实例化对象,这个类的元类是(ModelMetaclass),
68 类在之前的学习就已经知道,是数据和数据的处理方法的高度封装,
69 那么,实例化出表的类,应该有表和对表的一系列增删查改的操作方法的高度封装
70 """

71 def __init__(self, **kw):
72 """
73 cls[key] = value
74 :param kw:
75 """

76 super(Model, self).__init__(**kw)#继承父类的__init__方法,把值传入
77 def __getattr__(self, key):
78 """
79 __getattr__ 拦截点号运算。当对未定义的属性名称和实例进行点号运算时,就会用
80 属性名作为字符串调用这个方法。如果继承树可以找到该属性,则不调用此方法
81 """

82 try:
83 return self[key]
84 except KeyError:
85 raise AttributeError('没有属性:%s' % key)
86 def __setattr__(self, key, value):
87 """
88 __setattr__会拦截所有属性的的赋值语句。如果定义了这个方法,
89 self.name = value 就会变成self,__setattr__("name", value).
90 这个需要注意。当在__setattr__方法内对属性进行赋值是,不可使
91 用self.name = value,因为他会再次调用self,__setattr__("name", value),则
92 会形成无穷递归循环,最后导致堆栈溢出异常。应该通过对属性字典做索引运算来赋值任
93 何实例属性,也就是使用self.__dict__['name'] = value
94 """

95 self[key] = value
96 @classmethod
97 def select_all(cls, **kwargs):
98 """
99 一:支持单条查询或查询所有
100 sql语句:select * from 表名 where id(字段) = 1(查询id)
101 select * from 表名(查询所有)
102 二:
103 当类调用这个方法时,会把调用的类名自动传入
104 cls = 调用的类的名
105 **接受关键字参数(例如id= 1),赋值给kwargs
106 """

107 ms = mysql_singleton.Mysql().singleton()
108 if kwargs:
109 """
110 如果有传入的参数,取出参数,拼接sql语句
111 """

112 key = list(kwargs.keys())[0]
113 value = kwargs[key]
114 sql = "select * from %s where %s=?" % (cls.t_name, key)
115 sql = sql.replace('?', '%s')
116 re = ms.select(sql, value)
117 else:
118 sql = "select * from %s" % cls.t_name
119 re = ms.select(sql)
120 #用fetchall方法取得SQL语句执行结果(字典形式)
121 return [cls(**r) for r in re]
122 #for循环取出各个元素(字典形式),打散成如id = 1 ,name = "ls"
123 @classmethod
124 def select_one(cls, **kwargs):
125 key = list(kwargs.keys())[0]
126 value = kwargs[key]
127 ms = mysql_singleton.Mysql().singleton()
128 sql = "select * from %s where %s=?" % (cls.t_name, key)
129 sql = sql.replace('?', '%s')
130 re = ms.select(sql, value)
131 if re:
132 return cls(**re[0])
133 else:
134 return None
135 def save(self):
136 """
137 给表中插入数据
138 sql语句:insert into t1(id,name) values(1,"egon")
139 :return:
140 """

141 ms = mysql_singleton.Mysql().singleton()
142 fields = []
143 params = []
144 args = []
145 for k, v in self.mapping.items():
146 """
147 self指的是表,k为字段,v为字段的约束条件(Field的实例化)
148 """

149 fields.append(v.name)#['id','name']
150 params.append('?')#['?','?']
151 args.append(getattr(self, k, v.default))#['1','egon']
152 #返回对象(表)的属性(字段)k的值,没有则返回默认值v.default
153 #getattr(object, name[, default])函数用于返回一个对象属性值。
154 sql = "insert into %s (%s) values (%s)" % (self.table_name, ','.join(fields), ','.join(params))
155 #join()方法用于将序列中的元素以指定的字符连接生成一个新的字符串。
156 sql = sql.replace('?', '%s')
157 ms.execute(sql, args)
158 def update(self):
159 """
160 sql语句:update t1 set name = "ls",id = 2 where name = "egon"
161 :return:
162 """

163 ms = mysql_singleton.Mysql().singleton()
164 fields = []
165 args = []
166 pr = None
167 for k, v in self.mapping.items():
168 if v.primary_key:
169 pr = getattr(self, k, v.default)
170 #返回对象(表)的属性(字段)k的值,没有则返回默认值
171 #getattr(object, name[, default])函数用于返回一个对象属性值。
172 else:
173 fields.append(v.name + '=?')
174 args.append(getattr(self, k, v.default))
175 sql = "update %s set %s where %s = %s" % (
176 self.table_name, ', '.join(fields), self.primary_key, pr)
177 sql = sql.replace('?', '%s')
178 print(sql)
179 ms.execute(sql, args)
180class User(Model):
181 #User类对应着一个表,User类的实例化对象对应表中的一条记录
182 t_name = "user"
183 id = IntegerField('id',primary_key=True)
184 name = StringField("name")
185if __name__ == '__main__':
186 #数据查询
187 user = User.select_one(id=1)
188 print(user)
189 print(user.name)
190 #数据插入
191 user = User(name = 'ls')
192 user.save()
193 #数据修改
194 user = User.select_one(id=1)
195 user.name = 'mysql'
196 user.update()


5.附(sql文件)

 1SET foreign_key_checks = 0;
2DROP TABLE IF EXISTS userinfo,movie,notice,download_record;
3CREATE TABLE userinfo (
4 id INT PRIMARY KEY NOT NULL auto_increment,
5 `name` VARCHAR (32),
6 `password` VARCHAR (64),
7 is_vip INT,
8 locked INT,
9 user_type VARCHAR (32)
10) ENGINE = INNODB,charset = 'utf8';
11CREATE TABLE movie (
12 id INT PRIMARY KEY NOT NULL auto_increment,
13 `name` VARCHAR (32),
14 `path` VARCHAR (255),
15 is_free INT DEFAULT 0,
16 is_delete INT DEFAULT 0,
17 create_time timestamp default current_timestamp,
18 user_id int,
19 file_md5 VARCHAR (64)
20) charset = 'utf8';
21create table notice(
22 id int not null primary key auto_increment,
23 `name` varchar(64),
24 content varchar(255),
25 create_time timestamp default current_timestamp,
26 user_id int
27)charset = 'utf8';
28create table download_record(
29 id int not null PRIMARY key auto_increment,
30 user_id int,
31 movie_id int
32)charset = 'utf8';

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值