zope.generations biedt een manier van updaten van objecten in de database wanneer de applicatie schema veranderingen & nbsp;. Een aanvraag schema is in wezen de structuur van de gegevens, de structuur van de lessen in het geval van ZODB of de tafel omschrijvingen in het geval van een relationele database.
Gedetailleerde Documentatie
Generaties zijn een manier van bijwerken objecten in de database wanneer de applicatie schema veranderingen. Een toepassingsschema hoofdzaak de structuur van gegevens, de structuur van klassen bij ZODB of de tabel beschrijvingen bij een relationele database.
Wanneer u gegevens structuren van uw toepassing te wijzigen, bijvoorbeeld de semantische betekenis van een bestaand veld in een klasse veranderen, zal u een probleem met databases die zijn gemaakt voordat de wijziging te hebben. Voor een meer diepgaande discussie en mogelijke oplossingen, zie http://wiki.zope.org/zope3/DatabaseGenerations
We zullen met behulp van de component architectuur, en wij zullen een database en een verbinding nodig:
& Nbsp; >>> import cgi
& Nbsp; >>> van pprint import pprint
& Nbsp; >>> van zope.interface import werktuigen
& Nbsp; >>> van ZODB.tests.util import DB
& Nbsp; >>> db = DB ()
& Nbsp; >>> conn = db.open ()
& Nbsp; >>> root = conn.root ()
Stel je voor dat onze applicatie is een orakel: je kunt het leren om zinnen te reageren. Laten we het simpel houden en de gegevens in een dict te slaan:
& Nbsp; >>> wortel ['antwoorden'] = {'Hallo': 'Hi & hoe doe je',
& Nbsp; ... '? Zin van het leven': '42',
& Nbsp; ... 'vier ': 'Vier
& Nbsp; >>> transaction.commit ()
Initiële setup
Hier zijn een paar generaties-specifieke code. We zullen maken en registreer een SchemaManager. SchemaManagers zijn verantwoordelijk voor de daadwerkelijke update van de database. Deze zal slechts een dummy zijn. Het punt hier is om de generaties module bewust dat onze applicatie ondersteunt generaties.
De standaard uitvoering van SchemaManager is niet geschikt voor deze test omdat het gebruik maakt Python modules generaties beheren. Voor nu, zal het prima, omdat we niet willen dat het gewoon nog iets te doen.
& Nbsp; >>> van zope.generations.interfaces importeren ISchemaManager
& Nbsp; >>> van zope.generations.generations importeren SchemaManager
& Nbsp; >>> import zope.component
& Nbsp; >>> dummy_manager = SchemaManager (minimum_generation = 0, productie = 0)
& Nbsp; >>> zope.component.provideUtility (
& Nbsp; ... dummy_manager, ISchemaManager, naam = 'some.app')
'Some.app' is een unieke identificatie. Je moet over een URI of de gestippelde naam van uw pakket te gebruiken.
Wanneer u Zope starten en een database wordt geopend, wordt een gebeurtenis IDatabaseOpenedWithRoot gestuurd. Zope registreert evolveMinimumSubscriber standaard als een handler voor dit evenement. Laten we simuleren deze:
& Nbsp; >>> klasse DatabaseOpenedEventStub (object):
& Nbsp; ... def __init __ (zelf, database):
& Nbsp; ... self.database = databank
& Nbsp; >>> event = DatabaseOpenedEventStub (db)
& Nbsp; >>> van zope.generations.generations importeren evolveMinimumSubscriber
& Nbsp; >>> evolveMinimumSubscriber (event)
Het gevolg van deze actie is dat nu de database bevat het feit dat onze huidige schema nummer is 0. Als we het schema bij te werken, zal Zope3 een idee van wat het uitgangspunt was te hebben. Hier zien?
& Nbsp; >>> van zope.generations.generations importeren generations_key
& Nbsp; >>> wortel [generations_key] ['some.app']
& Nbsp; 0
In het echte leven moet je nooit moeten direct moeite met deze sleutel, maar moet u zich ervan bewust dat het bestaat.
Upgrade scenario
Terug naar het verhaal. Enige tijd verstrijkt en een van onze klanten wordt gehackt omdat we vergaten om HTML speciale tekens te ontsnappen! De horror! We moeten dit probleem zo snel mogelijk op te lossen zonder gegevens te verliezen. We besluiten om generaties te gebruiken om indruk te maken van onze collega's.
Laten we het actualiseren van de schema manager (drop de oude en installeer een nieuwe aangepaste één):
& Nbsp; >>> van zope.component import globalregistry
& Nbsp; >>> gsm = globalregistry.getGlobalSiteManager ()
& Nbsp; >>> gsm.unregisterUtility (mits = ISchemaManager, naam = 'some.app')
& Nbsp; True
& Nbsp; >>> klasse MySchemaManager (object):
& Nbsp; ... werktuigen (ISchemaManager)
& Nbsp; ...
& Nbsp; ... minimum_generation = 1
& Nbsp; ... generatie = 2
& Nbsp; ...
& Nbsp; ... def evolueren (zelf, context, generatie):
& Nbsp; ... root = context.connection.root ()
& Nbsp; ... antwoorden = root ['antwoorden']
& Nbsp; ... als generatie == 1:
& Nbsp; ... voor de vraag, antwoord in answers.items ():
& Nbsp; ... antwoorden [vraag] = cgi.escape (antwoord)
& Nbsp; ... Elif generatie == 2:
& Nbsp; ... voor de vraag, antwoord in answers.items ():
& Nbsp; ... del antwoorden [vraag]
& Nbsp; ... antwoorden [cgi.escape (vraag)] = antwoord
& Nbsp; ... anders:
& Nbsp; ... verhogen ValueError ("Bummer")
& Nbsp; ... wortel ['antwoorden'] = antwoorden # ping persistentie
& Nbsp; ... transaction.commit ()
& Nbsp; >>> manager = MySchemaManager ()
& Nbsp; >>> zope.component.provideUtility (manager, ISchemaManager, naam = 'some.app')
We hebben ingesteld minimum_generation op 1. Dat betekent dat onze applicatie weigert te draaien met een database ouder dan generatie 1. Het attribuut generatie is ingesteld op 2, wat betekent dat de nieuwste generatie die dit SchemaManager weet over is 2.
evolueren () is het werkpaard hier. Zijn taak is om de database te krijgen van generatie-1 op generatie. Het krijgt een context waarin het attribuut 'verbinding', dat is een verbinding met de ZODB heeft. U kunt die gebruiken om objecten te wijzigen zoals in dit voorbeeld.
In dit specifieke generatie implementatie 1 ontsnapt aan de antwoorden (zeg, kritisch, omdat ze kunnen worden ingevoerd door iedereen!), Generatie 2 ontsnapt aan de vragen (laten we zeggen, minder belangrijk, omdat deze kan worden ingevoerd door geautoriseerde personell alleen).
In feite, je niet echt een aangepaste uitvoering van ISchemaManager nodig. Een beschikbaar is, we hebben het gebruikt voor een dummy eerder. Het maakt gebruik van Python modules voor de organisatie van Evolver functies. Zie haar docstring voor meer informatie.
In het echte leven heb je veel complexer object structuren dan de hier. Om uw leven gemakkelijker te maken, zijn er twee zeer nuttige functies beschikbaar in zope.generations.utility: findObjectsMatching () en findObjectsProviding (). Ze zullen graven door containers recursief om u te helpen zoeken naar oude objecten die u wilt bijwerken, door de interface of door een andere criteria. Ze zijn gemakkelijk te begrijpen, controleren hun docstrings.
Generaties in actie
Dus, onze woedende klant downloadt onze laatste code en start Zope. Het evenement wordt automatisch opnieuw verzonden:
& Nbsp; >>> event = DatabaseOpenedEventStub (db)
& Nbsp; >>> evolveMinimumSubscriber (event)
Shazam! De klant is weer gelukkig!
& Nbsp; >>> pprint (root ['antwoorden'])
& Nbsp; {'Hallo': 'Hi & hoe doe je?',
& Nbsp; 'zin van het leven?': '42',
& Nbsp; 'vier ': 'Vier
& Nbsp; >>> wortel [generations_key] ['some.app']
& Nbsp; 1
We zien dat generaties werken, dus we besluiten om de volgende stap te zetten en te evolueren op generatie 2. Laten we eens kijken hoe dit handmatig kan worden gedaan:
& Nbsp; >>> van zope.generations.generations importeren evolueren
& Nbsp; >>> evolueren (db)
& Nbsp; >>> pprint (root ['antwoorden'])
& Nbsp; {'Hallo': 'Hi & hoe doe je?',
& Nbsp; 'zin van het leven?': '42',
& Nbsp; 'vier ': 'Vier
& Nbsp; 2
Standaard gedrag van Evolve upgrades naar de nieuwste generatie die door de SchemaManager. U kunt de manier waarop argument gebruiken om te evolueren () als je wil gewoon om te controleren of je nodig hebt om bij te werken of als u lui zoals de abonnee die we eerder hebben geroepen te zijn.
Volgorde van schema managers
Vaak subsystemen gebruikt om een applicatie te componeren vertrouwen op andere subsystemen om goed te functioneren. Als beide subsystemen verschaffen schema managers, is het vaak nuttig om de volgorde waarin de evolvers wordt ingezet kennen. Dit maakt een raamwerk en zijn klanten kunnen evolueren concert, en de klanten kunnen weten dat het kader worden ontwikkeld voor of na zelf.
Dit kan worden bewerkstelligd door regeling van de namen van de schema manager nutsbedrijven. Het schema managers worden uitgevoerd in de volgorde bepaald door het sorteren van hun namen.
& Nbsp; >>> manager1 = SchemaManager (minimum_generation = 0, productie = 0)
& Nbsp; >>> Manager2 = SchemaManager (minimum_generation = 0, productie = 0)
& Nbsp; >>> zope.component.provideUtility (
& Nbsp; ... manager1, ISchemaManager, naam = 'another.app')
& Nbsp; >>> zope.component.provideUtility (
& Nbsp; ... Manager2, ISchemaManager, naam = 'another.app-extensie')
Merk op hoe de naam van het eerste pakket wordt gebruikt om een namespace voor afhankelijke pakketten te maken. Dit is niet een vereiste van het kader, maar een handig patroon voor dit gebruik.
Laten we evolueren de database om deze generaties vast te stellen:
& Nbsp; >>> event = DatabaseOpenedEventStub (db)
& Nbsp; >>> evolveMinimumSubscriber (event)
& Nbsp; >>> wortel [generations_key] ['another.app']
& Nbsp; 0
& Nbsp; >>> wortel [generations_key] ['another.app-extensie']
& Nbsp; 0
Laten we aannemen dat om wat voor reden elk van deze subsystemen moet een generatie toe te voegen, en die generatie 1 van 'another.app-extensie' is afhankelijk van generatie 1 van 'another.app'. We zullen moeten schema managers voor ieder die record dat ze al lopen, zodat we het resultaat kan controleren bieden:
& Nbsp; >>> gsm.unregisterUtility (mits = ISchemaManager, naam = 'another.app')
& Nbsp; True
& Nbsp; >>> gsm.unregisterUtility (
& Nbsp; ... op voorwaarde = ISchemaManager, naam = 'another.app-extensie')
& Nbsp; True
& Nbsp; >>> klasse FoundationSchemaManager (object):
& Nbsp; ... werktuigen (ISchemaManager)
& Nbsp; ...
& Nbsp; ... minimum_generation = 1
& Nbsp; ... generatie = 1
& Nbsp; ...
& Nbsp; ... def evolueren (zelf, context, generatie):
& Nbsp; ... root = context.connection.root ()
& Nbsp; ... bestellen = root.get ('bestellen', [])
& Nbsp; ... als generatie == 1:
& Nbsp; ... ordering.append ('Stichting 1')
& Nbsp; ... print 'foundation generatie 1'
& Nbsp; ... anders:
& Nbsp; ... verhogen ValueError ("Bummer")
& Nbsp; ... wortel ['bestellen'] = bestellen # ping persistentie
& Nbsp; ... transaction.commit ()
& Nbsp; >>> klasse DependentSchemaManager (object):
& Nbsp; ... werktuigen (ISchemaManager)
& Nbsp; ...
& Nbsp; ... minimum_generation = 1
& Nbsp; ... generatie = 1
& Nbsp; ...
& Nbsp; ... def evolueren (zelf, context, generatie):
& Nbsp; ... root = context.connection.root ()
& Nbsp; ... bestellen = root.get ('bestellen', [])
& Nbsp; ... als generatie == 1:
& Nbsp; ... ordering.append ('afhankelijke 1')
& Nbsp; ... print 'afhankelijk generatie 1'
& Nbsp; ... anders:
& Nbsp; ... verhogen ValueError ("Bummer")
& Nbsp; ... wortel ['bestellen'] = bestellen # ping persistentie
& Nbsp; ... transaction.commit ()
& Nbsp; >>> manager1 = FoundationSchemaManager ()
& Nbsp; >>> Manager2 = DependentSchemaManager ()
& Nbsp; >>> zope.component.provideUtility (
& Nbsp; ... manager1, ISchemaManager, naam = 'another.app')
& Nbsp; >>> zope.component.provideUtility (
& Nbsp; ... Manager2, ISchemaManager, naam = 'another.app-extensie')
Evolueert de database nu zal altijd de 'another.app' run evolver voordat de 'another.app-extensie' Evolver:
& Nbsp; >>> event = DatabaseOpenedEventStub (db)
& Nbsp; >>> evolveMinimumSubscriber (event)
& Nbsp; foundation generatie 1
& Nbsp; afhankelijk generatie 1
& Nbsp; >>> wortel ['bestellen']
& Nbsp; ['foundation 1', 'afhankelijke 1']
installatie
In het bovenstaande voorbeeld, we handmatig geïnitialiseerd antwoorden. We zouden niet moeten handmatig doen. De aanvraag moet in staat zijn om dat automatisch te doen.
IInstallableSchemaManager breidt ISchemaManager, het verstrekken van een installatie methode voor het uitvoeren van een aanvankelijke installatie van een applicatie. Dit is een beter alternatief dan de registratie-databank geopend abonnees.
Laten we definiëren een nieuw schema manager die installatie omvat:
& Nbsp; >>> gsm.unregisterUtility (mits = ISchemaManager, naam = 'some.app')
& Nbsp; True
& Nbsp; >>> van zope.generations.interfaces importeren IInstallableSchemaManager
& Nbsp; >>> klasse MySchemaManager (object):
& Nbsp; ... werktuigen (IInstallableSchemaManager)
& Nbsp; ...
& Nbsp; ... minimum_generation = 1
& Nbsp; ... generatie = 2
& Nbsp; ...
& Nbsp; ... def installeren (zelf, context):
& Nbsp; ... root = context.connection.root ()
& Nbsp; ... wortel ['antwoorden'] = {'Hallo': 'Hi & hoe doe je',
& Nbsp; ... '? Zin van het leven': '42',
& Nbsp; ... 'vier ': 'Vier
& Nbsp; ...
& Nbsp; ... def evolueren (zelf, context, generatie):
& Nbsp; ... root = context.connection.root ()
& Nbsp; ... antwoorden = root ['antwoorden']
& Nbsp; ... als generatie == 1:
& Nbsp; ... voor de vraag, antwoord in answers.items ():
& Nbsp; ... antwoorden [vraag] = cgi.escape (antwoord)
& Nbsp; ... Elif generatie == 2:
& Nbsp; ... voor de vraag, antwoord in answers.items ():
& Nbsp; ... del antwoorden [vraag]
& Nbsp; ... antwoorden [cgi.escape (vraag)] = antwoord
& Nbsp; ... anders:
& Nbsp; ... verhogen ValueError ("Bummer")
& Nbsp; ... wortel ['antwoorden'] = antwoorden # ping persistentie
& Nbsp; ... transaction.commit ()
& Nbsp; >>> manager = MySchemaManager ()
& Nbsp; >>> zope.component.provideUtility (manager, ISchemaManager, naam = 'some.app')
Nu, laat het openen van een nieuwe database:
& Nbsp; >>> db.close ()
& Nbsp; >>> db = DB ()
& Nbsp; >>> conn = db.open ()
& Nbsp; >>> 'antwoorden' in conn.root ()
& Nbsp; Valse
& Nbsp; >>> event = DatabaseOpenedEventStub (db)
& Nbsp; >>> evolveMinimumSubscriber (event)
& Nbsp; >>> conn.sync ()
& Nbsp; >>> root = conn.root ()
& Nbsp; >>> pprint (root ['antwoorden'])
& Nbsp; {'Hallo': 'Hi & hoe doe je?',
& Nbsp; 'zin van het leven?': '42',
& Nbsp; 'vier ': 'Vier
& Nbsp; 2
De ZODB transactielogboek merkt op dat onze installatie script werd geëxecuteerd
& Nbsp; >>> [. It.description voor het in conn.db () storage.iterator ()] [- 2]
& Nbsp; u'some.app: install te generation '
(Kleine opmerking: het is niet het laatste record, want er zijn twee commits: MySchemaManager voert een, en evolveMinimumSubscriber voert de tweede MySchemaManager niet echt nodig om te plegen.).
Wat is nieuw in deze release:.
- Ondersteuning toegevoegd voor Python 3.3
- Vervangen deprecated zope.interface.implements gebruik met gelijkwaardige zope.interface.implementer decorateur.
- Dropped ondersteuning voor Python 2.4 en 2.5.
Wat is nieuw in versie 3.7.1:
- Removed buildout deel dat werd gebruikt tijdens de ontwikkeling, maar doet niet compileren op Windows.
- Generation scripts toe een transactie noot.
Eisen
- Python
Reacties niet gevonden