前前后后,大概两个月的时间,lunar这个项目终于达到了一个很高的完整度。
Lunar是一个Python语言的网络框架,类似于Django,Flask,Tornado等当下流行的web framework。最初有这个想法是在大二下学期,当时接触Python web编程有一段时间。最早接触Python web编程或许是在大一下?自觉当时编程还没有入门,第一个接触的web框架是Django,很庞大的框架,当时很low逼的去看那本Django Book的中文译本,翻译的其实很不错了,只是进度会落后于当前的版本,所以在跑example的时候会有一些问题,Django的庞大规模给我留下了很大的心理阴影,所以在之后,对于涉世未深的Pythoner,我可能都不会推荐Django作为第一个Python的网络框架来学习。
整个框架的挑战还是非常大的。核心的几个组件(模板引擎,ORM框架,请求和应答的处理)还是有一些难度,但是经过一步步的分析和编码还是能够完成功能。项目受Flask非常大的影响,最初作为造轮子的初衷,几乎完整的使用了Flask和SQLAlchemy的API接口。
项目同样开源在Github上: https://github.com/jasonlvhit/lunar
也可以通过pip直接安装:
$ pip install lunar
这里我大概的记述一下lunar整个项目的各个组件的设计和实现。
ORM framework
首先是ORM。
python圈子里面还是有很多很著名的orm框架,SQLAlchemy,peewee, pony orm各有特色,SQLAlchemy和peewee都已經是很成熟的框架,大量的被应用在商业环境中。回到Lunar,既然是造轮子,何不造个彻底,于是便撸了一个orm框架出来。
在ORM框架中,我们使用类的定义来表示数据库中的表结构,使用类方法避免繁琐的SQL语句,一个ORM类定义类似于下面这段代码:
py
class Post(db.Model): __tablename__ = 'post' id = database.PrimaryKeyField() title = database.CharField(100) content = database.TextField() pub_date = database.DateField() author_id = database.ForeignKeyField('author') tags = database.ManyToManyField(rel='post_tag_re', to_table='tag') def __repr__(self): return '<Post %s>' % self.title
上面这段代码取自Lunar框架的ORM测试和一个博客的example,这段代码定义了一个Post类,代表了数据库中的一张表,类中的一系列属性分别对应着表中的列数据。
一个peewee或者SQLAlchemy类似语法的一个ORM框架语句类似下面这样:
py
p = Post.get(id=1)
返回的结果是Post类的实例,这个实例,p.id返回的不是一个PrimaryKeyField,而是一个int类型值,其他的与数据库关联的类属性也是同样。
orm框架本质上是sql语句或者数据库模式(schema)和python对象之间的转换器或者翻译器,这有些类似于编译器结构。
在这里,Post是我们创建的一个orm类,post拥有若干数据操作方法,通过调用类似这样的更加人性化或者直观的api,代替传统的sql语句和对象映射。orm框架将语句翻译为sql语句,执行,并在最后将语句转换为post类的实例。
可能从这个角度看来,实现orm框架并不是什么tough的任务,让我们用上面提到的这个例子来看
py
p = Post.get(id=1)
这条语句翻译成的sql语句为
select * from post where id=1;
可以看到的是,get方法会使用一个select语句,翻译程序将post类的表名和条件分别组合到select语句中,嗅觉灵敏的pythoner会发现这是一个典型的Post类的classmethod,直接通过类来调用这个方法,我们可以快速的写出这个函数的伪代码:
py
class Model(Meta): ... @classmethod def get(cls, *args, **kwargs): # get method only supposes to be used by querying by id. # UserModel.get(id=2) # return a single instance. sql = "select * from %s" if kwargs: sql += "where %s" rs