zope.generations cung cấp một cách để cập nhật các đối tượng trong cơ sở dữ liệu khi thay đổi biểu đồ ứng dụng & nbsp;. Một biểu đồ ứng dụng cơ bản là cấu trúc của dữ liệu, cấu trúc của các lớp trong trường hợp ZODB hoặc các mô tả bảng trong trường hợp một cơ sở dữ liệu quan hệ.
Tài liệu chi tiết
Các thế hệ là một cách để cập nhật các đối tượng trong cơ sở dữ liệu khi thay đổi biểu đồ ứng dụng. Một biểu đồ ứng dụng cơ bản là cấu trúc của dữ liệu, cấu trúc của các lớp trong trường hợp ZODB hoặc các mô tả bảng trong trường hợp của một cơ sở dữ liệu quan hệ.
Khi bạn thay đổi ứng dụng của bạn của cấu trúc dữ liệu, ví dụ, bạn thay đổi ý nghĩa ngữ nghĩa của một lĩnh vực hiện có trong một lớp học, bạn sẽ có một vấn đề với cơ sở dữ liệu đã được tạo ra trước khi thay đổi của bạn. Đối với một cuộc thảo luận kỹ lưỡng hơn và giải pháp có thể, xem http://wiki.zope.org/zope3/DatabaseGenerations
Chúng tôi sẽ sử dụng các thành phần kiến trúc, và chúng tôi sẽ cần một cơ sở dữ liệu và kết nối:
& Nbsp; >>> nhập cgi
& Nbsp; >>> từ pprint nhập pprint
& Nbsp; >>> từ zope.interface cụ nhập khẩu
& Nbsp; >>> từ ZODB.tests.util nhập DB
& Nbsp; >>> db = DB ()
& Nbsp; >>> conn = db.open ()
& Nbsp; >>> root = conn.root ()
Hãy tưởng tượng rằng ứng dụng của chúng tôi là một oracle: bạn có thể dạy cho nó phản ứng với cụm từ. Hãy giữ nó đơn giản và lưu trữ dữ liệu trong một dict:
& Nbsp; >>> gốc ['câu trả lời'] = {'Hello': '? Hi & làm thế nào để bạn làm ",
& Nbsp; ... '? Ý nghĩa của cuộc sống': '42',
& Nbsp; ... 'bốn ': 'Bốn
& Nbsp; >>> transaction.commit ()
thiết lập ban đầu
Dưới đây là một số mã thế hệ cụ thể. Chúng tôi sẽ tạo ra và đăng ký một SchemaManager. SchemaManagers chịu trách nhiệm về các bản cập nhật thực tế của các cơ sở dữ liệu. Điều này sẽ chỉ là một giả. Vấn đề ở đây là làm cho các thế hệ mô-đun nhận thức được rằng ứng dụng của chúng tôi hỗ trợ các thế hệ.
Việc thực hiện mặc định của SchemaManager là không thích hợp cho thử nghiệm này bởi vì nó sử dụng các mô-đun Python để quản lý thế hệ. Để bây giờ, nó sẽ chỉ tốt đẹp, vì chúng ta không muốn nó làm bất cứ điều gì chỉ được nêu ra.
& Nbsp; >>> từ zope.generations.interfaces nhập ISchemaManager
& Nbsp; >>> từ zope.generations.generations nhập SchemaManager
& Nbsp; >>> nhập zope.component
& Nbsp; >>> dummy_manager = SchemaManager (minimum_generation = 0, hệ = 0)
& Nbsp; >>> zope.component.provideUtility (
& Nbsp; ... dummy_manager, ISchemaManager, name = 'some.app')
'Some.app' là một định danh duy nhất. Bạn nên sử dụng một URI hoặc tên chấm của gói của bạn.
Khi bạn bắt đầu Zope và một cơ sở dữ liệu được mở ra, một IDatabaseOpenedWithRoot sự kiện được gửi đi. Zope đăng ký evolveMinimumSubscriber theo mặc định như một handler cho sự kiện này. Hãy mô phỏng này:
& Nbsp; >>> class DatabaseOpenedEventStub (object):
& Nbsp; ... def __init __ (self, cơ sở dữ liệu):
& Nbsp; ... self.database = cơ sở dữ liệu
& Nbsp; >>> event = DatabaseOpenedEventStub (db)
& Nbsp; >>> từ zope.generations.generations nhập evolveMinimumSubscriber
& Nbsp; >>> evolveMinimumSubscriber (sự kiện)
Hậu quả của hành động này là hiện nay các cơ sở dữ liệu chứa một thực tế là số lượng đồ hiện tại của chúng tôi là 0. Khi chúng tôi cập nhật các lược đồ, Zope3 sẽ có một ý tưởng về những gì mà điểm khởi đầu là. Ở đây, thấy không?
& Nbsp; >>> từ zope.generations.generations nhập generations_key
& Nbsp; >>> gốc [generations_key] ['some.app']
& Nbsp; 0
Trong cuộc sống thực, bạn không bao giờ phải bận tâm với chìa khoá này trực tiếp, nhưng bạn nên biết rằng nó tồn tại.
Nâng cấp kịch bản
Trở lại với câu chuyện. Một thời gian trôi qua và một trong những khách hàng của chúng tôi bị tấn công bởi vì chúng ta quên thoát HTML ký tự đặc biệt! Các kinh dị! Chúng ta phải khắc phục vấn đề này càng sớm càng tốt mà không bị mất bất kỳ dữ liệu. Chúng tôi quyết định sử dụng hệ để gây ấn tượng với các đồng nghiệp của chúng tôi.
Hãy cập nhật quản lý schema (thả một tuổi và cài đặt một tùy chỉnh mới):
& Nbsp; >>> từ zope.component globalregistry nhập khẩu
& Nbsp; >>> gsm = globalregistry.getGlobalSiteManager ()
& Nbsp; >>> gsm.unregisterUtility (cung cấp = ISchemaManager, name = 'some.app')
& Nbsp; Đúng
& Nbsp; >>> class MySchemaManager (object):
& Nbsp; ... cụ (ISchemaManager)
& Nbsp; ...
& Nbsp; ... minimum_generation = 1
& Nbsp; ... thế hệ = 2
& Nbsp; ...
& Nbsp; ... def tiến hóa (tự, bối cảnh, thế hệ):
& Nbsp; ... root = context.connection.root ()
& Nbsp; ... câu trả lời = root ['câu trả lời']
& Nbsp; ... nếu thế hệ == 1:
& Nbsp; ... cho câu hỏi, câu trả lời trong answers.items ():
& Nbsp; ... câu trả lời [câu hỏi] = cgi.escape (trả lời)
& Nbsp; ... thế hệ elif == 2:
& Nbsp; ... cho câu hỏi, câu trả lời trong answers.items ():
& Nbsp; ... del câu trả lời [câu hỏi]
& Nbsp; ... câu trả lời [cgi.escape (câu hỏi)] = câu trả lời
& Nbsp; ... khác:
& Nbsp; ... tăng ValueError ("Thật là đáng tiếc")
& Nbsp; ... root ['câu trả lời'] = câu trả lời # ping kiên trì
& Nbsp; ... transaction.commit ()
& Nbsp; >>> manager = MySchemaManager ()
& Nbsp; >>> zope.component.provideUtility (quản lý, ISchemaManager, name = 'some.app')
Chúng tôi đã thiết lập minimum_generation 1. Điều đó có nghĩa là ứng dụng của chúng tôi sẽ từ chối để chạy với một cơ sở dữ liệu lớn hơn so với thế hệ 1. Các thuộc tính thế hệ được thiết lập để 2, có nghĩa là thế hệ mới nhất mà SchemaManager này biết về là 2.
phát triển () là Workhorse đây. Nhiệm vụ của nó là để có được cơ sở dữ liệu từ thế hệ này sang thế hệ 1. Nó được một bối cảnh trong đó có các thuộc tính 'kết nối', đó là một kết nối đến ZODB. Bạn có thể sử dụng để thay đổi các đối tượng như trong ví dụ này.
Trong thế hệ thực hiện cụ thể này 1 thoát các câu trả lời (nói, quan trọng, bởi vì chúng có thể được nhập vào bởi bất cứ ai!), Thế hệ 2 thoát ra các câu hỏi (nói, ít quan trọng, bởi vì chúng có thể được nhập bằng cách ủy quyền personell chỉ).
Trong thực tế, bạn không thực sự cần một tùy chỉnh thực hiện các ISchemaManager. Một là có sẵn, chúng tôi đã sử dụng nó cho một giả trước đây. Nó sử dụng các mô-đun Python cho tổ chức các chức năng Evolver. Xem docstring của mình để biết thêm thông tin.
Trong cuộc sống thực, bạn sẽ có cấu trúc đối tượng phức tạp hơn nhiều so với những người ở đây. Để làm cho cuộc sống của bạn dễ dàng hơn, có hai chức năng rất hữu ích có sẵn trong zope.generations.utility: findObjectsMatching () và findObjectsProviding (). Họ sẽ khai thác thông qua container đệ quy để giúp bạn tìm ra các đối tượng cũ mà bạn muốn cập nhật, bởi giao diện hoặc bởi một số tiêu chí khác. Họ rất dễ hiểu, kiểm tra docstrings của họ.
Các thế hệ trong hành động
Vì vậy, khách hàng tức giận của chúng tôi tải mã mới nhất của chúng tôi và khởi động lại Zope. Sự kiện này được tự động gửi một lần nữa:
& Nbsp; >>> event = DatabaseOpenedEventStub (db)
& Nbsp; >>> evolveMinimumSubscriber (sự kiện)
Shazam! Các khách hàng là hạnh phúc một lần nữa!
& Nbsp; >>> pprint (root ['câu trả lời'])
& Nbsp; {'Hello': 'Hi & làm thế nào để bạn làm gì?',
& Nbsp; "Ý nghĩa của cuộc sống? ':' 42 ',
& Nbsp; 'bốn ': 'Bốn
& Nbsp; >>> gốc [generations_key] ['some.app']
& Nbsp; 1
Chúng tôi thấy rằng các thế hệ được làm việc, vì vậy chúng tôi quyết định đi bước tiếp theo và tiến triển đến thế hệ 2. Hãy xem cách này có thể được thực hiện bằng tay:
& Nbsp; >>> từ zope.generations.generations nhập khẩu phát triển
& Nbsp; >>> tiến hóa (db)
& Nbsp; >>> pprint (root ['câu trả lời'])
& Nbsp; {'Hello': 'Hi & làm thế nào để bạn làm gì?',
& Nbsp; "Ý nghĩa của cuộc sống? ':' 42 ',
& Nbsp; 'bốn ': 'Bốn
& Nbsp; 2
Hành vi mặc định của tiến hóa nâng cấp thế hệ mới nhất được cung cấp bởi các SchemaManager. Bạn có thể sử dụng các lập luận như thế nào để phát triển () khi bạn muốn chỉ để kiểm tra xem bạn cần phải cập nhật hoặc nếu bạn muốn được lười biếng như các thuê bao mà chúng tôi đã gọi trước đó.
Thứ tự của các nhà quản lý đồ
Thường xuyên hệ thống con sử dụng để tạo một ứng dụng dựa trên hệ thống con khác để hoạt động đúng. Nếu cả hai hệ thống con cung cấp cho các nhà quản lý lược đồ, nó thường là hữu ích để biết thứ tự trong đó các nhóm tiến hóa sẽ được gọi. Điều này cho phép một khuôn khổ và đó là khách hàng để có thể phát triển trong buổi hòa nhạc, và các khách hàng có thể biết rằng khuôn khổ sẽ được phát triển trước khi hoặc sau khi chính nó.
Điều này có thể được thực hiện bằng cách kiểm soát các tên của các tiện ích quản lý schema. Các nhà quản lý schema được chạy theo thứ tự xác định bằng cách phân loại tên của họ.
& Nbsp; >>> manager1 = SchemaManager (minimum_generation = 0, hệ = 0)
& Nbsp; >>> manager2 = SchemaManager (minimum_generation = 0, hệ = 0)
& Nbsp; >>> zope.component.provideUtility (
& Nbsp; ... manager1, ISchemaManager, name = 'another.app')
& Nbsp; >>> zope.component.provideUtility (
& Nbsp; ... manager2, ISchemaManager, name = 'another.app-phần mở rộng')
Chú ý tên của các gói đầu tiên được sử dụng để tạo ra một không gian tên cho các gói phụ thuộc. Đây không phải là một yêu cầu của khuôn khổ này, nhưng một mô hình thuận tiện cho việc sử dụng này.
Hãy phát triển các cơ sở dữ liệu để thiết lập các thế hệ:
& Nbsp; >>> event = DatabaseOpenedEventStub (db)
& Nbsp; >>> evolveMinimumSubscriber (sự kiện)
& Nbsp; >>> gốc [generations_key] ['another.app']
& Nbsp; 0
& Nbsp; >>> gốc [generations_key] ['another.app-phần mở rộng']
& Nbsp; 0
Hãy giả định rằng đối với một số lý do mỗi hệ thống con cần thêm một thế hệ, và rằng thế hệ 1 của 'another.app-phần mở rộng' phụ thuộc vào thế hệ 1 của 'another.app'. Chúng tôi sẽ cung cấp cho các nhà quản lý cần phải giản đồ cho mỗi bản ghi rằng họ đã được chạy vì vậy chúng tôi có thể xác minh kết quả:
& Nbsp; >>> gsm.unregisterUtility (cung cấp = ISchemaManager, name = 'another.app')
& Nbsp; Đúng
& Nbsp; >>> gsm.unregisterUtility (
& Nbsp; ... cung cấp = ISchemaManager, name = 'another.app-phần mở rộng')
& Nbsp; Đúng
& Nbsp; >>> class FoundationSchemaManager (object):
& Nbsp; ... cụ (ISchemaManager)
& Nbsp; ...
& Nbsp; ... minimum_generation = 1
& Nbsp; ... thế hệ = 1
& Nbsp; ...
& Nbsp; ... def tiến hóa (tự, bối cảnh, thế hệ):
& Nbsp; ... root = context.connection.root ()
& Nbsp; ... đặt hàng = root.get ('đặt hàng', [])
& Nbsp; ... nếu thế hệ == 1:
& Nbsp; ... ordering.append ("nền tảng 1 ')
& Nbsp; ... print 'nền tảng thế hệ 1'
& Nbsp; ... khác:
& Nbsp; ... tăng ValueError ("Thật là đáng tiếc")
& Nbsp; ... root ['đặt hàng'] = đặt hàng # ping kiên trì
& Nbsp; ... transaction.commit ()
& Nbsp; >>> class DependentSchemaManager (object):
& Nbsp; ... cụ (ISchemaManager)
& Nbsp; ...
& Nbsp; ... minimum_generation = 1
& Nbsp; ... thế hệ = 1
& Nbsp; ...
& Nbsp; ... def tiến hóa (tự, bối cảnh, thế hệ):
& Nbsp; ... root = context.connection.root ()
& Nbsp; ... đặt hàng = root.get ('đặt hàng', [])
& Nbsp; ... nếu thế hệ == 1:
& Nbsp; ... ordering.append ('phụ thuộc 1')
& Nbsp; ... print 'thế hệ phụ thuộc 1'
& Nbsp; ... khác:
& Nbsp; ... tăng ValueError ("Thật là đáng tiếc")
& Nbsp; ... root ['đặt hàng'] = đặt hàng # ping kiên trì
& Nbsp; ... transaction.commit ()
& Nbsp; >>> manager1 = FoundationSchemaManager ()
& Nbsp; >>> manager2 = DependentSchemaManager ()
& Nbsp; >>> zope.component.provideUtility (
& Nbsp; ... manager1, ISchemaManager, name = 'another.app')
& Nbsp; >>> zope.component.provideUtility (
& Nbsp; ... manager2, ISchemaManager, name = 'another.app-phần mở rộng')
Phát triển cơ sở dữ liệu sẽ luôn luôn chạy 'another.app' Evolver trước khi 'another.app-phần mở rộng' Evolver:
& Nbsp; >>> event = DatabaseOpenedEventStub (db)
& Nbsp; >>> evolveMinimumSubscriber (sự kiện)
& Nbsp; thế hệ nền tảng 1
& Nbsp; hệ phụ thuộc 1
& Nbsp; >>> gốc ['đặt hàng']
& Nbsp; ['nền tảng 1', 'phụ thuộc 1']
Cài đặt
Trong ví dụ trên, chúng ta tự khởi tạo các câu trả lời. Chúng ta không cần phải làm điều đó bằng tay. Ứng dụng này sẽ có thể làm điều đó tự động.
IInstallableSchemaManager kéo dài ISchemaManager, cung cấp một phương pháp cài đặt để tiến hành cài đặt ban đầu của một ứng dụng. Đây là một lựa chọn tốt hơn so với đăng ký cơ sở dữ liệu mở thuê bao.
Hãy xác định một người quản lý sơ đồ mới bao gồm cài đặt:
& Nbsp; >>> gsm.unregisterUtility (cung cấp = ISchemaManager, name = 'some.app')
& Nbsp; Đúng
& Nbsp; >>> từ zope.generations.interfaces nhập IInstallableSchemaManager
& Nbsp; >>> class MySchemaManager (object):
& Nbsp; ... cụ (IInstallableSchemaManager)
& Nbsp; ...
& Nbsp; ... minimum_generation = 1
& Nbsp; ... thế hệ = 2
& Nbsp; ...
& Nbsp; ... def cài đặt (bản thân, ngữ cảnh):
& Nbsp; ... root = context.connection.root ()
& Nbsp; ... root ['câu trả lời'] = {'Hello': '? Hi & làm thế nào để bạn làm ",
& Nbsp; ... '? Ý nghĩa của cuộc sống': '42',
& Nbsp; ... 'bốn ': 'Bốn
& Nbsp; ...
& Nbsp; ... def tiến hóa (tự, bối cảnh, thế hệ):
& Nbsp; ... root = context.connection.root ()
& Nbsp; ... câu trả lời = root ['câu trả lời']
& Nbsp; ... nếu thế hệ == 1:
& Nbsp; ... cho câu hỏi, câu trả lời trong answers.items ():
& Nbsp; ... câu trả lời [câu hỏi] = cgi.escape (trả lời)
& Nbsp; ... thế hệ elif == 2:
& Nbsp; ... cho câu hỏi, câu trả lời trong answers.items ():
& Nbsp; ... del câu trả lời [câu hỏi]
& Nbsp; ... câu trả lời [cgi.escape (câu hỏi)] = câu trả lời
& Nbsp; ... khác:
& Nbsp; ... tăng ValueError ("Thật là đáng tiếc")
& Nbsp; ... root ['câu trả lời'] = câu trả lời # ping kiên trì
& Nbsp; ... transaction.commit ()
& Nbsp; >>> manager = MySchemaManager ()
& Nbsp; >>> zope.component.provideUtility (quản lý, ISchemaManager, name = 'some.app')
Bây giờ, cho phép mở một cơ sở dữ liệu mới:
& Nbsp; >>> db.close ()
& Nbsp; >>> db = DB ()
& Nbsp; >>> conn = db.open ()
& Nbsp; >>> 'câu trả lời' ở conn.root ()
& Nbsp; False
& Nbsp; >>> event = DatabaseOpenedEventStub (db)
& Nbsp; >>> evolveMinimumSubscriber (sự kiện)
& Nbsp; >>> conn.sync ()
& Nbsp; >>> root = conn.root ()
& Nbsp; >>> pprint (root ['câu trả lời'])
& Nbsp; {'Hello': 'Hi & làm thế nào để bạn làm gì?',
& Nbsp; "Ý nghĩa của cuộc sống? ':' 42 ',
& Nbsp; 'bốn ': 'Bốn
& Nbsp; 2
Nhật ký giao dịch ZODB lưu ý rằng kịch bản cài đặt của chúng tôi đã được thực hiện
& Nbsp; >>> [. It.description cho nó trong conn.db storage.iterator () ()] [- 2]
& Nbsp; u'some.app: chạy cài đặt hệ '
(Lưu ý nhỏ: nó không phải là kỷ lục cuối cùng bởi vì có hai cam kết: MySchemaManager thực hiện một, và evolveMinimumSubscriber thực hiện điều thứ hai MySchemaManager không thực sự cần phải cam kết..)
Điều gì là mới trong phiên bản này:.
- Thêm hỗ trợ cho Python 3.3
- Thay thế phản zope.interface.implements sử dụng tương đương với zope.interface.implementer trang trí.
- Dropped hỗ trợ cho Python 2.4 và 2.5.
là gì mới trong phiên bản 3.7.1:
- Gỡ bỏ phần buildout đó đã được sử dụng trong quá trình phát triển nhưng không không biên dịch trên Windows.
- kịch bản thế hệ thêm một lưu ý giao dịch.
Yêu cầu :
- Python
Bình luận không