简单介绍下 什么是ORM ?
ORM,即Object-Relational Mapping(对象关系映射),它的作用是在关系型数据库和业务实体对象之间作一个映射,这样,我们在具体的操作业务对象的时候,就不需要再去和复杂的SQL语句打交道,只需简单的操作对象的属性和方法。
他的优点就是用起来很是方便, 但是缺点也明显,就是不支持太复杂的语句、含有Queryset的orm性能不咋地。
Python下的orm没几个选择,Django orm ,peewee ,SQLAlchemy ,Pony Orm 。
文章的原文地址是,http://xiaorui.cc/?p=2154
介绍完ORM后,那么简单描述下,我在写python orm中遇到的问题,确切说不能是问题,应该是一些心得。
为什么会用ORM来操作数据库 .
如果没有ORM数据库映射模型的话,我们一般是这么玩数据库的。
use = conn.cursor.execute(‘select * from api_users where id=%s’, ‘123’)
但是如果有orm的话,那么我们只需要下面的友好方式就行了。
#blog: http://xiaorui.cc class User(Model): __table__ = 'api_users' id = IntegerField(primary_key=True) name = StringField() #xiaorui.cc user = User.get(id=123)
很多时候造轮子,都想按照自己的实现方式,实现出跟别人一样的效果。 比如想做个简单的django rom,peewee 。 就因为这,把自己玩的有些抑郁了。
下面是Django orm的实现方式,说实话django咋实现的,我没去看。 我就自己黑灯瞎火下的。
#blog: http://xiaorui.cc user = User() #直接修改属性 user.id = 1 user.name = 'xiaorui.cc' user.save()
这样虽然很舒心,但是带来的问题是,要在类里面找到你要修改哪个字段。
刚才我们写了一个表的模型,当你进行操作的时候,你的父类怎么找到字段关联的对象?
两个方法,第一个方法是 dir(self.__class__) 找到里面所有的对象,进行扫描,看看谁的属性是数据库类型的,也就是Field这个类的。 一定要过滤,因为类里面还有别的对象。
#blog: http://xiaorui.cc class Model(Utils): def __init__(self, rid=0, **kwargs): self.table_name = self.__class__.__name__.lower() for name in self.field_names: field = getattr(self.__class__, name.replace("`", "")) setattr(self, name.replace("`", ""), field.default) for key, value in kwargs.items(): setattr(self, key.replace("`", ""), value) # self.debug() def debug(self): for name in dir(self.__class__): print name,'-------',getattr(self.__class__,name) @property def field_names(self): names = [] for name in dir(self.__class__): var = getattr(self.__class__, name.replace("`", "")) if isinstance(var, Field): names.append("`%s`"%name) return names @property def field_values(self): values = [] for name in self.field_names: value = getattr(self, name.replace("`", "")) if isinstance(value, Model): value = value.id if isinstance(value, str): value = value.replace("'", "''") try: value = value.decode("gbk") except Exception, e: pass try: value = value.decode("utf8") except Exception, e: pass values.append("'%s'" % value) return values
那么第二个方法就是,用 self.__dict__ ,缺点是这里也会把你实例化的属性也给塞进去。 当然你还是可以做过滤判断。
class Field(object): pass class MetaModel(type): db_table = None fields = {} def __init__(cls, name, bases, attrs): super(MetaModel, cls).__init__(name, bases, attrs) fields = {} for key, val in cls.__dict__.iteritems(): if isinstance(val, Field): fields[key] = val cls.fields = fields cls.attrs = attrs class Model(): __metaclass__ = MetaModel def save(self): #self.t = 'field t' insert = 'insert ignore into %s(%s) values (%s);' % (self.db_table, ', '.join(self.__dict__.keys()), ', '.join(['%s'] * len(self.__dict__))) print insert
说完了,怎么让父类找到你要修改的字段。 但是上面两种方法都会带来另一个问题度。那么就是第二次save的时候,name字段的值是 xiaorui.cc,按照常理来说,第二次我只是save addr这个字段,name没有,那么name就应该为空 。 这说明了我们没有对类里面的对象进行清理。
class Rui(Model): db_table = 'kkk' name = Field() addr = Field() r = Rui() r.name = 'xiaorui.cc' r.save() r.addr = 'in baidu' r.save()
后来我又想,其实完全可以这么用。 这就不用考虑过滤类里面的所有对象了。
Rui().save(name='xiaorui.cc',addr='beijing')
恩,当然我做的东西更像是ORM及sql语句的映射,其实这也是我想要的。专业的orm还有一个很主要的东西,就是QuerySet,她会把你的从mysql取回来的结果,构建成queryset集合的格式。 虽然费劲多包装了一层,但是对于用户来说,使用方法是相当友好的。 但是我不喜欢QuerySet这种东西,性能真是个问题。 对于,django orm 对比 mysqldb原生sql 性能对比,可以看看我以前的文章。
这里要额外说下缓存及路由的实现,对于缓存其实相对简单, 可以直接选个nosql或者是内存做存储,直接放在python内存中,倒是可以用lru的算法实现旧数据的替换。 那么什么又是路由,所有的路由就是当你分库分表后,你的sql语句就不能像以前那样直接查询了。如果你的分表是按照 id ,那么你的路由就要按照id区间,来定位你应该访问哪个表。
再说的详细点.
有一个大表,你已经按照uid来水平区切分了。
有两种方式来访问它,最笨的方法就是用union的方法,解决不能用它,因为他的存在,已经否定了分表的意义了。
(SELECT xx FROM table1 WHERE type=1) union (SELECT xx FROM table1 WHERE type=1) union (SELECT xx FROM table1 WHERE type=1) union (SELECT xx FROM table1 WHERE type=1)
那么比较高效的方法是,通过uid定位到具体的表通过算法实现,比如通过uid和表的数目取模,最后得到的表的位置。例如:uid 为500的用户,和表的数目是5个,那表名就是user_0,
uid为1005的用户,1005%5=0; 那表名就是user_0;
依次类推,通过算法实现,总体思路就是把数据分散存储到不同的表中,减少压力及事务锁带来的麻烦。以前做监控的时候,遇到亿级别的数据,用的是时间区间分表的方法,但原理都是一样的。 这与orm路由已经说的差不多了。
以前在乐视网开发DBA管理系统时,也造了一个orm轮子…. 不要脸的说是颇为强大的orm模块,可惜的是因为别的业务也要做调用,我做了不少的妥协。最后写的跟线上业务有些的耦合了,真心不纯粹 ! 夹杂了基本的QuerySet,LruCache,RPC功能。我不打算开源,因为代码质量太差… 虽一直在线上跑着,但我对着个不是很满意.
这次我还是决定重写一个比较简单的,只是做SQL语句映射的。 恩,我把简易版的ORM放到github了,功能还有些简单,部分功能还没有完善,比如 order by , group by ,having 啥的。
话不多说了,我下面介绍下不太用心写的项目,欢迎批评!!! 当然有兴趣的朋友可以一块搞搞,毕竟这项目本身就很干净。
项目名字: simple_orm_python
项目地址: https://github.com/rfyiamcool/simple_orm_mysql
#coding:utf-8 #blog : http://xiaorui.cc from simple_orm_mysql import * class User(Model): name = CharField() addr = CharField() if __name__ == "__main__": user = User() user.addr = "beijing of china" user.name = 'fengyun' user.save() user.addr = "shanghai of china" user.save() user.get(name='xiaorui.cc',addr="beijing")
对于python开发orm映射对象模型,就讲这么多了。 其实可以举一反三在mongodb中也可以用到。 下次找个时间,给大家分享下基于mongodb的orm模型开发。 话说mongodb的orm开发其实更加的灵活。
END !!!