sqlalchemy 示例
正文开始前,不得不吐糟一下 ORM。
在很久很久之前,我是一个坚定的 ORM 黑,基于对数据库优化有点小研究,感觉这东西就是脱了裤子放 P,在带来性能下降的同时,带来的很多很重包依赖,项目中也在使用基本类似 SQL 语句的方案读写数据库。合作的人多了起来,直到有一天,我发现程序变成了这个样子:
class User(object): def get_by_id(self, id): ... def get_by_email(self, email): ... def get_all(self): ...
这个例子有点夸张,但总结一下,就是没有固定的标准,合作起来就容易错乱。
然后我要做的当然是整理下代码结构,不要这么乱来,结果越整理,发现越像 ORM,所以还是直接用 ORM 算了~
好了,正文开始。
几乎所有的例子都是从这个开始的:
class User(Base): __tablename__ = 'users' id = Column(Integer, primary_key=True) name = Column(String) email = Column(String) password = Column(String)
但我觉得这种定义对我毫无意义:
首先,我的数据库是多个项目都需要读写的,我有一个单独的 alembic 项目负责维护数据库的变动,我没有理由要在其它项目需要的字段发生变动时,跑到这个项目里修改代码;
其次,这些值的类型在 alembic 里都定义过,数据库里都是可以取到的,我没有理由需要重新定义一遍,我应该只需要定义 alembic 里没有的,需要用到的外键。
在问过几个练过 sqlalchemy 的朋友之后,得到的结论是:我太懒了,写几个不会死人,要为懒付出代价。 = ____ =
很喜欢 Ruby 里的 Active Record ,它几乎是一个完美的框架,sqlalchemy 倒是有一个叫 Elixir (这货不是 Erlang VM 上的语言,只是同名而已。。。)的扩展,跟 Active Record 很像,但最近更新时间是三年之前。
不多说了,贴代码了,各种 Google + 文档 + 看源码 的总结:
1. 以下代码很大程度上参考了 flask-sqlalchemy 项目。
2. 以下代码只适合接入已有数据库,不创建和修改表结构。
from sqlalchemy.orm.exc import UnmappedClassError from sqlalchemy.ext.declarative import declared_attr, declarative_base from sqlalchemy import create_engine engine = create_engine('mysql://root:@127.0.0.1/db?charset=utf8', echo=True) Session = sessionmaker(bind=engine) Base = declarative_base() __all__ = ['db'] class Base(object): @declared_attr def __table__(cls): return Table(cls.__tablename__, MetaData(), autoload=True, autoload_with=engine) class _QueryProperty(object): def __init__(self, sa): self.sa = sa def __get__(self, obj, t): try: mapper = class_mapper(t) if mapper: return t.query_class(mapper, session=self.sa.session) except UnmappedClassError: return None class MyDB(object): def __init__(self): self.Model = self.make_declarative_base() self.session = Session() def make_declarative_base(self): base = declarative_base(cls=Base) base.query = _QueryProperty(self) base.query_class = Query return base db = MyDB()
address.py
import db class Address(db.Model): __tablename__ = 'addresses'
user.py
from sqlalchemy.orm import relationship, foreign from address import Address import db class User(db.Model): __tablename__ = 'users' address = relationship('Address', primaryjoin='User.address_id == foreign(Address.id)', uselist=False)
接下来就可以在程序里
u = User.query.get(1) u.address
这样就算是基本完成了,以后有时间会把 Entity 整理一下,越来越觉得,Grape 真是个了不起的 web 框架!
- Posted in: 开发