1.在已有的数据库上建立model对象有两种办法。
1> 先创建Table对象,然后创建要映射的object对象,用mapper将之映射起来。例子如下:
department = Table('crew_section',
Column('rid',Integer,primary_key=True),
Column('birthdate', NormalDate),
Column('employmenttype',Integer,ForeignKey(department.c.rid)),
meta,autoload=True,autoload_with=engine)
class Department(object):
def __repr__(self):
return "<%s>"% repr(self.__class__.__name__)
mapper(Department, department)
crew_section是数据库中表的名字,在瘟到死下大小写不敏感,所以不用区分大小写,在os x下是则需要区分大小写。
用这种办法创建出来的映射对象,只重载明确表示出来的字段如代码中的‘birthdate’字段(做自定义的类型转换)、'employmenttype'字段(做映射的外键),其他未重载的字段可以直接以属性的形式倍调用如 department.other.
2>直接继承自DeclarativeBase:
class CrewBase(DeclarativeBase):
__tablename__ ='crew_base'
#{ Attributes
id = Column('rid', Integer, primary_key=True)
name = Column('crewbasecode', PopularString)
description = Column('description', Text)
#}
def __repr__(self):
return " <Base code:%s ,description:%s>" % (self.code, self.description)
crew_base 是数据库的表名。
这样做必须对所有的字段重载,否则其他的属性不能被调用。
对于已有的数据库,建议使用这种办法,这样可以清楚表明对象的设计结构。而且可以将建好的数据库字段名改为自己认为比较合适的。(个人项目比较特殊)
2 一对多的操作(基于继承自 DeclarativeBase的办法)
department_id = Column('employmenttype', Integer, ForeignKey(Department.id))
department = relation(Department, backref=backref('crew_member', order_by=Department.id))
第一行代码将数据库中的字段以外键形式重载,第二行代码建立外键,外键映射对象,本对象之间的关系
之后可以直接实例化对象。用instance.department来取出对象的department属性。
crew_member => 本对象所映射的表名。
这里 一个department具有多个crew 主动关联的一方在crew。
3. 多对多的操作。
先将多对多关系的中间表建立Table对象:
crew_base_period = Table('crew_base_period', metadata,
Column('crewrid',Integer, ForeignKey('crew_member.rid')),
Column('crewbaserid', Integer, ForeignKey('crew_base.rid')),
Column('startdate', NormalDate),
Column('enddate', NormalDate)
)
在主动关联的一方写下如下语句:
bases = relation(CrewBase, secondary=crew_base_period, backref='crew_member')
CrewBase是要关联的对象,将中间表对象传给secondary参数,backref为自己的表。
实例化之后就可以直接调用instance.bases来查找自己想要的属性。
4.自定义类型转型
class PopularString(types.TypeDecorator):
"to remove some unuseful data and convert the decode it"
impl = types.String
def process_bind_param(self, value, dialect):
return "PREFIX:" + value
def process_result_value(self, value, dialect):
return value.strip().decode('gbk')
impl是要转化的值本来的类型,prosess_bind_param处理从程序到db的操作,prosess_bind_param处理从db到程序的操作。
定义好之后,直接将类名写到定义Column的地方就好了。如:
Column('birthdate', NormalDate)
但是很奇怪的是,用自定义好的类型,用query.filter()查询,类型转换可以起作用。如果用from_statement发送sql就要用下面的办法。
typemap = {
'lastname' : PopularString,
'firstname' : PopularString,
'birthdate' : NormalDate,
'inservicedate' : NormalDate,
'outservicedate': NormalDate,
'upgradeddate' : NormalDate,
'sex': PopularGender
}
crew = DBSession.query(Crew).from_statement(text("SELECT * from crew_member where " +
"rtrim(crew_member.lastname) || rtrim(crew_member.firstname) " +
" like '%" + crew_name + "%'", typemap=typemap)).first()
(再次说明,本人的项目很妖怪,其实不用写这么复杂,如果你不小心google到这里来了,希望不要误导了你)
需要先格外的写个text,typemap中即为{‘字段名’:自定义类型}的字典。
5. 其他的一些小问题
sqlalchemy可以直接这样操作字段(以Crew为对象)
crew = DBSession.query(Crew).filter((Crew.last_name + Crew.first_name).like(u'%XX%'.encode('gbk'))).first()
即使用 + 来代替数据库的连接符
filter 和 from_statement 似乎有冲突,不能对同一条记录查询。
6.一切框架都是浮云(by Hsu Jeffrey )
Jeffrey兄说得很对。。。最近两个星期研究sqlalchemy看来,不管是怎么样的操作,其实归根到底,依旧是化为sql来对数据库操作。所以还是得好好研究研究底层点的东西。
附件为例子,包含本文大部分的例子。