码迷,mamicode.com
首页 > 编程语言 > 详细

Python学习 - 编写自己的ORM(1)

时间:2014-10-18 06:25:41      阅读:343      评论:0      收藏:0      [点我收藏+]

标签:blog   http   os   ar   使用   for   sp   数据   div   

这篇博文参考的是廖雪峰的Python教程的实战部分,传送门。推荐大家看看装饰器和使用元类这两个章节,然后在看实战部分。

这篇博文有时间了还会更新,主要是学习Python的语法,如上面提到的装饰器和元类。

起步:编写简单的ORM对象

写一个类映射某个数据表,下面是写一个User类,对应数据库中的user表:

class User(Model):
    
    id = StringField(primary_key=True,ddl=‘varchar(50)‘)
    name = StringField(ddl=‘varchar(50)‘)

 

这个表中只有两个字段,id和name,为了简便,这两个字段都为StringField类型。其中StringField类型也为定义的类,对应数据库中的varchar类型。

class Field(object):
    _count = 0
    def __init__(self,**kw):
        self.name = kw.get(‘name‘,None)
        self._default = kw.get(‘default‘,None)
        self.nullable = kw.get(‘nullable‘,False)
        self.primary_key = kw.get(‘primary_key‘,False)
        self._order = Field._count
        Field._count = Field._count + 1
        print ‘xxx‘
        self.ddl = kw.get(‘ddl‘,‘‘)

    @property
    def default(self):
        d = self._default
        return d() if callable(d) else d
    def __str__(self):
        s = [‘<%s:%s,%s,default(%s),‘ % (self.__class__.__name__,self.name,self.ddl,self._default)]
        self.nullable and s.append(‘N‘)
        s.append(‘>‘)
        return ‘‘.join(s)
class StringField(Field):
    def __init__(self,**kw):
        if not ‘default‘ in kw:
            kw[‘default‘] = ‘‘
        if not ‘ddl‘ in kw:
            kw[‘ddl‘] = ‘varchar(250)‘
        super(StringField,self).__init__(**kw)

 其中Field为基类,可以将数据库中的类型扩充完。然后在写一个Model类:

class Model(dict):
    __metaclass__ = ModelMetaclass

    def __init__(self,**kw):
        super(Model,self).__init__(**kw)

    def __getattr__(self,key):
        try:
            return self[key]
        except KeyError:
            raise AttributeError(r"‘Model‘ object has no attribute ‘%s‘" % key)

    def __setattr__(self,key,value):
        self[key] = value
        print ‘%s: %s‘ % self,value

 Model类父类为dict,其中__metaclass__为元类。这个是整个代码的基础。元类创建一个ModelMetaclass类的实例,注意__new__(cls,name,bases,attrs)中的四个参数表示的意思就很好理解了:

class ModelMetaclass(type):
    def __new__(cls,name,bases,attrs):
        if name == ‘Model‘:
            return type.__new__(cls,name,bases,attrs)
        print (‘Found model: %s‘ % name)
        mappings = dict()
        primary_key = None
        li = []
        for k,v in attrs.iteritems():
            if isinstance(v,Field):
                if not v.name:
                    v.name = k
                if v.primary_key:
                    if primary_key:
                        raise TypeError(‘Cannot define more than one primary key in class: %s‘ % name)
                    if v.nullable:
                        v.nullable = False
                    primary_key = v
                mappings[k] = v
        if not primary_key:
            raise TypeError(‘Primary key not defined in class: %s‘ % name)
        for k in mappings.iterkeys():
            attrs.pop(k)
        attrs[‘__table__‘] = name.lower()
        attrs[‘__mappings__‘] = mappings
        attrs[‘__primary_key__‘] = primary_key
        attrs[‘__sql__‘] =  _gen_sql(attrs[‘__table__‘],mappings)
        print str(attrs[‘__sql__‘])
        tables.append(attrs[‘__sql__‘])
        for trigger in _triggers:
            if not trigger in attrs:
                attrs[trigger] = None
        return type.__new__(cls,name,bases,attrs)

 最后是完整的代码,不过只实现了创建表的功能,这只是万里长征的第一步,慢慢来吧:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# orm.py
tables = []
# Field类
class Field(object):
    _count = 0
    def __init__(self,**kw):
        print ‘aaa‘
        self.name = kw.get(‘name‘,None)
        self._default = kw.get(‘default‘,None)
        self.nullable = kw.get(‘nullable‘,False)
        self.primary_key = kw.get(‘primary_key‘,False)
        self._order = Field._count
        Field._count = Field._count + 1
        print ‘xxx‘
        self.ddl = kw.get(‘ddl‘,‘‘)

    @property
    def default(self):
        d = self._default
        return d() if callable(d) else d
    def __str__(self):
        s = [‘<%s:%s,%s,default(%s),‘ % (self.__class__.__name__,self.name,self.ddl,self._default)]
        self.nullable and s.append(‘N‘)
        s.append(‘>‘)
        return ‘‘.join(s)
class StringField(Field):
    def __init__(self,**kw):
        if not ‘default‘ in kw:
            kw[‘default‘] = ‘‘
        if not ‘ddl‘ in kw:
            kw[‘ddl‘] = ‘varchar(250)‘
        super(StringField,self).__init__(**kw)
_triggers = frozenset([‘pre_insert‘,‘pre_update‘,‘pre_insert‘])
# 生成sql语句
def _gen_sql(table_name,mappings):
    pk = None
    sql = [‘create table `%s` (‘ % table_name]
    for f in sorted(mappings.values(),lambda x,y:cmp(x._order, y._order)):
        if not hasattr(f,‘ddl‘):
            raise StandardError(‘No ddl in field `%s`‘ % n)
        ddl = f.ddl
        nullable = f.nullable
        if f.primary_key:
            pk = f.name
        sql.append(nullable and ‘ `%s` %s‘ % (f.name,ddl) or ‘ `%s` %s not null‘ % (f.name,ddl))
        if pk:
            sql.append(‘ primary key,‘)
        pk = None
    sql.append(‘);‘)
    return ‘\n‘.join(sql)
        
class ModelMetaclass(type):
    def __new__(cls,name,bases,attrs):
        if name == ‘Model‘:
            return type.__new__(cls,name,bases,attrs)
        print (‘Found model: %s‘ % name)
        mappings = dict()
        primary_key = None
        li = []
        for k,v in attrs.iteritems():
            if isinstance(v,Field):
                if not v.name:
                    v.name = k
                if v.primary_key:
                    if primary_key:
                        raise TypeError(‘Cannot define more than one primary key in class: %s‘ % name)
                    if v.nullable:
                        v.nullable = False
                    primary_key = v
                mappings[k] = v
        if not primary_key:
            raise TypeError(‘Primary key not defined in class: %s‘ % name)
        for k in mappings.iterkeys():
            attrs.pop(k)
        attrs[‘__table__‘] = name.lower()
        attrs[‘__mappings__‘] = mappings
        attrs[‘__primary_key__‘] = primary_key
        attrs[‘__sql__‘] =  _gen_sql(attrs[‘__table__‘],mappings)
        print str(attrs[‘__sql__‘])
        tables.append(attrs[‘__sql__‘])
        for trigger in _triggers:
            if not trigger in attrs:
                attrs[trigger] = None
        return type.__new__(cls,name,bases,attrs)

class Model(dict):
    __metaclass__ = ModelMetaclass

    def __init__(self,**kw):
        super(Model,self).__init__(**kw)

    def __getattr__(self,key):
        try:
            return self[key]
        except KeyError:
            raise AttributeError(r"‘Model‘ object has no attribute ‘%s‘" % key)

    def __setattr__(self,key,value):
        self[key] = value
        print ‘%s: %s‘ % self,value
  # 该方法可以注释掉
    def save(self):
        fields = []
        params = []
        args = []
        for k,v in self.__mappings__.iteritems():
            fields.append(v.name)
            params.append(‘?‘)
            args.append(getattr(self,k,None))
        sql = ‘insert into %s (%s) values(%s)‘ % (self.__table__,‘,‘.join(fields),‘,‘.join(params))
        print sql
        print (‘ARGS: %s‘ % str(args))

class User(Model):
    
    id = StringField(primary_key=True,ddl=‘varchar(50)‘)
    name = StringField(ddl=‘varchar(50)‘)

sql = ‘‘.join(tables)
sql = sql.replace(‘`‘,‘‘)
print sql
import mysql.connector
config = {‘user‘:‘root‘,‘password‘:‘z5201314‘,‘host‘:‘127.0.0.1‘,‘port‘:3306,‘database‘:‘test‘}
cxn = mysql.connector.connect(**config)
cursor = cxn.cursor()
cursor.execute(sql)

 运行结果:

bubuko.com,布布扣

查看数据库:

bubuko.com,布布扣

不过,这才是刚刚开始。。。

Python学习 - 编写自己的ORM(1)

标签:blog   http   os   ar   使用   for   sp   数据   div   

原文地址:http://www.cnblogs.com/mr-zys/p/4032458.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!