zope.generations oferă o modalitate de actualizare obiecte în baza de date atunci când se schimbă schemei aplicare. & Nbsp; O schemă cerere este, în esență structura de date, structura de clase, în cazul ZODB sau descrierile de masă, în cazul o bază de date relațională.
documentație detaliată
Generații sunt o modalitate de actualizare obiecte în baza de date atunci când se schimbă schema de aplicare. O schemă de aplicare este esențial structura de date, structura de clase, în cazul ZODB sau descrierile de masă în cazul unei baze de date relațională.
Când modificați structuri de date de aplicare dumneavoastră, de exemplu, schimbați sensul semantic al unui câmp existent într-o clasă, veți avea o problemă cu baze de date care au fost create înainte de a schimba ta. Pentru o discuție mai aprofundată și soluții posibile, a se vedea http://wiki.zope.org/zope3/DatabaseGenerations
Vom folosi arhitectura componente, și vom avea nevoie de o bază de date și o conexiune:
& Nbsp; >>> CGI import
& Nbsp; >>> din pprint pprint import
& Nbsp; >>> de instrumente de import zope.interface
& Nbsp; >>> din ZODB.tests.util import DB
& Nbsp; >>> db = DB ()
& Nbsp; >>> Conn = db.open ()
& Nbsp; >>> root = conn.root ()
Imaginați-vă că aplicația noastră este un oracol: puteti invata sa reactioneze la fraze. Să-l păstrați simplu și stocheze datele într-un dict:
& Nbsp; >>> profunde ['răspunsurile'] = {'Hello': '? Bună și cum faci ",
& Nbsp; ... "? Noțiunea de viață": "42",
& Nbsp; ... "patru ':' Patru
& Nbsp; >>> transaction.commit ()
Configurarea inițială
Iată câteva cod generații-specifice. Vom crea și înregistra un SchemaManager. SchemaManagers sunt responsabile pentru actualizările reale ale bazei de date. Acesta va fi doar un fals. Punctul de aici este de a face generațiile modulul conștienți de faptul că cererea noastră acceptă generații.
Punerea în aplicare a SchemaManager implicit nu este potrivit pentru acest test, deoarece foloseste module Python pentru a gestiona generații. Pentru moment, aceasta va fi bine, deoarece nu vrem să facem ceva doar încă.
& Nbsp; >>> din zope.generations.interfaces import ISchemaManager
& Nbsp; >>> din zope.generations.generations import SchemaManager
& Nbsp; >>> zope.component import
& Nbsp; >>> dummy_manager = SchemaManager (minimum_generation = 0, generație = 0)
& Nbsp; >>> zope.component.provideUtility (
& Nbsp; ... dummy_manager, ISchemaManager, name = 'some.app')
"Some.app" este un identificator unic. Ar trebui să utilizați un URI sau numele punctată de pachet.
Când porniți Zope și o bază de date este deschis, un IDatabaseOpenedWithRoot eveniment este trimis. Zope înregistrează evolveMinimumSubscriber implicit ca un handler pentru acest eveniment. Să simula acest lucru:
& Nbsp; >>> DatabaseOpenedEventStub clasă (obiect):
& Nbsp; ... def automat metodei __init __ (self, baze de date):
& Nbsp; ... self.database = bază de date
& Nbsp; >>> eveniment = DatabaseOpenedEventStub (db)
& Nbsp; >>> din zope.generations.generations import evolveMinimumSubscriber
& Nbsp; >>> evolveMinimumSubscriber (eveniment)
Consecința acestei acțiuni este că acum baza de date conține că numărul nostru actual schemă este 0. Când am actualiza schema, Zope3 va avea o idee despre ceea ce a fost punctul de plecare. Aici, vezi?
& Nbsp; >>> din zope.generations.generations import generations_key
& Nbsp; >>> rădăcină [generations_key] ['some.app']
& Nbsp; 0
În viața reală nu trebuie să vă trebuie să se ocupe cu această cheie direct, dar ar trebui să fie conștienți de faptul că ea există.
Upgrade scenariu
Înapoi la povestea. Unii timpul trece și unul dintre clientii nostri se tocat că am uitat să scape de caractere speciale HTML! Oroare! Trebuie să rezolva această problemă urgent, fără a pierde date. Noi decide să utilizeze generații pentru a impresiona colegii noștri.
Să actualizeze managerul schemă (picătură pe cel vechi și instalați un nou personalizat una):
& Nbsp; >>> din globalregistry import zope.component
& Nbsp; >>> gsm = globalregistry.getGlobalSiteManager ()
& Nbsp; >>> gsm.unregisterUtility (cu condiția = ISchemaManager, name = 'some.app')
& Nbsp; Adevărat
& Nbsp; >>> MySchemaManager clasă (obiect):
& Nbsp; ... unelte (ISchemaManager)
& Nbsp; ...
& Nbsp; ... minimum_generation = 1
& Nbsp; ... generație = 2
& Nbsp; ...
& Nbsp; ... def evolua (auto, context, generație):
& Nbsp; ... root = context.connection.root ()
& nbsp; ... răspunsuri = radacina ['răspunsuri "]
& Nbsp; ... dacă generație == 1:
& Nbsp; ... pentru întrebare, răspunsul la answers.items ():
& Nbsp; ... raspunsuri [cauză] = cgi.escape (răspuns)
& Nbsp; ... generație Elif == 2:
& Nbsp; ... pentru întrebare, răspunsul la answers.items ():
& Nbsp; ... del raspunsuri [cauză]
& Nbsp; ... raspunsuri [cgi.escape (întrebare)] = răspuns
& Nbsp; ... altfel:
& Nbsp; ... ridica ValueError ("Bummer")
& Nbsp; ... profunde ['răspunsurile'] = răspunsuri # ping persistență
& Nbsp; ... transaction.commit ()
& Nbsp; >>> administrator = MySchemaManager ()
& Nbsp; >>> zope.component.provideUtility (director, ISchemaManager, name = 'some.app')
Ne-am propus minimum_generation la 1. Aceasta înseamnă că cererea noastră va refuza să ruleze cu o bază de date mai mare de generare 1. Atributul de generare este setat la 2, ceea ce înseamnă că ultima generatie care aceasta SchemaManager știe despre este 2.
evolua () este calul de bătaie aici. Rolul său este de a obtine date de la generație la generație 1. Ea devine un context care are "conexiunea" atribut, care este o conexiune la ZODB. Puteți folosi pentru a schimba obiecte ca în acest exemplu.
În această generație special, punerea în aplicare o scapă răspunsuri (să zicem, critice, deoarece ele pot fi introduse de către oricine!), Generare 2 scapă întrebările (să zicem, mai puțin importante, deoarece acestea pot fi introduse prin partea personalului autorizat numai).
De fapt, tu nu prea au nevoie de o punere în aplicare personalizat de ISchemaManager. Una dintre ele este disponibil, l-am folosit pentru un manechin anterior. Acesta utilizează module Python pentru organizare de funcții evolver. Vezi docstring sa de mai multe informații.
În viața reală, veți avea mult mai mult structuri obiect complex decât cel de aici. Pentru a face viața mai ușoară, există două funcții foarte utile disponibile în zope.generations.utility: findObjectsMatching () și findObjectsProviding (). Ei vor sapa prin containere recursiv pentru a vă ajuta să caute obiecte vechi pe care doriți să actualizați, de interfață sau prin alte criterii. Ele sunt ușor de înțeles, verifica docstrings lor.
Generații în acțiune
Deci, clientul nostru furios descarcă cele mai recente codul nostru și repornește Zope. Evenimentul este trimis automat din nou:
& Nbsp; >>> eveniment = DatabaseOpenedEventStub (db)
& Nbsp; >>> evolveMinimumSubscriber (eveniment)
Shazam! Clientul este fericit din nou!
& Nbsp; >>> pprint (rădăcină ['răspunsuri'])
& Nbsp; {'Hello': 'Bună și cum faci? ",
& Nbsp; "Noțiunea de viață?": "42",
& Nbsp; "patru ':' Patru
& Nbsp; >>> rădăcină [generations_key] ['some.app']
& Nbsp; 1
Vedem că generațiile sunt de lucru, așa că decide să facă pasul următor și să evolueze în generație 2. Să vedem cum acest lucru se poate face manual:
& Nbsp; >>> din zope.generations.generations import evolua
& Nbsp; >>> evolua (db)
& Nbsp; >>> pprint (rădăcină ['răspunsuri'])
& Nbsp; {'Hello': 'Bună și cum faci? ",
& Nbsp; "Noțiunea de viață?": "42",
& Nbsp; "patru ':' Patru
& Nbsp; 2
Comportamentul implicit de upgrade-uri evolueze pentru ultima generatie oferite de SchemaManager. Puteți utiliza argumentul cum de a evolua (), atunci când vrei doar să verificați dacă aveți nevoie pentru a actualiza sau dacă vrei să fii leneș ca abonatul pe care l-am numit anterior.
Ordonarea managerilor de schemă
Frecvent subsisteme folosit pentru a compune o aplicație se bazează pe alte subsisteme să funcționeze corespunzător. Dacă ambele subsisteme oferi managerilor de scheme, este adesea util să cunoască ordinea în care evolvers vor fi invocate. Acest lucru permite un cadru și este clienții să poată evolua în concert, iar clienții pot ști că acest cadru va fi evoluat înainte sau după sine.
Acest lucru poate fi realizat prin controlul numele de utilități managerului schemă. Managerii schemei sunt rulate în ordinea stabilită de sortare numele lor.
& Nbsp; >>> manager1 = SchemaManager (minimum_generation = 0, generație = 0)
& Nbsp; >>> manager2 = SchemaManager (minimum_generation = 0, generație = 0)
& Nbsp; >>> zope.component.provideUtility (
& Nbsp; ... manager1, ISchemaManager, name = 'another.app')
& Nbsp; >>> zope.component.provideUtility (
& Nbsp; ... manager2, ISchemaManager, name = 'another.app-extindere ")
Observați cum numele primului pachet este folosit pentru a crea un spațiu de nume pentru pachete dependente. Aceasta nu este o cerință a cadrului, ci un model convenabil pentru această utilizare.
Să evolua în baza de date pentru a stabili aceste generații:
& Nbsp; >>> eveniment = DatabaseOpenedEventStub (db)
& Nbsp; >>> evolveMinimumSubscriber (eveniment)
& Nbsp; >>> rădăcină [generations_key] ['another.app']
& Nbsp; 0
& Nbsp; >>> rădăcină [generations_key] ['another.app-extindere "]
& Nbsp; 0
Să presupunem că pentru un motiv oarecare fiecare dintre aceste subsisteme trebuie să adauge o generație, și că generația 1 din "another.app-extindere" depinde de generație 1 a "another.app". Avem nevoie de a oferi managerilor schemei pentru fiecare înregistrare pe care pe care le-am rula astfel încât să putem verifica rezultatul:
& Nbsp; >>> gsm.unregisterUtility (cu condiția = ISchemaManager, name = 'another.app')
& Nbsp; Adevărat
& Nbsp; >>> gsm.unregisterUtility (
& Nbsp; ... cu condiția = ISchemaManager, name = 'another.app-extindere ")
& Nbsp; Adevărat
& Nbsp; >>> clasă FoundationSchemaManager (obiect):
& Nbsp; ... unelte (ISchemaManager)
& Nbsp; ...
& Nbsp; ... minimum_generation = 1
& Nbsp; ... generație = 1
& Nbsp; ...
& Nbsp; ... def evolua (auto, context, generație):
& Nbsp; ... root = context.connection.root ()
& Nbsp; ... ordonare = root.get ('comanda', [])
& Nbsp; ... dacă generație == 1:
& Nbsp; ... ordering.append ("fundație 1 ')
& Nbsp; ... "generație fundație 1 'print
& Nbsp; ... altfel:
& Nbsp; ... ridica ValueError ("Bummer")
& Nbsp; ... rădăcină ['comanda'] = comanda # ping persistență
& Nbsp; ... transaction.commit ()
& Nbsp; >>> DependentSchemaManager clasă (obiect):
& Nbsp; ... unelte (ISchemaManager)
& Nbsp; ...
& Nbsp; ... minimum_generation = 1
& Nbsp; ... generație = 1
& Nbsp; ...
& Nbsp; ... def evolua (auto, context, generație):
& Nbsp; ... root = context.connection.root ()
& Nbsp; ... ordonare = root.get ('comanda', [])
& Nbsp; ... dacă generație == 1:
& Nbsp; ... ordering.append ("dependent 1 ')
& Nbsp; ... print "generație dependentă 1 '
& Nbsp; ... altfel:
& Nbsp; ... ridica ValueError ("Bummer")
& Nbsp; ... rădăcină ['comanda'] = comanda # ping persistență
& 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-extindere ")
Evoluând în baza de date acum va rula întotdeauna "another.app" evolver înainte "another.app-extensie" evolver:
& Nbsp; >>> eveniment = DatabaseOpenedEventStub (db)
& Nbsp; >>> evolveMinimumSubscriber (eveniment)
& Nbsp; generație fundație 1
& Nbsp; generație dependentă 1
& Nbsp; >>> rădăcină ['comanda']
& Nbsp; ['fundație 1 "," dependent 1']
Instalare
În exemplul de mai sus, am initializat manual răspunsurile. Noi nu ar trebui să faci asta manual. Cererea trebuie să fie capabil să facă asta în mod automat.
IInstallableSchemaManager extinde ISchemaManager, oferind o metodă de instalare pentru efectuarea unei instalări intial a unei cereri. Aceasta este o alternativă mai bună decât înregistrarea abonați-baze de date deschise.
Să definim un nou manager schemă care include instalare:
& Nbsp; >>> gsm.unregisterUtility (cu condiția = ISchemaManager, name = 'some.app')
& Nbsp; Adevărat
& Nbsp; >>> din zope.generations.interfaces import IInstallableSchemaManager
& Nbsp; >>> MySchemaManager clasă (obiect):
& Nbsp; ... unelte (IInstallableSchemaManager)
& Nbsp; ...
& Nbsp; ... minimum_generation = 1
& Nbsp; ... generație = 2
& Nbsp; ...
& Nbsp; ... instala def (auto, context):
& Nbsp; ... root = context.connection.root ()
& Nbsp; ... rădăcină ['răspunsurile'] = {'Hello': '? Bună și cum faci ",
& Nbsp; ... "? Noțiunea de viață": "42",
& Nbsp; ... "patru ':' Patru
& Nbsp; ...
& Nbsp; ... def evolua (auto, context, generație):
& Nbsp; ... root = context.connection.root ()
& nbsp; ... răspunsuri = radacina ['răspunsuri "]
& Nbsp; ... dacă generație == 1:
& Nbsp; ... pentru întrebare, răspunsul la answers.items ():
& Nbsp; ... raspunsuri [cauză] = cgi.escape (răspuns)
& Nbsp; ... generație Elif == 2:
& Nbsp; ... pentru întrebare, răspunsul la answers.items ():
& Nbsp; ... del raspunsuri [cauză]
& Nbsp; ... raspunsuri [cgi.escape (întrebare)] = răspuns
& Nbsp; ... altfel:
& Nbsp; ... ridica ValueError ("Bummer")
& Nbsp; ... profunde ['răspunsurile'] = răspunsuri # ping persistență
& Nbsp; ... transaction.commit ()
& Nbsp; >>> administrator = MySchemaManager ()
& Nbsp; >>> zope.component.provideUtility (director, ISchemaManager, name = 'some.app')
Acum, vă permite să deschideți un nou bază de date:
& Nbsp; >>> db.close ()
& Nbsp; >>> db = DB ()
& Nbsp; >>> Conn = db.open ()
& Nbsp; "răspunsuri" >>> din conn.root ()
& Nbsp; Fals
& Nbsp; >>> eveniment = DatabaseOpenedEventStub (db)
& Nbsp; >>> evolveMinimumSubscriber (eveniment)
& Nbsp; >>> conn.sync ()
& Nbsp; >>> root = conn.root ()
& Nbsp; >>> pprint (rădăcină ['răspunsuri'])
& Nbsp; {'Hello': 'Bună și cum faci? ",
& Nbsp; "Noțiunea de viață?": "42",
& Nbsp; "patru ':' Patru
& Nbsp; 2
Registrul de tranzacții ZODB constată că script-ul nostru instala fost executat
& Nbsp; >>> [. It.description pentru el în conn.db () storage.iterator ()] [- 2]
& Nbsp; u'some.app: funcționare instala generație "
(Nota Minor: nu e ultima înregistrare, deoarece există două comite: MySchemaManager efectuează o, iar evolveMinimumSubscriber efectuează al doilea MySchemaManager nu reprezintă cu adevărat nevoie să se angajeze..)
Ce este nou în această versiune:.
- Adăugat suport pentru Python 3.3
- Înlocuit utilizare zope.interface.implements depreciat cu decorator zope.interface.implementer echivalent.
- renunțat la suportul pentru Python 2.4 și 2.5.
Ce este nou în versiunea 3.7.1:
- parte buildout îndepărtat care a fost folosit in timpul dezvoltarii, dar nu nu compila pe Windows.
- script-uri de generație a adăuga o notă tranzacție.
Cerințe :
- Python
Comentariile nu a fost găsit