ZODB 讨论中

::-- hoxide [2005-02-20 06:24:40]

ZODB

简述

起因

学习笔记

ZODB的安装

基本概念

例子

   1 from ZODB import FileStorage, DB
   2 import ZODB
   3 from Persistence import Persistent
   4 from BTrees.OOBTree import OOBTree
   5 
   6 class User(Persistent):
   7     pass
   8 
   9 def test1():
  10     storage = FileStorage.FileStorage("test-filestorage.fs")
  11     db = DB(storage)
  12     conn = db.open()
  13     dbroot = conn.root()
  14     # Ensure that a 'userdb' key is present
  15     # in the root
  16     if not dbroot.has_key('userdb'):
  17         dbroot['userdb'] = OOBTree()
  18     userdb = dbroot['userdb']
  19     # Create new User instance
  20     newuser = User()
  21     # Add whatever attributes you want to track
  22     newuser.id = 'amk'
  23     newuser.first_name = 'Andrew'
  24     newuser.last_name = 'Kuchling'
  25     # Add object to the BTree, keyed on the ID
  26     userdb[newuser.id] = newuser
  27     # Commit the change
  28     get_transaction().commit()
  29     conn.close()
  30     storage.close()
  31 
  32 def test2():
  33     storage = FileStorage.FileStorage("test-filestorage.fs")
  34     db = DB(storage)
  35     conn = db.open()
  36     dbroot = conn.root()
  37     it = [dbroot]
  38     for t in it:
  39         for k, v in t.items():
  40             if isinstance(v, OOBTree):
  41                 print k, ':'
  42                 it.append(v)
  43             elif isinstance(v, User):
  44                 print 'Key:', k
  45                 print 'ID:', v.id
  46                 print 'first_name:', v.first_name
  47                 print 'last_name:', v.last_name
  48     
  49 if __name__ == "__main__":
  50     test1()
  51     test2()

逐步分解

   1 from ZODB import FileStorage, DB
   2 storage = FileStorage.FileStorage('/tmp/test-filestorage.fs')
   3 db = DB(storage)
   4 conn = db.open()

   1 import ZODB
   2 from Persistence import Persistent
   3 
   4 class User(Persistent):
   5     pass

   1 dbroot = conn.root()
   2 # Ensure that a 'userdb' key is present
   3 # in the root
   4 if not dbroot.has_key('userdb'):
   5    from BTrees.OOBTree import OOBTree
   6    dbroot['userdb'] = OOBTree()
   7 userdb = dbroot['userdb']

dbroot和userdb都是OOBTree的实例, 什么是BTree稍后解释, 你可以暂且认为是ZODB化的dict.

做userdb中插入一条User记录:

   1 # Create new User instance
   2 newuser = User()
   3 # Add whatever attributes you want to track
   4 newuser.id = 'amk'
   5 newuser.first_name = 'Andrew' ; newuser.last_name = 'Kuchling'
   6 ...
   7 # Add object to the BTree, keyed on the ID
   8 userdb[newuser.id] = newuser
   9 # Commit the change
  10 get_transaction().commit()

你也许会奇怪get_transaction()是哪来的, 有什么用? get_transaction是在import ZODB的时候加入到builtins里面的, 他获得一个事务.

   1 conn.close()
   2 storage.close()

   1     storage = FileStorage.FileStorage("test-filestorage.fs")
   2     db = DB(storage)
   3     conn = db.open()

然后获取dbroot:

   1 dbroot = conn.root()

   1     it = [dbroot]
   2     for t in it:
   3         print t
   4         for k, v in t.items():
   5             if isinstance(v, OOBTree):
   6                 print k, ':'
   7                 it.append(v)
   8             elif isinstance(v, User):
   9                 print 'Key:', k
  10                 print 'ID:', v.id
  11                 print 'first_name:', v.first_name
  12                 print 'last_name:', v.last_name

这个只是试验,证明test1的确在数据库中存放了数据.

至此例子分析完毕.

ZODB的关系模型

顾名思义他们分别是用来模拟Mapping结构和List结构的(python中的dict和list).

为什么要提供这两种结构呢?因为ZODB不能有效得处理python中的可变对象(dict和list). 当改变ZODB对象时, 应该将对象标记为脏的(dirty),这样在commit时就知道到底哪些数据需要更新了. 在ZODB对象中用'_p_changed'属性标记脏数据.

但是在改变可变类型时_p_changed并不改变, 需要用户手动设置, 如:

   1 userobj.friends.append(otherUser)
   2 userobj._p_changed = 1

PersistentMapping, PersistentList只解决了正确性的问题. 而BTree则应该是真正ZODB化的解决方案.

BTree

 OOBTree, OOBucket, OOSet, OOTreeSet, 
 IOBTree, IOBucket, IOSet, IOTreeSet, 
 OIBTree, OIBucket, OISet, OITreeSet, 
 IIBTree, IIBucket, IISet, IITreeSet, 

例子

直接拷贝了

   1 >>> from BTrees.OOBTree import OOBTree
   2 >>> t = OOBTree()
   3 >>> t.update({ 1: "red", 2: "green", 3: "blue", 4: "spades" })
   4 >>> len(t)
   5 4
   6 >>> t[2]
   7 'green'
   8 >>> s = t.keys() # this is a "lazy" sequence object
   9 >>> s
  10 <OOBTreeItems object at 0x0088AD20>
  11 >>> len(s) # it acts like a Python list
  12 4
  13 >>> s[-2]
  14 3
  15 >>> list(s) # materialize the full list
  16 [1, 2, 3, 4]
  17 >>> list(t.values())
  18 ['red', 'green', 'blue', 'spades']
  19 >>> list(t.values(1, 2))
  20 ['red', 'green']
  21 >>> list(t.values(2))
  22 ['green', 'blue', 'spades']
  23 >>> t.minKey() # smallest key
  24 1
  25 >>> t.minKey(1.5) # smallest key >= 1.5
  26 2

btree和tree set 类型的keys(),values()和items()方法返回的是"lazy" sequence, 即值在需要时才会获取.

BTree同样有可变对象的问题, 看例子:

   1 >>> L1, L2, L3 = [1], [2], [3]
   2 >>> from BTrees.OOBTree import OOSet
   3 >>> s = OOSet((L2, L3, L1)) # this is fine, so far
   4 >>> list(s.keys()) # note that the lists are in sorted order
   5 [[1], [2], [3]]
   6 >>> s.has_key([3]) # and [3] is in the set
   7 1
   8 >>> L2[0] = 5 # horrible -- the set is insane now
   9 >>> s.has_key([3]) # for example, it’s insane this way
  10 0
  11 >>> s
  12 OOSet([[1], [5], [3]])

不要用可变对象作为key啊.

子事务

ZEO的使用

例子

   1 from ZEO import ClientStorage
   2 from ZODB import DB
   3 from Persistence import Persistent
   4 from BTrees.OOBTree import OOBTree
   5 from time import time as _time
   6 from time import sleep as _sleep
   7 
   8 class ChatSession(Persistent):
   9     def __init__(self, name):
  10         self.name = name
  11         # Internal attribute: _messages holds all the chat messages.
  12         self._messages = OOBTree()
  13     def add_message(self, message):
  14         """Add a message to the channel.
  15         message -- text of the message to be added
  16         """
  17         while 1:
  18             try:
  19                 now = _time()
  20                 self._messages[now] = message
  21                 get_transaction().commit()
  22             except ConflictError:
  23                 # Conflict occurred; this process should pause and
  24                 # wait for a little bit, then try again.
  25                 _sleep(.2)
  26                 pass
  27             else:
  28                 # No ConflictError exception raised, so break
  29                 # out of the enclosing while loop.
  30                 break
  31             # end while
  32     def get_messages4time(self, T):
  33             new = []
  34             get_transaction().commit()
  35             for t, message in self._messages.items():
  36                 if t > T:
  37                     new.append((t, message))
  38             return new
  39 
  40 from cmd import Cmd
  41 class chat(Cmd):
  42     prompt = 'Chat: '
  43     def setchat(self, chatses, me):
  44         self._chatses = chatses
  45         self._me = me
  46     def emptyline(self):
  47         pass
  48     def default(self, line):
  49         self._chatses.add_message(self._me+': '+line)
  50     def do_exit(self, line):
  51         return True
  52     def do_quit(self, line):
  53         return True
  54 
  55 def pchat(conn, chatses, me):
  56     T = _time()
  57     while True:
  58         try:
  59             conn.sync()
  60             new = chatses.get_messages4time(T)
  61             if new:
  62                 for t,l  in new:
  63                     if t > T:
  64                         T = t
  65                     if not l.startswith(me+':'):
  66                         print
  67                         print l
  68             else:
  69                 _sleep(1)
  70         except SystemExit:
  71             break
  72 
  73 from sys import argv as _argv
  74 from thread import start_new_thread as _start_new_thread
  75 
  76 def main():
  77     addr = ('localhost', 7000)
  78     storage = ClientStorage.ClientStorage(addr)
  79     db = DB(storage)
  80     conn = db.open()
  81     root = conn.root()
  82     name = _argv[1]
  83     me = _argv[2]
  84     if not root.has_key(name):
  85         root[name] = ChatSession(name)
  86     _start_new_thread(pchat, (conn, root[name], me) )
  87     c = chat()
  88     c.setchat(root[name], me)
  89     c.cmdloop()
  90     
  91 if __name__ == '__main__':
  92     main()

图片

ZODB笔记 (last edited 2009-12-25 07:12:51 by localhost)