ORM即把数据库中的一个数据表给映射到代码里的一个类上,表的字段对应着类的属性。将增删改查等基本操作封装为类对应的方法,从而写出更干净和更富有层次性的代码。
以查询数据为例,原始的写法要Python代码sql混合,示例代码如下:
1
import
MySQLdb
2
import
os,sys
3
4
def
main():
5 conn=MySQLdb.connect(host="localhost",port=3306,passwd=‘toor‘,user=‘root‘)
6 conn.select_db("xdyweb")
7 cursor=conn.cursor()
8 count=cursor.execute("select * from users")
9 result=cursor.fetchmany()
10print(isinstance(result,tuple))
11print(type(result))
12print(len(result))
13for i in result:
14print(i)
15for j in i:
16print(j)
17print("row count is %s"%count)
18 cursor.close()
19 conn.close()
2021if__name__=="__main__":
22 cp=os.path.abspath(‘.‘)
23 sys.path.append(cp)
24 main()
而我们现在想要实现的是类似这样的效果:
1
#
查找:
2 u=user.get(id=1)
3#添加4 u=user(name=‘y‘,password=‘y‘,email=‘1@q.com‘)
5 u.insert()
实现思路是遍历Model的属性,得出要操作的字段,然后根据不同的操作要求(增,删,改,查)去动态生成不同的sql语句。
1
#
coding:utf-8
2
3
#
author:xudongyang
4
5
#
19:25 2015/4/15
6
7
import
logging,time,sys,os,threading
8
import
test as db
9
#
logging.basicConfig(level=logging.INFO,format=‘%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s‘,datefmt=‘%a, %d %b %Y %H:%M:%S‘)
10 logging.basicConfig(level=logging.INFO)
11 12class Field(object):
13#映射数据表中一个字段的属性,包括字段名称,默认值,是否主键,可空,可更新,可插入,字段类型(varchar,text,Integer之类),字段顺序 14 _count=0#当前定义的字段是类的第几个字段 15def__init__(self,**kw):
16 self.name = kw.get(‘name‘, None)
17 self._default = kw.get(‘default‘, None)
18 self.primary_key = kw.get(‘primary_key‘, False)
19 self.nullable = kw.get(‘nullable‘, False)
20 self.updatable = kw.get(‘updatable‘, True)
21 self.insertable = kw.get(‘insertable‘, True)
22 self.ddl = kw.get(‘ddl‘, ‘‘)
23 self._order = Field._count
24 Field._count = Field._count + 1
25 @property
26def default(self):
27 d = self._default
28return d() if callable(d) else d
29 30class StringField(Field):
31#继承自Field, 32def__init__(self, **kw):
33ifnot‘default‘in kw:
34 kw[‘default‘] = ‘‘ 35ifnot‘ddl‘in kw:
36 kw[‘ddl‘] = ‘varchar(255)‘ 37 super(StringField, self).__init__(**kw)
38 39class IntegerField(Field):
40 41def__init__(self, **kw):
42ifnot‘default‘in kw:
43 kw[‘default‘] = 0
44ifnot‘ddl‘in kw:
45 kw[‘ddl‘] = ‘bigint‘ 46 super(IntegerField, self).__init__(**kw)
47class FloatField(Field):
48 49def__init__(self, **kw):
50ifnot‘default‘in kw:
51 kw[‘default‘] = 0.0
52ifnot‘ddl‘in kw:
53 kw[‘ddl‘] = ‘real‘ 54 super(FloatField, self).__init__(**kw)
55 56class BooleanField(Field):
57 58def__init__(self, **kw):
59ifnot‘default‘in kw:
60 kw[‘default‘] = False
61ifnot‘ddl‘in kw:
62 kw[‘ddl‘] = ‘bool‘ 63 super(BooleanField, self).__init__(**kw)
64 65class TextField(Field):
66 67def__init__(self, **kw):
68ifnot‘default‘in kw:
69 kw[‘default‘] = ‘‘ 70ifnot‘ddl‘in kw:
71 kw[‘ddl‘] = ‘text‘ 72 super(TextField, self).__init__(**kw)
73 74class BlobField(Field):
75 76def__init__(self, **kw):
77ifnot‘default‘in kw:
78 kw[‘default‘] = ‘‘ 79ifnot‘ddl‘in kw:
80 kw[‘ddl‘] = ‘blob‘ 81 super(BlobField, self).__init__(**kw)
82 83class VersionField(Field):
84 85def__init__(self, name=None):
86 super(VersionField, self).__init__(name=name, default=0, ddl=‘bigint‘)
87 88def _gen_sql(table_name, mappings):
89print(__name__+‘is called‘+str(time.time()))
90 pk = None
91 sql = [‘-- generating SQL for %s:‘ % table_name, ‘create table `%s` (‘ % table_name]
92for f in sorted(mappings.values(), lambda x, y: cmp(x._order, y._order)):
93ifnot hasattr(f, ‘ddl‘):
94raise StandardError(‘no ddl in field "%s".‘ % n)
95 ddl = f.ddl
96 nullable = f.nullable
97if f.primary_key:
98 pk = f.name
99 sql.append(nullable and‘ `%s` %s,‘ % (f.name, ddl) or‘ `%s` %s not null,‘ % (f.name, ddl))
100 sql.append(‘ primary key(`%s`)‘ % pk)
101 sql.append(‘);‘)
102 sql=‘n‘.join(sql)
103 logging.info(‘sql is :‘+sql)
104return sql
105106class ModelMetaClass(type):
107#为什么__new__方法会被调用两次108#为什么attrs.pop(k)要进行这个,而且进行了之后u.name就可以输出yy而不是一个Field对象109def__new__(cls,name,base,attrs):
110 logging.info("cls is:"+str(cls))
111 logging.info("name is:"+str(name))
112 logging.info("base is:"+str(base))
113 logging.info("attrs is:"+str(attrs))
114print(‘new is called at ‘+str(cls)+str(time.time()))
115116if name =="Model":
117return type.__new__(cls,name,base,attrs)
118 mapping=dict()
119 primary_key=None
120for k,v in attrs.iteritems():
121 primary_key=None
122if isinstance(v,Field):
123ifnot v.name:
124 v.name=k
125 mapping[k]=v
126#检测是否是主键127if v.primary_key:
128if primary_key:
129raise TypeError("There only should be on primary_key")
130if v.updatable:
131 logging.warning(‘primary_key should not be changed‘)
132 v.updatable=False
133if v.nullable:
134 logging.warning(‘pri.. not be.null‘)
135 v.nullable=False
136 primary_key=v
137138for k in mapping.iterkeys():
139 attrs.pop(k)
140141 attrs[‘__mappings__‘]=mapping
142 logging.info(‘mapping is :‘+str(mapping))
143 attrs[‘__primary_key__‘]=primary_key
144 attrs[‘__sql__‘]=lambda self: _gen_sql(attrs[‘__table__‘], mapping)
145return type.__new__(cls,name,base,attrs)
146class ModelMetaclass(type):
147‘‘‘148 Metaclass for model objects.
149‘‘‘150def__new__(cls, name, bases, attrs):
151# skip base Model class:152if name==‘Model‘:
153return type.__new__(cls, name, bases, attrs)
154155# store all subclasses info:156ifnot hasattr(cls, ‘subclasses‘):
157 cls.subclasses = {}
158ifnot name in cls.subclasses:
159 cls.subclasses[name] = name
160else:
161 logging.warning(‘Redefine class: %s‘ % name)
162163 logging.info(‘Scan ORMapping %s...‘ % name)
164 mappings = dict()
165 primary_key = None
166for k, v in attrs.iteritems():
167if isinstance(v, Field):
168ifnot v.name:
169 v.name = k
170 logging.info(‘Found mapping: %s => %s‘ % (k, v))
171# check duplicate primary key:172if v.primary_key:
173if primary_key:
174raise TypeError(‘Cannot define more than 1 primary key in class: %s‘ % name)
175if v.updatable:
176 logging.warning(‘NOTE: change primary key to non-updatable.‘)
177 v.updatable = False
178if v.nullable:
179 logging.warning(‘NOTE: change primary key to non-nullable.‘)
180 v.nullable = False
181 primary_key = v
182 mappings[k] = v
183# check exist of primary key:184ifnot primary_key:
185raise TypeError(‘Primary key not defined in class: %s‘ % name)
186for k in mappings.iterkeys():
187 attrs.pop(k)
188ifnot‘__table__‘in attrs:
189 attrs[‘__table__‘] = name.lower()
190 attrs[‘__mappings__‘] = mappings
191 attrs[‘__primary_key__‘] = primary_key
192 attrs[‘__sql__‘] = lambda self: _gen_sql(attrs[‘__table__‘], mappings)
193# for trigger in _triggers:194# if not trigger in attrs:195# attrs[trigger] = None196return type.__new__(cls, name, bases, attrs)
197class Model(dict):
198__metaclass__ = ModelMetaClass
199def__init__(self, **kw):
200 super(Model, self).__init__(**kw)
201202def__getattr__(self, key):
203try:
204return self[key]
205except KeyError:
206raise AttributeError(r"‘Dict‘ object has no attribute ‘%s‘" % key)
207208def__setattr__(self, key, value):
209 self[key] = value
210211 @classmethod
212def get(cls, pk):
213‘‘‘214 Get by primary key.
215‘‘‘216 d = db.select_one(‘select * from %s where %s=?‘ % (cls.__table__, cls.__primary_key__.name), pk)
217return cls(**d) if d else None
218219 @classmethod
220def find_first(cls, where, *args):
221‘‘‘222 Find by where clause and return one result. If multiple results found,
223 only the first one returned. If no result found, return None.
224‘‘‘225 d = db.select_one(‘select * from %s %s‘ % (cls.__table__, where), *args)
226return cls(**d) if d else None
227228 @classmethod
229def find_all(cls, *args):
230‘‘‘231 Find all and return list.
232‘‘‘233 L = db.select(‘select * from `%s`‘ % cls.__table__)
234return [cls(**d) for d in L]
235236 @classmethod
237def find_by(cls, where, *args):
238‘‘‘239 Find by where clause and return list.
240‘‘‘241 L = db.select(‘select * from `%s` %s‘ % (cls.__table__, where), *args)
242return [cls(**d) for d in L]
243244 @classmethod
245def count_all(cls):
246‘‘‘247 Find by ‘select count(pk) from table‘ and return integer.
248‘‘‘249return db.select_int(‘select count(`%s`) from `%s`‘ % (cls.__primary_key__.name, cls.__table__))
250251 @classmethod
252def count_by(cls, where, *args):
253‘‘‘254 Find by ‘select count(pk) from table where ... ‘ and return int.
255‘‘‘256return db.select_int(‘select count(`%s`) from `%s` %s‘ % (cls.__primary_key__.name, cls.__table__, where), *args)
257258def update(self):
259 self.pre_update and self.pre_update()
260 L = []
261 args = []
262for k, v in self.__mappings__.iteritems():
263if v.updatable:
264if hasattr(self, k):
265 arg = getattr(self, k)
266else:
267 arg = v.default
268 setattr(self, k, arg)
269 L.append(‘`%s`=?‘ % k)
270 args.append(arg)
271 pk = self.__primary_key__.name
272 args.append(getattr(self, pk))
273 db.update(‘update `%s` set %s where %s=?‘ % (self.__table__, ‘,‘.join(L), pk), *args)
274return self
275276def delete(self):
277 self.pre_delete and self.pre_delete()
278 pk = self.__primary_key__.name
279 args = (getattr(self, pk), )
280 db.update(‘delete from `%s` where `%s`=?‘ % (self.__table__, pk), *args)
281return self
282283def insert(self):
284 self.pre_insert and self.pre_insert()
285 params = {}
286for k, v in self.__mappings__.iteritems():
287if v.insertable:
288ifnot hasattr(self, k):
289 setattr(self, k, v.default)
290 params[v.name] = getattr(self, k)
291 db.insert(‘%s‘ % self.__table__, **params)
292return self
293class user(Model):
294 name=StringField(name=‘name‘,primary_key=True)
295 password=StringField(name=‘password‘)
296297def main():
298 u=user(name=‘yy‘,password=‘yyp‘)
299300 logging.info(u.__sql__)
301 logging.info(dir(u.__mappings__.values()))
302 u.password=‘xxx‘303print(u.password)
304305if__name__ == ‘__main__‘:
306 main()
要注意的是遍历Model属性这部分代码,利用了Python的__metaclass__实现,截断了Model的创建过程,进而对Model的属性进行遍历,具体代码见ModelMetaclass的__new__方法实现。
这是模仿廖老师的代码,[http://liaoxuefeng.com],感谢。还有两个疑问注释在了代码中,希望有看明白的人解惑。
原文:http://www.cnblogs.com/cncyber/p/4433301.html
【说明】:本文章由站长整理发布,文章内容不代表本站观点,如文中有侵权行为,请与本站客服联系(QQ:254677821)!