sqlalchemy源码阅读(1)
来源:互联网 发布:实现数据库的增删改查 编辑:程序博客网 时间:2024/06/05 09:23
code
先上代码,今天要解释的是下面这个代码,位置在sqlalchemy/util/langhelpers.py
def public_factory(target, location): """Produce a wrapping function for the given cls or classmethod. Rationale here is so that the __init__ method of the class can serve as documentation for the function. """ if isinstance(target, type): fn = target.__init__ callable_ = target doc = "Construct a new :class:`.%s` object. \n\n"\ "This constructor is mirrored as a public API function; see :func:`~%s` "\ "for a full usage and argument description." % ( target.__name__, location, ) else: fn = callable_ = target doc = "This function is mirrored; see :func:`~%s` "\ "for a description of arguments." % location location_name = location.split(".")[-1] spec = compat.inspect_getfullargspec(fn) del spec[0][0] metadata = format_argspec_plus(spec, grouped=False) metadata['name'] = location_name code = """\def %(name)s(%(args)s): return cls(%(apply_kw)s)""" % metadata env = {'cls': callable_, 'symbol': symbol} exec(code, env) decorated = env[location_name] decorated.__doc__ = fn.__doc__ if compat.py2k or hasattr(fn, '__func__'): fn.__func__.__doc__ = doc else: fn.__doc__ = doc return decorated
为什么要阅读这个代码
这个要从我今天研究的一个问题说起,之前没有特别研究过sqlalchemy的executemany的实现过程,因为工作需要,还是需要研究一下sqlalchemy的内部实现。
使用sqlalchemy的测试代码如下,
from sqlalchemy import ( create_engine,)from sqlalchemy.orm import ( sessionmaker)from sqlalchemy.ext.declarative import declarative_basefrom sqlalchemy import Column, Integerfrom sqlalchemy.dialects.postgresql import JSONengine = create_engine("postgresql://root:root@127.0.0.1:5432/testdb")DBSession = sessionmaker(bind=engine)Base = declarative_base(name="Alex")class Books(Base): __tablename__ = 'books' id = Column(Integer, primary_key=True) data = Column(JSON)def simple_query(): new_book = Books(id=1, data={"hello": 123}) session = DBSession() session.add(new_book) session.commit()def test_many(): session = DBSession() data_list = [ {"id": 200, "data": {"hello": 123}}, {"id": 300, "data": {"hello": 123}}, {"id": 400, "data": {"hello": 123}}, {"id": 500, "data": {"hello": 123}}, ] session.execute(Books.metadata.tables['books'].insert(), data_list) #1 session.commit()simple_query()test_many()
根据python的DBAPI,python是不支持JDBC的prepared statement的,但是DBAPI提供executemany的接口,可以让你一次执行插入多行,具体见DBAPI。
但是在网上可以找到的SO来看,在#1这里所使用的都是一个Table的insert()可以执行expr,并不是我们现在使用的declarative的表。废了一些功夫才找到上面这个用法,不知道是不是正规的用法,如果有了解的还请告诉我,要不只能等到以后某天自己发现改过来了。
在查找这个问题的时候,碰到了sqlalchemy里面是如何实现declarative的问题, 首当其冲的就是这个函数。
好了,自己工作的事情写这么多,下面来分析一下我们上面要分析的这个函数,为什么是它呢?
分析
Base = declarative_base(name="Alex")class Books(Base): __tablename__ = 'books' id = Column(Integer, primary_key=True) data = Column(JSON) def __init__(self, *args, **kwargs): super.__init__(self, *args, **kwargs)
如上,看到我们使用declarative方式使用sqlalchemy的声明过程。首先我们使用declarative_base定义了我们要使用的Base类,注意,这出来的是一个类哦,因为declarative_base
里面使用了DeclarativeMeta
这个metaclass。
...............................from ...orm import mapper, class_mapper, synonym...............................class DeclarativeMeta(type): def __init__(cls, classname, bases, dict_): if '_decl_class_registry' not in cls.__dict__: _as_declarative(cls, classname, cls.__dict__) type.__init__(cls, classname, bases, dict_) def __setattr__(cls, key, value): _add_attribute(cls, key, value)def _as_declarative(cls, classname, dict_):....... if defer_map: cfg_cls = _DeferredMapperConfig else: cfg_cls = _MapperConfig mt = cfg_cls(mapper_cls, cls, table, inherits, declared_columns, column_copies, our_stuff, mapper_args_fn) if not defer_map: mt.map()
当我们定义完Book这个类,python解释器扫描完这个类,就会为我们创建这个类对象,相应的也会调用_as_declarative
这个路径进来。注意在最后,默认实际上是调用了_MapperConfig.map
. 而在这个函数中,使用mapper_cls
来创建我们的mapper对象。那么这个mapper_cls
就是我们简单认为的是Mapper吗?no no no
实际上,它是sqlalchemy/orm/init.py中的mapper对象。而这个对象则是由下面来生成的
mapper = public_factory(Mapper, ".orm.mapper")
那么现在,我们的public_factory就出现了,为什么会有它呢?而不是直接使用Mapper?
从它的注释中可以看出,这个函数的功能就是将一个类的init 方法暴露出来,暴露成一个函数对象。那么问题是为什么要暴露成函数对象呢?注释里又说了,这个是为了生成文档用的。好吧,看到这里,个人认为,sqlalchemy中的命名规则还是比较随意的,只是从函数的名字还不能清晰的得知这个函数的用途或者说目的。
收获
这里的收获其实不止可以把一个类的init 方法暴露出来用来生成文档用。这里想说的是exec
这个函数。
exec(object[, globals[, locals]])
这个是exec这个函数的原型,对照上面的代码可以看到,它是把env这个dict当做了这里的globals来使用的,并且根据exec的文档,exec执行后,会把__builtins__
插入到这个env里面来,通过运行可以证明,这个是对的。尤为重要的一点是经过exec,它把上面的code字符串编程了function对象decorated
,经过一番处理后,返回了出去。而这个函数就是_MapperConfig.map
中所使用的mapper_cls
。
令人奇怪的是,它虽然带有cls
但是时间上它并不是一个class对象,而是一个function对象。有点误导人。
- sqlalchemy源码阅读(1)
- flask-sqlalchemy(1)
- SQLAlchemy简析(1)
- VODemo源码阅读(1)
- struts1源码阅读(1)
- struts1源码阅读(1)
- Spring 源码阅读(1)
- DispatcherServlet 源码阅读(1)
- nginx源码阅读(1)
- waste源码阅读笔记(1)
- YunTable源码阅读笔记(1)
- QUnit源码阅读(1):工具函数
- IPMsg源码阅读笔记(1)
- redis源码阅读(1)---- 开始
- go源码阅读笔记(math.1)
- Android系统源码阅读(1):编译
- Fragment相关源码阅读笔记(1)
- EventBus源码阅读(1)-ThreadMode
- TCP MSS PMTU PING
- Java动态代理
- springmvc 后台偶尔获取不到参数
- Fresco 图片预加载
- 2017.1.17【初中部 GDKOI】模拟赛B组 队列变换 题解
- sqlalchemy源码阅读(1)
- 欢迎使用CSDN-markdown编辑器
- struts2拦截器
- spring 定时任务使用
- Objective-C 【NSObject 的实现分析】
- Android Studio无线调试app之Android WiFi ADB
- RESTful服务使用HTTP方法相关概念学习理解
- 一个空指针异常null==1
- java.lang.OutOfMemoryError: PermGen space