当前位置: 首页 > 工具软件 > SQLObject > 使用案例 >

[SQLObject官方文档] 继承

堵泽宇
2023-12-01
·sqlmeta类
这是在SQLObject 0.7中引入的,允许使用一种清晰的方式指定数据库的元数据,而不需要使用类的命名空间中的属性来指定。
有一些特别的属性可以用在这个类当中,可以用于改变类的行为。他们包括:
table:
数据库的表名,是从style派生而来的,仅用于没有指定类名时。如果没有指定名字,且没有定义可选的style,则标准方式是指定MixedCase为mixed_case。
idName:
指定数据库表的主键名,如未指定时继承自style。缺省为id。
idType:
设置ID时的强制函数。缺省为int。
style:
一个样式(style)对象,这个对象允许使用其他算法翻译Python属性和类名称与数据库列名和表名之间的对应关系。参考"Changing the Naming Style"了解更多。它是一个IStyle实例的接口。
lazyUpdate:
布尔值,缺省为False。如果为True,那么改变属性时不会自动更新查询。而等待调用inst.syncUpdates()或inst.sync()时才执行更新。
defaultOrder:
查询数据时的缺省排序方式。
cacheValues:
布尔值,缺省为True。如果为True,保存在行中的值一直缓存到inst.expire()被调用。如果设为False,属性值不会被缓存,所以每次存取一个属性的值都会使用查询来返回结果。如果需要处理多进程并发处理时,也许需要这样。当然也可以使用事务(transactions),这不是默认的。
registry:
SQLObject使用字符串来连接一个类,且这些类必须避开模块名,而又是又会出现在不同的系统中的命名冲突。这个属性提供了类的命名空间。
fromDatabase:
布尔值,缺省为False。如果为True,创建一个类时会自动查询数据库的各个列,如果有缺失的列则自动加上。
columns:
形如{columnName:anSOColInstance}形式的字典。可以通过只读属性获取列的信息。
columnList:
columns的列表,用于需要一个有序而牢固的列信息列表时使用(by gashero)。
columnDefinitions:
类似columns的字典,但是包含列的原始定义信息。并非是特定类的,且没有逻辑。
joins:
这个类的所有联系对象。
indexes:
这个类的所有索引。
createSQL:
创建表之后的SQL查询。createSQL可以是单行的SQL命令或者是一些SQL命令组成的列表,或者是按照数据库名(dbNames)和值组成的字典,值可以是单行SQL或者是SQL列表。这经常用于ALTER TABLE命令来修改表定义。
expired:
布尔值。如果为True,那么下次存取对象列属性时,将会执行查询。

在上一版本的SQLObject中这些属性是作为类的属性而直接存在的,属性名前加上一个下划线。现在推荐将代码改成新的样式(style)。这些旧的方法在SQLObject 0.8释出时将不再支持。
注意:当继承SQLObject时,sqlmeta属性不会继承。也无法通过sqlmeta.columns词典访问父类列对象。

使用sqlmeta
按照如下代码:
class MyClass(SQLObject):
    class sqlmeta:
        lazyUpdate=True
        cacheValues=False
    columnA=StringCol()
    columnB=IntCol() #by gashero
    def _set_attr1(self,value):
        #设置值时需要做的事情
    def _get_attr1(self):
        #取得值时需要作的事情
如上定义将会创建表my_class(表名在更改style属性时会有所不同),包含两个列columnA和columnB。还有第三个可以被存取的属性MyClass.attr1。sqlmeta改变了MyClass的行为,可以延迟更新,并告知不要使用缓存,所以每次请求信息时都会查询数据库。

·SQLObject
除了sqlmeta和列规范之外,其他的特定属性可以设置在类中。这些属性可以用除了_connection属性之外都可以在sqlmeta中定义。如果在SQLObject 0.7中使用,可以得到不赞成使用的警告。最好及时修改代码来保持对未来的兼容。
_connection:
使用的连接对象,从DBConnection类实现。也可以在包定义中使用__connection__供使用,但是必须确保在类定义之前定义__connection__。也可以在实例创建时传递connection对象,有如transaction中描述的那样。
如果已经定义了sqlhub.processConnection,则会忽略这个属性而使用sqlhub。如果只有少数几个类使用相同的连接是有好处的,除了(besides)使用了多种类型。
_table:
这是旧样式(style)的属性,等同于sqlmeta类中的table属性。
_joins:
同上,对应于sqlmeta的joins属性。
_cacheValues:
同上,对应于sqlmeta的cacheValues属性。
_idName:
同上,对应于sqlmeta的idName属性。
_style:
同上,对应于sqlmeta的style属性。

·自定义对象
自定义类往往需要一些自定义方法,但是需要注意一些细节。
初始化对象:
有两种方式实例化SQLObject对象,从数据库引出和插入数据库。相同的是都要创建Python对象。这导致了__init__的微笑差异。
一般来说,不需要更改__init__。作为替换的_init方法,可以在引出或插入之后执行。方法定义如同_init(self,id,connection=None,selectResults=None),也许你喜欢使用_init(self,*args,**kw)的形式。注意,如果重载方法,一定要调用SQLObject._init(self,*args,**kw)。
添加魔术属性:
你可以使用任何已有的技术来定义这种新样式中的方法,包括classmethod(类方法),static(静态方法),和property(属性),而且还可以使用捷径。比如你有一个方法名以_set_、_get_、_del_、_doc_开始,它将会被用于创建属性。所以,如果对应Person的ID下包含图像在/var/people/images目录下,可以使用:
class Person(SQLObject):
    # ...
    def imageFilename(self):
        return 'images/person-%s.jpg'%self.id
    def _get_image(self):
        if not os.path.exists(self.imageFilename()):
            return None
        f=open(self.imageFilename())
        v=f.read()
        f.close()
        return v
    def _set_image(self,value):
        f=open(self.imageFilename(),'w')
        f.write(value)
        f.close()
    def _del_image(self,value):
        os.unlink(self.imageFilename())
然后,可以像使用普通属性(attribute)一样使用.image属性(property)。对它的修改也会直接反映到文件系统当中。这是保存无意义数据的好方法。
同样也可以传递一个image关键字参数到构造方法或者set方法,形如Person(...,image=imageText)。所有的方法(_get_、_set_等)都是可选的,(by gashero)你可以使用其中的任何一个而省略其他的。这样如果只定义了_get_attr方法,那么attr属性就是只读的。

重载列属性:
重载数据库列属性时有些复杂。例如(for instance),想要在改变一个人名字的时候执行特定代码。在大多数系统中你需要自己实现这个功能,然后再调用父类的代码。但是父类(SQLObject)并不知道子类的列。

SQLObject创建了形如_set_lastName的方法定义你的列,当时当你再次想要使用时却发现父类没有相关引用(即不可以写SQLObject._set_lastName(...),因为SQLObject类并不知道你的类的列)。你需要自己重载_set_lastName方法。
为了处理这种问题,SQLObjec类创建了两个方法作为getter和setter,例如:_set_lastName和_SO_set_lastName。所以可以截获所有对lastName的更改:
class Person(SQLObject):
    lastName=StringCol()
    firstName=StringCol()
    def _set_lastName(self,value):
        self.notifyLastNameChange(value)
        self._SO_set_lastName(value)
或者你可能想要包含电话号码的数字,需要限制长度,可以按照如下格式:
import re
class PhoneNumber(SQLObject):
    phoneNumber=StringCol(length=30)
    _garbageCharactersRE=re.compile(r'[/-/./(/) ]')
    _phoneNumberRE=re.compile(r'^[0-9]+$')
    def _set_phoneNumber(self,value):
        value=self._garbageCharactersRE.sub('',value)
        if not len(value)>=10:
            raise ValueError(
                'Phone numbers must be at least 10 digits long')
        if not self._phoneNumberRE.match(value):
            raise ValueError,'Phone numbers can contain only digits'
        self._SO_set_phoneNumber(value)
    def _get_phoneNumber(self):
        value=self._SO_get_phoneNumber()
        number='(%s) %s-%s'%(value[0:3],value[3:6],value[6:10])
        if len(value) > 10:
            number+=' ext.%s'%value[10:]
        return number
在修改从属性中获得的数据时必须小心。有些时候,人们希望设置与返回的值相同。这个例子中我们在存入数据库之前去除了一些字符,并在取出的时候重新格式化了。这个方法(反对存取属性)的优点之一是程序员往往希望将这些分开。

当然,还应该注意,这些转换在存入和取出时都会发生,但是在查询的时候却不会发生。所以如果你将值从Pythonic形式转换为SQLish形式时,你的查询(当使用.select()或者.selectBy()方法)需要使用SQL/Database形式(因为这些命令是按照SQL来在数据库上运行的)。

取消属性定义(Undefined attributes)

还有一个有用的特性,因为你有时需要返回奇怪的结果。SQLObject在你设置一个未定义的属性时不会跑出异常;这很好解释,并且不会改变数据库。他的工作方式有如其他Python类一样,但是在SQLObject类中却没有。
这在有些时候可能会出问题,如果你已经有了一个'name'属性,而你却写了'a.namme="Victor"',这时不会跑出异常,但是却是错误的。
 
 类似资料: