zope.generations надає можливість поновлення об'єктів в базі даних при зміні схеми програми та NBSP ;. Схема застосування, по суті, структура даних, структура класів у разі ZODB або опису таблиць в разі реляційна база даних.
Докладна документація
Поколінь спосіб оновлення об'єктів в базі даних, коли зміни схеми програми. Схема застосування, по суті, структура даних, структура класів у разі ZODB або описів таблиць у випадку реляційної базі даних.
При зміні структури даних вашого застосування, наприклад, можна змінити смислове значення існуючого поля в класі, ви будете мати проблеми з базами даних, які були створені перед зміною. Для більш ретельного обговорення та можливі рішення см http://wiki.zope.org/zope3/DatabaseGenerations
Ми будемо використовувати компонентну архітектуру, і нам знадобиться база даних і підключення:
І NBSP; >>> імпорту CGI
І NBSP; >>> від pprint pprint імпорту
І NBSP; >>> від zope.interface знарядь імпортних
І NBSP; >>> від ZODB.tests.util імпорт DB
І NBSP; >>> дБ = DB ()
І NBSP; >>> підключень = db.open ()
І NBSP; >>> корінь = conn.root ()
Уявіть собі, що наш додаток Oracle: ви можете навчити його реагувати на фрази. Давайте тримати його простим і зберігати дані в Словнику:
І NBSP; >>> корінь ['відповіді'] = {'Hello': '? Привіт і як ви це робите ",
І NBSP; ... »? Сенс життя": "42",
І NBSP; ... чотири ':' Чотири <п'ять "}
І NBSP; >>> імпортної угоди
І NBSP; >>> transaction.commit ()
Початкова настройка
Ось кілька поколінь код, специфічний. Ми створити і зареєструвати SchemaManager. SchemaManagers несуть відповідальність за фактичні оновлень бази даних. Він буде просто манекен. Справа в тому, щоб зробити покоління модуль відомо, що наш додаток підтримує поколінь.
Реалізація за замовчуванням SchemaManager не підходить для цього тесту, тому що він використовує модулі Python для управління поколінь. На даний момент, це буде просто відмінно, так як ми не хочемо, щоб зробити що-небудь тільки поки.
І NBSP; >>> від zope.generations.interfaces імпортувати ISchemaManager
І NBSP; >>> від zope.generations.generations імпортувати SchemaManager
І NBSP; >>> імпорту zope.component
І NBSP; >>> dummy_manager = SchemaManager (minimum_generation = 0, генерація = 0)
І NBSP; >>> zope.component.provideUtility (
І NBSP; ... dummy_manager, ISchemaManager, ім'я = 'some.app')
"Some.app» є унікальний ідентифікатор. Ви повинні використовувати URI або пунктирну ім'я пакета.
Коли ви починаєте Zope і відкритий бази даних, IDatabaseOpenedWithRoot подія буде надіслано. Zope реєструє evolveMinimumSubscriber за умовчанням як обробника цієї події. Давайте моделювати наступним чином:
І NBSP; >>> класу DatabaseOpenedEventStub (об'єкт):
І NBSP; ... Def __init __ (сам, бази даних):
І NBSP; ... self.database = бази даних
І NBSP; >>> подія = DatabaseOpenedEventStub (дБ)
І NBSP; >>> від zope.generations.generations імпортувати evolveMinimumSubscriber
І NBSP; >>> evolveMinimumSubscriber (подія)
Наслідком цієї акції в тому, що в даний час база даних містить той факт, що наша нинішня номер схеми 0. Якщо ми оновлюємо схему, Zope3 будете мати уявлення про те, що відправною точкою було. Ось, бачиш?
І NBSP; >>> від zope.generations.generations імпортувати generations_key
І NBSP; >>> корінь [generations_key] ['some.app']
І NBSP; 0
У реальному житті ви ніколи не повинні возитися з цим ключем відразу, але ви повинні знати, що вона існує.
Оновлення сценарій
Повернутися до історії. Деякий час проходить, і один з наших клієнтів отримує зламали, бо ми забули, щоб уникнути HTML спеціальні символи! Жах! Ми повинні вирішити цю проблему якомога швидше без втрати даних. Ми вирішили використовувати поколінь, щоб справити враження наших однолітків.
Давайте відновимо менеджер схеми (падіння старий і встановити новий звичай один):
І NBSP; >>> від zope.component імпорту globalregistry
І NBSP; >>> GSM = globalregistry.getGlobalSiteManager ()
І NBSP; >>> gsm.unregisterUtility (за умови, = ISchemaManager, ім'я = 'some.app')
І NBSP; Правда
І NBSP; >>> класу MySchemaManager (об'єкт):
І NBSP; ... інвентар (ISchemaManager)
І NBSP; ...
І NBSP; ... minimum_generation = 1
І NBSP; ... покоління = 2
І NBSP; ...
І NBSP; ... Def розвиватися (я, контекст, покоління):
І NBSP; ... корінь = context.connection.root ()
і NBSP; ... Відповіді = корінь ['відповіді']
І NBSP; ... якщо покоління == 1:
І NBSP; ... відповідь на питання, відповідь на answers.items ():
І NBSP; ... Відповіді [питання] = cgi.escape (відповідь)
І NBSP; ... Еліф покоління == 2:
І NBSP; ... відповідь на питання, відповідь на answers.items ():
І NBSP; ... дель відповіді [питання]
І NBSP; ... Відповіді [cgi.escape (питання)] = відповідь
І NBSP; ... інше:
І NBSP; ... підняти ValueError ("облом")
І NBSP; ... корінь ['відповіді'] = Відповіді наполегливість # пінг
І NBSP; ... transaction.commit ()
І NBSP; >>> менеджер = MySchemaManager ()
І NBSP; >>> zope.component.provideUtility (менеджер, ISchemaManager, ім'я = 'some.app')
Ми створили minimum_generation 1. Це означає, що наш додаток не запуститься з базою даних старше покоління 1. атрибут створення файлів 2, що означає, що останнє покоління, що це SchemaManager знає про 2.
розвиватися () є робочою конячкою тут. Його робота, щоб отримати базу даних з покоління-1 в покоління. Він отримує контекст, який має атрибут "Підключення", який є підключення до ZODB. Ви можете використовувати це, щоб змінювати об'єкти, як у цьому прикладі.
У даному конкретному поколінні реалізації 1 втечу відповіді (скажімо, критичні, тому що вони можуть бути введені будь-яким!), Генерації 2 тікає запитання (скажімо, менш важливі, тому що вони можуть бути введені уповноваженим Оренда персоналу тільки).
Справді, ви дійсно не потрібно власну реалізацію ISchemaManager. Одним з них є доступні, ми використовували його для манекена раніше. Він використовує модулі Python для організації функцій Evolver. См свій рядок документації для отримання додаткової інформації.
У реальному житті, ви будете мати набагато більш складні структури об'єктів, ніж один тут. Щоб зробити життя простіше, є дві дуже корисні функції, доступні в zope.generations.utility: findObjectsMatching () і findObjectsProviding (). Вони будуть копатися в контейнерах рекурсивно, щоб допомогти вам шукати старі об'єкти, які ви хочете оновити, інтерфейсом або яким-небудь іншим критеріям. Вони легкі для розуміння, перевірити їх рядки документації.
Покоління в дії
Таким чином, наша лють клієнт завантажує наш останній код і запускає Zope. Подія автоматично відправлено разів:
І NBSP; >>> подія = DatabaseOpenedEventStub (дБ)
І NBSP; >>> evolveMinimumSubscriber (подія)
Shazam! Клієнт знову щаслива!
І NBSP; >>> pprint (корінь ['відповіді'])
І NBSP; {'Hello': 'Привіт і як ви це робите? ",
І NBSP; 'Сенс життя?': '42',
І NBSP; 'чотири ': 'Чотири <п'ять "}
Тому що evolveMinimumSubscriber дуже ледачий, він тільки оновлює базу даних достатньо, щоб ваш додаток може використовувати його (в minimum_generation, тобто). Справді, маркер вказує, що генерація база даних була доведена до 1:
І NBSP; >>> корінь [generations_key] ['some.app']
І NBSP; 1
Ми бачимо, що покоління працюють, тому ми вирішили зробити наступний крок і розвиватися в покоління 2. Давайте подивимося, як це можна зробити вручну:
І NBSP; >>> від zope.generations.generations імпортувати розвиватися
І NBSP; >>> розвиватися (дБ)
І NBSP; >>> pprint (корінь ['відповіді'])
І NBSP; {'Hello': 'Привіт і як ви це робите? ",
І NBSP; 'Сенс життя?': '42',
І NBSP; 'чотири ': 'Чотири <п'ять "}
І NBSP; >>> корінь [generations_key] ['some.app']
І NBSP; 2
За замовчуванням поведінку розвиватися модернізації до останнього покоління, наданої SchemaManager. Ви можете використовувати Цей аргумент розвиватися (), коли ви хочете просто перевірити, якщо ви хочете оновити або якщо ви хочете бути лінивим, як абоненту, який ми назвали раніше.
Замовлення менеджерів схеми
Часто підсистем використовуються для створення додатка покладаються на інші підсистеми для правильної роботи. Якщо обидві підсистеми надати менеджерам схеми, часто буває корисно знати порядок, в якому буде викликатися в еволюціонують. Це дозволяє основу, і це клієнти, щоб мати можливість розвиватися в концерті, і клієнти можуть знати, що концепція буде перетворився до або після себе.
Це може бути досягнуто шляхом управління імена утиліт менеджера схеми. Менеджери схеми виконуються в порядку, що визначається сортування їхні імена.
І NBSP; >>> manager1 = SchemaManager (minimum_generation = 0, генерація = 0)
І NBSP; >>> manager2 = SchemaManager (minimum_generation = 0, генерація = 0)
І NBSP; >>> zope.component.provideUtility (
І NBSP; ... manager1, ISchemaManager, ім'я = 'another.app')
І NBSP; >>> zope.component.provideUtility (
І NBSP; ... manager2, ISchemaManager, ім'я = 'another.app-розширення »)
Зверніть увагу, як ім'я першого пакету використовується для створення простору імен для залежних пакетів. Це не є обов'язковою вимогою в рамках, але зручно шаблон для такого використання.
Давайте розвиватися базу даних, щоб встановити ці покоління:
І NBSP; >>> подія = DatabaseOpenedEventStub (дБ)
І NBSP; >>> evolveMinimumSubscriber (подія)
І NBSP; >>> корінь [generations_key] ['another.app']
І NBSP; 0
І NBSP; >>> корінь [generations_key] ['another.app-розширення']
І NBSP; 0
Давайте припустимо, що з якоїсь причини кожна з цих підсистем необхідно додати покоління, і це покоління 1 "another.app-розширення" залежить від покоління 1 "another.app». Нам потрібно надати керівникам схеми для кожного цього запису, що вони були запускати, щоб ми могли перевірити результат:
І NBSP; >>> gsm.unregisterUtility (за умови, = ISchemaManager, ім'я = 'another.app')
І NBSP; Правда
І NBSP; >>> gsm.unregisterUtility (
І NBSP; ... за умови, = ISchemaManager, ім'я = 'another.app-розширення »)
І NBSP; Правда
І NBSP; >>> класу FoundationSchemaManager (об'єкт):
І NBSP; ... інвентар (ISchemaManager)
І NBSP; ...
І NBSP; ... minimum_generation = 1
І NBSP; ... покоління = 1
І NBSP; ...
І NBSP; ... Def розвиватися (я, контекст, покоління):
І NBSP; ... корінь = context.connection.root ()
І NBSP; ... порядок = root.get ('порядок', [])
І NBSP; ... якщо покоління == 1:
І NBSP; ... ordering.append ('Фонд 1 ")
І NBSP; ... покоління основа 1 »друк
І NBSP; ... інше:
І NBSP; ... підняти ValueError ("облом")
І NBSP; ... корінь ['впорядкування'] = замовлення # пінг наполегливість
І NBSP; ... transaction.commit ()
І NBSP; >>> класу DependentSchemaManager (об'єкт):
І NBSP; ... інвентар (ISchemaManager)
І NBSP; ...
І NBSP; ... minimum_generation = 1
І NBSP; ... покоління = 1
І NBSP; ...
І NBSP; ... Def розвиватися (я, контекст, покоління):
І NBSP; ... корінь = context.connection.root ()
І NBSP; ... порядок = root.get ('порядок', [])
І NBSP; ... якщо покоління == 1:
І NBSP; ... ordering.append ('залежить 1')
І NBSP; ... Друк "залежить покоління 1"
І NBSP; ... інше:
І NBSP; ... підняти ValueError ("облом")
І NBSP; ... корінь ['впорядкування'] = замовлення # пінг наполегливість
І NBSP; ... transaction.commit ()
І NBSP; >>> manager1 = FoundationSchemaManager ()
І NBSP; >>> manager2 = DependentSchemaManager ()
І NBSP; >>> zope.component.provideUtility (
І NBSP; ... manager1, ISchemaManager, ім'я = 'another.app')
І NBSP; >>> zope.component.provideUtility (
І NBSP; ... manager2, ISchemaManager, ім'я = 'another.app-розширення »)
Розвиток бази даних тепер завжди буде працювати "another.app" Evolver до "another.app-розширення" Evolver:
І NBSP; >>> подія = DatabaseOpenedEventStub (дБ)
І NBSP; >>> evolveMinimumSubscriber (подія)
І NBSP; покоління основа 1
І NBSP; залежати покоління 1
І NBSP; >>> корінь ['впорядкування']
І NBSP; ['основа 1', 'залежить 1']
Установка
У наведеному вище прикладі, ми вручну ініціалізувати відповіді. Ми не повинні робити це вручну. Додаток повинен бути в змозі зробити це автоматично.
IInstallableSchemaManager поширюється ISchemaManager, забезпечуючи метод установки для виконання intial установки програми. Це краще, ніж альтернативні реєстрації абонентів в базі даних відкритий.
Давайте визначимо новий менеджер схеми, яка включає установку:
І NBSP; >>> gsm.unregisterUtility (за умови, = ISchemaManager, ім'я = 'some.app')
І NBSP; Правда
І NBSP; >>> від zope.generations.interfaces імпортувати IInstallableSchemaManager
І NBSP; >>> класу MySchemaManager (об'єкт):
І NBSP; ... інвентар (IInstallableSchemaManager)
І NBSP; ...
І NBSP; ... minimum_generation = 1
І NBSP; ... покоління = 2
І NBSP; ...
І NBSP; ... Def встановити (я, контекст):
І NBSP; ... корінь = context.connection.root ()
І NBSP; ... корінь ['відповіді'] = {'Hello': '? Привіт і як ви це робите ",
І NBSP; ... »? Сенс життя": "42",
І NBSP; ... чотири ':' Чотири <п'ять "}
І NBSP; ... transaction.commit ()
І NBSP; ...
І NBSP; ... Def розвиватися (я, контекст, покоління):
І NBSP; ... корінь = context.connection.root ()
і NBSP; ... Відповіді = корінь ['відповіді']
І NBSP; ... якщо покоління == 1:
І NBSP; ... відповідь на питання, відповідь на answers.items ():
І NBSP; ... Відповіді [питання] = cgi.escape (відповідь)
І NBSP; ... Еліф покоління == 2:
І NBSP; ... відповідь на питання, відповідь на answers.items ():
І NBSP; ... дель відповіді [питання]
І NBSP; ... Відповіді [cgi.escape (питання)] = відповідь
І NBSP; ... інше:
І NBSP; ... підняти ValueError ("облом")
І NBSP; ... корінь ['відповіді'] = Відповіді наполегливість # пінг
І NBSP; ... transaction.commit ()
І NBSP; >>> менеджер = MySchemaManager ()
І NBSP; >>> zope.component.provideUtility (менеджер, ISchemaManager, ім'я = 'some.app')
Тепер, давайте відкрити нову базу даних:
І NBSP; >>> db.close ()
І NBSP; >>> дБ = DB ()
І NBSP; >>> підключень = db.open ()
І NBSP; "Відповіді" >>> в conn.root ()
І NBSP; Помилкові
І NBSP; >>> подія = DatabaseOpenedEventStub (дБ)
І NBSP; >>> evolveMinimumSubscriber (подія)
І NBSP; >>> conn.sync ()
І NBSP; >>> корінь = conn.root ()
І NBSP; >>> pprint (корінь ['відповіді'])
І NBSP; {'Hello': 'Привіт і як ви це робите? ",
І NBSP; 'Сенс життя?': '42',
І NBSP; 'чотири ': 'Чотири <п'ять "}
І NBSP; >>> корінь [generations_key] ['some.app']
І NBSP; 2
Журнал ZODB угода зазначає, що наша встановити скрипт був виконаний
І NBSP; >>> [. It.description його в conn.db () storage.iterator ()] [- 2]
І NBSP; u'some.app: біг встановити покоління "
(Мінорній ноті: це не остання запис, тому що є дві фіксації: MySchemaManager виконує одну і evolveMinimumSubscriber виконує другий MySchemaManager насправді не потрібно вчиняти.).
Що нового У цьому випуску :.
- Додана підтримка Python 3.3
- Замінено рекомендується використання zope.interface.implements з еквівалентним zope.interface.implementer декоратора.
- Припинено підтримка Python 2.4 і 2.5.
Що нового у версії 3.7.1:
- Вилучено Споруда частина, яка використовується під час розвитку, але робить НЕ компілюється на Windows.
- Сценарії покоління додати примітку транзакцій.
Вимоги
- Python
Коментар не знайдено