zope.generations gir en måte å oppdatere objektene i databasen når søknad skjema endringer. & Nbsp; En søknad skjema er i hovedsak den strukturen av data, oppbygging av klassene i tilfelle av ZODB eller bord beskrivelser i tilfelle av en relasjonsdatabase.
Detaljert Dokumentasjon
Generasjoner er en måte å oppdatere objektene i databasen når programmet skjemaendringer. En applikasjonsskjema er i det vesentlige strukturen av data, strukturen av klassene i tilfelle av ZODB eller bord beskrivelsene i tilfelle av en relasjonsdatabase.
Når du endrer programmets datastrukturer, for eksempel endre du den semantiske betydningen av et eksisterende felt i en klasse, vil du ha et problem med databaser som ble opprettet før endringen. For en mer grundig diskusjon og mulige løsninger, se http://wiki.zope.org/zope3/DatabaseGenerations
Vi skal bruke komponentarkitektur, og vi trenger en database og en forbindelse:
& Nbsp; >>> import cgi
& Nbsp; >>> fra pprint import pprint
& Nbsp; >>> fra zope.interface import redskaper
& Nbsp; >>> fra ZODB.tests.util import DB
& Nbsp; >>> db = DB ()
& Nbsp; >>> conn = db.open ()
& Nbsp; >>> root = conn.root ()
Tenk deg at vår søknad er et orakel: du kan lære det å reagere på setninger. La oss holde det enkelt og lagre dataene i en dict:
& Nbsp; >>> rot ['svar'] = {'Hello': '? Hi & hvordan gjør du ",
& Nbsp; ... '? Meaning of life': '42',
& Nbsp; ... 'fire ': 'Fire
& Nbsp; >>> transaction.commit ()
Første installasjon
Her er noen generasjoner spesifikk kode. Vi vil opprette og registrere en SchemaManager. SchemaManagers er ansvarlig for selve oppdateringer av databasen. Dette vil være bare en dummy. Poenget her er å gjøre generasjoner modul klar over at vår søknad støtter generasjoner.
Standard gjennomføring av SchemaManager er ikke egnet for denne testen fordi den bruker Python moduler for å styre generasjoner. For nå, vil det være helt fint, siden vi ikke vil at den skal gjøre noe ennå.
& Nbsp; >>> fra zope.generations.interfaces importere ISchemaManager
& Nbsp; >>> fra zope.generations.generations importere SchemaManager
& Nbsp; >>> import zope.component
& Nbsp; >>> dummy_manager = SchemaManager (minimum_generation = 0, generasjon = 0)
& Nbsp; >>> zope.component.provideUtility (
& Nbsp; ... dummy_manager, ISchemaManager, name = 'some.app')
'Some.app' er en unik identifikator. Du bør bruke en URI eller den stiplede navnet på pakken.
Når du starter Zope og databasen er åpnet, er en hendelse IDatabaseOpenedWithRoot sendt. Zope registrerer evolveMinimumSubscriber som standard som et behandlingsprogram for denne hendelsen. La oss simulere dette:
& Nbsp; >>> klasse DatabaseOpenedEventStub (objekt):
& Nbsp; ... def __init __ (selv, database):
& Nbsp; ... self.database = database
& Nbsp; >>> event = DatabaseOpenedEventStub (db)
& Nbsp; >>> fra zope.generations.generations importere evolveMinimumSubscriber
& Nbsp; >>> evolveMinimumSubscriber (hendelse)
Konsekvensen av denne handlingen er at nå databasen inneholder det faktum at vår nåværende skjema nummer er 0. Når vi oppdatere skjemaet, vil Zope3 ha en idé om hva utgangspunktet var. Her ser?
& Nbsp; >>> fra zope.generations.generations importere generations_key
& Nbsp; >>> rot [generations_key] ['some.app']
& Nbsp; 0
I det virkelige liv bør du aldri trenger å bry deg med denne nøkkelen direkte, men du bør være klar over at det eksisterer.
Oppgrader scenario
Tilbake til historien. Noen tiden går og en av våre kunder blir hacket fordi vi glemte å unnslippe HTML spesialtegn! Gru! Vi må løse dette problemet ASAP uten å miste data. Vi velger å bruke generasjoner å imponere våre kolleger.
La oss oppdatere skjemaet manager (droppe den gamle og sette inn en ny tilpasset en):
& Nbsp; >>> fra zope.component import globalregistry
& Nbsp; >>> gsm = globalregistry.getGlobalSiteManager ()
& Nbsp; >>> gsm.unregisterUtility (forut = ISchemaManager, name = 'some.app')
& Nbsp; Sann
& Nbsp; >>> klasse MySchemaManager (objekt):
& Nbsp; ... redskaper (ISchemaManager)
& Nbsp; ...
& Nbsp; ... minimum_generation = 1
& Nbsp; ... generasjon = 2
& Nbsp; ...
& Nbsp; ... def utvikle seg (selv, kontekst, generasjon):
& Nbsp; ... root = context.connection.root ()
& nbsp; ... svar = rot ['svar']
& Nbsp; ... hvis generasjon == 1:
& Nbsp; ... for spørsmålet, svaret i answers.items ():
& Nbsp; ... svar [spørsmålet] = cgi.escape (svar)
& Nbsp; ... elif generasjon == 2:
& Nbsp; ... for spørsmålet, svaret i answers.items ():
& Nbsp; ... del svar [spørsmålet]
& Nbsp; ... svar [cgi.escape (spørsmål)] = svar
& Nbsp; ... annet:
& Nbsp; ... heve ValueError ("Bummer")
& Nbsp; ... rot ['svar'] = svar # ping utholdenhet
& Nbsp; ... transaction.commit ()
& Nbsp; >>> leder = MySchemaManager ()
& Nbsp; >>> zope.component.provideUtility (manager, ISchemaManager, name = 'some.app')
Vi har satt minimum_generation til 1. Det betyr at vår søknad vil nekte å kjøre med en database eldre enn generasjon 1. generasjon attributt er satt til 2, noe som betyr at den siste generasjonen som dette SchemaManager vet om er 2.
utvikle seg () er arbeidshesten her. Sin jobb er å få databasen fra generasjon-en til generasjon. Det blir en kontekst som har attributten "tilkobling", som er en forbindelse til ZODB. Du kan bruke den til å endre objektene som i dette eksemplet.
I denne generasjonen implementering en rømming svarene (si, kritiske, fordi de kan legges inn av hvem som helst!), Generasjon 2 rømming spørsmålene (si, mindre viktige, fordi disse kan legges inn ved autorisert personell only).
Faktisk trenger du egentlig ikke trenger en tilpasset implementering av ISchemaManager. En er tilgjengelig, vi har brukt det for en dummy tidligere. Den bruker Python-moduler for organisering av Evolver funksjoner. Se sin docstring for mer informasjon.
I det virkelige liv, vil du ha mye mer komplekse objektstrukturer enn den her. For å gjøre livet ditt enklere, det er to svært nyttige funksjoner som er tilgjengelige i zope.generations.utility: findObjectsMatching () og findObjectsProviding (). De vil grave gjennom containere rekursivt for å hjelpe deg å oppsøke gamle gjenstander som du ønsker å oppdatere, etter grensesnittet eller ved noen andre kriterier. De er enkle å forstå, sjekke sine docstrings.
Generations i aksjon
Så laster ned vår rasende klient våre nyeste koden og starter Zope. Arrangementet sendes automatisk igjen:
& Nbsp; >>> event = DatabaseOpenedEventStub (db)
& Nbsp; >>> evolveMinimumSubscriber (hendelse)
Shazam! Klienten er glad igjen!
& Nbsp; >>> pprint (root ['svar'])
& Nbsp; {'Hello': 'Hei og hvordan gjør du?',
& Nbsp; 'meningen med livet?': '42',
& Nbsp; 'fire ': 'Fire
& Nbsp; >>> rot [generations_key] ['some.app']
& Nbsp; 1
Vi ser at generasjoner jobber, så vi bestemmer oss for å ta neste steg og utvikle seg til generasjon 2. La oss se hvordan dette kan gjøres manuelt:
& Nbsp; >>> fra zope.generations.generations importere utvikle seg
& Nbsp; >>> utvikle seg (db)
& Nbsp; >>> pprint (root ['svar'])
& Nbsp; {'Hello': 'Hei og hvordan gjør du?',
& Nbsp; 'meningen med livet?': '42',
& Nbsp; 'fire ': 'Fire
& Nbsp; 2
Standard oppførsel av Evolve oppgraderinger til den nyeste generasjonen levert av SchemaManager. Du kan bruke hvordan argument for å utvikle seg () når du bare ønsker å sjekke om du må oppdatere eller hvis du ønsker å være lat som abonnent som vi har kalt tidligere.
Bestilling av skjema ledere
Ofte delsystemer brukes til å komponere et program stole på andre delsystemer til å fungere ordentlig. Hvis begge delsystemer gi skjema ledere, er det ofte nyttig å vite i hvilken rekkefølge de evolvers vil bli påberopt. Dette gjør at et rammeverk og det er kunder å være i stand til å utvikle seg på konsert, og kundene kan vite at rammeverket vil bli utviklet seg før eller etter seg selv.
Dette kan oppnås ved regulering av navnene på de skjema leder verktøy. Skjemaet ledere blir drevet i den rekkefølgen bestemmes ved å sortere navnene deres.
& Nbsp; >>> manager1 = SchemaManager (minimum_generation = 0, generasjon = 0)
& Nbsp; >>> manager2 = SchemaManager (minimum_generation = 0, generasjon = 0)
& Nbsp; >>> zope.component.provideUtility (
& Nbsp; ... manager1, ISchemaManager, name = 'another.app')
& Nbsp; >>> zope.component.provideUtility (
& Nbsp; ... manager2, ISchemaManager, name = '-forlengelse another.app')
Legg merke til hvordan navnet på den første pakken brukes til å opprette et navnerom for avhengige pakker. Dette er ikke et krav i rammeverket, men et passende mønster for denne bruken.
La oss utvikle databasen for å etablere disse generasjoner:
& Nbsp; >>> event = DatabaseOpenedEventStub (db)
& Nbsp; >>> evolveMinimumSubscriber (hendelse)
& Nbsp; >>> rot [generations_key] ['another.app']
& Nbsp; 0
['-Extension another.app'] >>> root [generations_key]; & nbsp
& Nbsp; 0
La oss anta at for noen grunn hver av disse delsystemene må legge til en generasjon, og den generasjonen en av "-extension another.app 'avhenger generasjon en av' another.app '. Vi må gi skjema ledere for hver som registrerer at de har blitt kjørt så vi kan verifisere resultatet:
& Nbsp; >>> gsm.unregisterUtility (forut = ISchemaManager, name = 'another.app')
& Nbsp; Sann
& Nbsp; >>> gsm.unregisterUtility (
& Nbsp; ... forut = ISchemaManager, name = '-forlengelse another.app')
& Nbsp; Sann
& Nbsp; >>> klasse FoundationSchemaManager (objekt):
& Nbsp; ... redskaper (ISchemaManager)
& Nbsp; ...
& Nbsp; ... minimum_generation = 1
& Nbsp; ... generasjon = 1
& Nbsp; ...
& Nbsp; ... def utvikle seg (selv, kontekst, generasjon):
& Nbsp; ... root = context.connection.root ()
& Nbsp; ... bestilling = root.get ('bestilling', [])
& Nbsp; ... hvis generasjon == 1:
& Nbsp; ... ordering.append ('stiftelse 1')
& Nbsp; ... print 'stiftelse generasjon 1'
& Nbsp; ... annet:
& Nbsp; ... heve ValueError ("Bummer")
& Nbsp; ... root ['bestilling'] = bestilling # ping utholdenhet
& Nbsp; ... transaction.commit ()
& Nbsp; >>> klasse DependentSchemaManager (objekt):
& Nbsp; ... redskaper (ISchemaManager)
& Nbsp; ...
& Nbsp; ... minimum_generation = 1
& Nbsp; ... generasjon = 1
& Nbsp; ...
& Nbsp; ... def utvikle seg (selv, kontekst, generasjon):
& Nbsp; ... root = context.connection.root ()
& Nbsp; ... bestilling = root.get ('bestilling', [])
& Nbsp; ... hvis generasjon == 1:
& Nbsp; ... ordering.append ('avhengige 1')
& Nbsp; ... print 'avhengige generasjon 1'
& Nbsp; ... annet:
& Nbsp; ... heve ValueError ("Bummer")
& Nbsp; ... root ['bestilling'] = bestilling # ping utholdenhet
& 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 = '-forlengelse another.app')
Evolving databasen nå vil alltid kjøre 'another.app' Evolver før "-extension another.app 'den Evolver:
& Nbsp; >>> event = DatabaseOpenedEventStub (db)
& Nbsp; >>> evolveMinimumSubscriber (hendelse)
& Nbsp; fundament generasjon 1
& Nbsp; avhengige generasjon 1
& Nbsp; >>> root ['bestilling']
& Nbsp; ['stiftelse 1', 'avhengige 1']
Installasjon
I eksempelet ovenfor, vi initialisert svarene manuelt. Vi bør ikke ha for å gjøre det manuelt. Søknaden bør være i stand til å gjøre det automatisk.
IInstallableSchemaManager strekker ISchemaManager, som gir en installeringsmetode for å utføre en førstegangs installasjon av et program. Dette er et bedre alternativ enn å registrere databaseåpnet abonnenter.
La oss definere en ny skjema manager som inkluderer installasjon:
& Nbsp; >>> gsm.unregisterUtility (forut = ISchemaManager, name = 'some.app')
& Nbsp; Sann
& Nbsp; >>> fra zope.generations.interfaces importere IInstallableSchemaManager
& Nbsp; >>> klasse MySchemaManager (objekt):
& Nbsp; ... redskaper (IInstallableSchemaManager)
& Nbsp; ...
& Nbsp; ... minimum_generation = 1
& Nbsp; ... generasjon = 2
& Nbsp; ...
& Nbsp; ... def installere (selv, kontekst):
& Nbsp; ... root = context.connection.root ()
& Nbsp; ... root ['svar'] = {'Hello': '? Hi & hvordan gjør du ",
& Nbsp; ... '? Meaning of life': '42',
& Nbsp; ... 'fire ': 'Fire
& Nbsp; ...
& Nbsp; ... def utvikle seg (selv, kontekst, generasjon):
& Nbsp; ... root = context.connection.root ()
& nbsp; ... svar = rot ['svar']
& Nbsp; ... hvis generasjon == 1:
& Nbsp; ... for spørsmålet, svaret i answers.items ():
& Nbsp; ... svar [spørsmålet] = cgi.escape (svar)
& Nbsp; ... elif generasjon == 2:
& Nbsp; ... for spørsmålet, svaret i answers.items ():
& Nbsp; ... del svar [spørsmålet]
& Nbsp; ... svar [cgi.escape (spørsmål)] = svar
& Nbsp; ... annet:
& Nbsp; ... heve ValueError ("Bummer")
& Nbsp; ... rot ['svar'] = svar # ping utholdenhet
& Nbsp; ... transaction.commit ()
& Nbsp; >>> leder = MySchemaManager ()
& Nbsp; >>> zope.component.provideUtility (manager, ISchemaManager, name = 'some.app')
Nå kan åpne en ny database:
& Nbsp; >>> db.close ()
& Nbsp; >>> db = DB ()
& Nbsp; >>> conn = db.open ()
& Nbsp; >>> 'svar' i conn.root ()
& Nbsp; False
& Nbsp; >>> event = DatabaseOpenedEventStub (db)
& Nbsp; >>> evolveMinimumSubscriber (hendelse)
& Nbsp; >>> conn.sync ()
& Nbsp; >>> root = conn.root ()
& Nbsp; >>> pprint (root ['svar'])
& Nbsp; {'Hello': 'Hei og hvordan gjør du?',
& Nbsp; 'meningen med livet?': '42',
& Nbsp; 'fire ': 'Fire
& Nbsp; 2
Den ZODB transaksjonslogg bemerker at vår installere scriptet ble henrettet
& Nbsp; >>> [. It.description for det i conn.db () storage.iterator ()] [- 2]
& Nbsp; u'some.app: kjører installere generasjon '
(Minor note: det er ikke den siste posten fordi det er to innlegg: MySchemaManager utfører en, og evolveMinimumSubscriber utfører den andre MySchemaManager ikke virkelig trenger å forplikte seg..)
Hva er nytt i denne utgaven:.
- Lagt til støtte for Python 3.3
- Erstattet foreldet zope.interface.implements bruk med tilsvarende zope.interface.implementer dekoratør.
- Droppet støtte for Python 2.4 og 2.5.
Hva er nytt i versjon 3.7.1:
- Fjernet buildout del som ble brukt under utvikling, men gjør ikke kompilere på Windows.
- Generation skript legge en transaksjon notat.
Krav :
- Python
Kommentarer ikke funnet