当前位置:首页 > Python教程 > python技巧

Python实现ORM

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()
View Code

而我们现在想要实现的是类似这样的效果:

技术分享 技术分享
                1
                #
                查找:
                2 u=user.get(id=1)
3#添加4 u=user(name=y,password=y,email=1@q.com)
5 u.insert()
View Code

实现思路是遍历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):
 33ifnotdefaultin kw:
 34             kw[default] = ‘‘ 35ifnotddlin kw:
 36             kw[ddl] = varchar(255) 37         super(StringField, self).__init__(**kw)
 38 39class IntegerField(Field):
 40 41def__init__(self, **kw):
 42ifnotdefaultin kw:
 43             kw[default] = 0
 44ifnotddlin kw:
 45             kw[ddl] = bigint 46         super(IntegerField, self).__init__(**kw)
 47class FloatField(Field):
 48 49def__init__(self, **kw):
 50ifnotdefaultin kw:
 51             kw[default] = 0.0
 52ifnotddlin kw:
 53             kw[ddl] = real 54         super(FloatField, self).__init__(**kw)
 55 56class BooleanField(Field):
 57 58def__init__(self, **kw):
 59ifnotdefaultin kw:
 60             kw[default] = False
 61ifnotddlin kw:
 62             kw[ddl] = bool 63         super(BooleanField, self).__init__(**kw)
 64 65class TextField(Field):
 66 67def__init__(self, **kw):
 68ifnotdefaultin kw:
 69             kw[default] = ‘‘ 70ifnotddlin kw:
 71             kw[ddl] = text 72         super(TextField, self).__init__(**kw)
 73 74class BlobField(Field):
 75 76def__init__(self, **kw):
 77ifnotdefaultin kw:
 78             kw[default] = ‘‘ 79ifnotddlin 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=xxx303print(u.password)
304305if__name__ == __main__:
306     main()
View Code

 

要注意的是遍历Model属性这部分代码,利用了Python的__metaclass__实现,截断了Model的创建过程,进而对Model的属性进行遍历,具体代码见ModelMetaclass的__new__方法实现。

这是模仿廖老师的代码,[http://liaoxuefeng.com],感谢。还有两个疑问注释在了代码中,希望有看明白的人解惑。

原文:http://www.cnblogs.com/cncyber/p/4433301.html


【说明】本文章由站长整理发布,文章内容不代表本站观点,如文中有侵权行为,请与本站客服联系(QQ:254677821)!