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

这篇博文参考的是廖雪峰的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)

运行结果:

查看数据库:

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

时间: 2024-10-29 19:09:53

Python学习 - 编写自己的ORM(1)的相关文章

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

上一篇文章简单的实现了ORM(对象关系模型),这一篇文章主要实现简单的MySQL数据库操作. 想要操作数据库,首先要建立一个数据库连接.下面定义一个创建数据库连接的函数,得到一个连接叫做engine. def create_engine(user,password,database,host='127.0.0.1',port=3306,**kw): import mysql.connector global engine if engine is not None: raise DBError(

python 学习笔记十一 SQLALchemy ORM(进阶篇)

SqlAlchemy ORM SQLAlchemy是Python编程语言下的一款ORM框架,该框架建立在数据库API之上,使用关系对象映射进行数据库操作,简言之便是:将对象转换成SQL,然后使用数据API执行SQL并获取执行结果. Dialect用于和数据API进行交流,根据配置文件的不同调用不同的数据库API,从而实现对数据库的操作,如: MySQL-Python mysql+mysqldb://<user>:<password>@<host>[:<port&g

Python学习 - 编写一个简单的web框架(一)

自己动手写一个web框架,因为我是菜鸟,对于python的一些内建函数不是清楚,所以在写这篇文章之前需要一些python和WSGI的预备知识,这是一系列文章.这一篇只实现了如何处理url. 参考这篇文章:http://www.cnblogs.com/russellluo/p/3338616.html 预备知识 web框架主要是实现web服务器和web应用之间的交互.底层的网络协议主要有web服务器完成.譬如监听端口,填充报文等等. Python内建函数__iter__和__call__和WSGI

Python学习笔记八:ORM框架SQLAlchemy

一:SQLAlchemy使用 1:实体类的创建 ORM中的实体类与一般的Python类不同,在其中,使用 __tablename__=""指明该类与数据库中某个表相对应,然后定义一系列成员属性,属性值使用 Column(数据类型) 来映射到表中具体哪一列. 首先,创建数据库引擎,并由静态方法获取一个基类:declarative_base() 创建了一个 BaseModel 类,这个类的子类可以自动与一个表关联. 然后,继承base类,定义实体类: 带外键的实体类创建: 最后,运行基类中

Python学习---抽屉框架分析[ORM操作]180314

Django ORM操作     1. 字段操作         class User(model.Model);             u=字段        用处:            1 .admin中的字段验证            2. obj.clean_fields() 进行自定义的验证             3. 利用Djanfo Form进行验证,此时前台和后台的操作分开               但form和model里的字段重复[推荐使用]             

Python学习 - 编写一个简单的web框架(二)

在上一篇日志中已经讨论和实现了根据url执行相应应用,在我阅读了bottle.py官方文档后,按照bottle的设计重写一遍,主要借鉴大牛们的设计思想. 一个bottle.py的简单实例 来看看bottle是如何使用的,代码来自http://www.bottlepy.org/docs/0.12/index.html: from bottle import route, run, template @route('/hello/<name>') def index(name): return t

python学习笔记(十六) - ORM框架(SQLAlchemy)

所谓的ORM就是Object-Relational Mapping,把关系数据库的表结果映射到对象上. 1. 安装SQLAlchemy: easy_install sqlalchemy 2. 导入SQLAlchemy,并初始化DBSession: # 导入: from sqlalchemy import Column, String, create_engine from sqlalchemy.orm import sessionmaker from sqlalchemy.ext.declara

python学习笔记-Day022 - django ORM操作

一对多:models.ForeignKey() 首先定义表结构: class UserType(models.Model):     caption = models.CharField(max_length=32)     def __unicode__(self):         return self.caption          class UserInfo(models.Model):     username = models.CharField(max_length=32)

Python学习(三):入门篇:Python中怎么编写类

Python中怎么编写类 Last Edit 2013/5/2 先看一个例子: #person.py class person: """class to representaion a person""" def __init__(self,name,age): self.name=name if 0<age<=150: self.age=age else: print 'age is no valid!' def display(s