zope.generations

Screenshot Λογισμικό:
zope.generations
Στοιχεία Λογισμικού:
Εκδοχή: 4.0.0 Alpha 1
Ανεβάστε ημερομηνία: 15 Apr 15
Προγραμματιστής: Zope Corporation and Contributors
Άδεια: Δωρεάν
Δημοτικότητα: 2

Rating: nan/5 (Total Votes: 0)

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 = DB ()
& Nbsp? >>> Conn = db.open ()
& Nbsp? >>> Root = conn.root ()
Φανταστείτε ότι η αίτησή μας είναι ένας χρησμός: μπορείτε να το διδάξει να αντιδράσει σε φράσεις. Ας κρατήσουμε απλή και να αποθηκεύουν τα δεδομένα σε μια dict:
& Nbsp? >>> Ρίζα [«απαντήσεις»] = {«Γεια σας»: «Γεια σου & πώς το κάνεις»,
& 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 __ (self, βάση δεδομένων):
& Nbsp? ... Self.database = βάση δεδομένων
& Nbsp? >>> Εκδήλωση = DatabaseOpenedEventStub (dB)
& Nbsp? >>> Από zope.generations.generations εισαγωγή evolveMinimumSubscriber
& Nbsp? >>> EvolveMinimumSubscriber (εκδήλωση)
Η συνέπεια αυτής της δράσης είναι ότι τώρα η βάση δεδομένων περιέχει το γεγονός ότι τα τρέχοντα αριθμό σχήμα μας είναι 0. Όταν ενημερώσετε το σχήμα, Zope3 θα έχετε μια ιδέα του τι είναι η αφετηρία ήταν. Εδώ, βλέπετε;
& Nbsp? >>> Από zope.generations.generations εισαγωγή generations_key
& Nbsp? >>> Ρίζα [generations_key] [«some.app»]
& Nbsp? 0
Στην πραγματική ζωή, δεν πρέπει ποτέ να ασχοληθείτε με αυτό το κλειδί απ 'ευθείας, αλλά θα πρέπει να γνωρίζετε ότι υπάρχει.
Αναβάθμιση σενάριο
Επιστροφή στην ιστορία. Κάποια στιγμή περνάει και ένας από τους πελάτες μας παίρνει hacked γιατί ξέχασε να ξεφύγουν HTML ειδικούς χαρακτήρες! Η φρίκη! Πρέπει να διορθώσετε αυτό το πρόβλημα το συντομότερο δυνατόν, χωρίς να χάσει όλα τα δεδομένα. Έχουμε αποφασίσει να χρησιμοποιήσει γενιές για να εντυπωσιάσει τους συμμαθητές μας.
Ας ενημερώσει τον διαχειριστή του σχήματος (πτώση το παλιό και να εγκαταστήσετε ένα νέο προσαρμοσμένο ένα):
& Nbsp? >>> Από zope.component globalregistry εισαγωγής
& Nbsp? >>> GSM = globalregistry.getGlobalSiteManager ()
& Nbsp? >>> Gsm.unregisterUtility (εφόσον = ISchemaManager, όνομα = "some.app»)
& Nbsp? True
& Nbsp? >>> Κατηγορία MySchemaManager (αντικείμενο):
& Nbsp? ... Εργαλεία (ISchemaManager)
& Nbsp? ...
& Nbsp? ... Minimum_generation = 1
& Nbsp? ... Γενιά = 2
& Nbsp? ...
& Nbsp? ... Def εξελίσσονται (αυτο, το πλαίσιο, η γενιά):
& Nbsp? ... Root = context.connection.root ()
& Nbsp? ... Απαντήσεις root = [«απαντήσεις»]
& Nbsp? ... Αν παραγωγή == 1:
& Nbsp? ... Για την ερώτηση, απάντηση σε answers.items ():
& Nbsp? ... Απαντήσεις [ερώτηση] = cgi.escape (απάντηση)
& Nbsp? ... Elif γενιά == 2:
& Nbsp? ... Για την ερώτηση, απάντηση σε answers.items ():
& Nbsp? ... Del απαντήσεις [ερώτηση]
& Nbsp? ... Απαντήσεις [cgi.escape (ερώτηση)] = απάντηση
& Nbsp? ... Αλλού:
& Nbsp? ... Αυξήσει ValueError ("Κρίμα")
& Nbsp? ... Ρίζα ['απαντήσεις'] = # απαντήσεις ping επιμονή
& Nbsp? ... Transaction.commit ()
& Nbsp? >>> Διευθυντής = MySchemaManager ()
& Nbsp? >>> Zope.component.provideUtility (διαχειριστής, ISchemaManager, όνομα = "some.app»)
Έχουμε θέσει minimum_generation προς 1. Αυτό σημαίνει ότι η εφαρμογή μας θα αρνηθεί να τρέχει με μια βάση δεδομένων μεγαλύτερα από γενιά 1. Το χαρακτηριστικό γενιά έχει οριστεί σε 2, πράγμα που σημαίνει ότι η τελευταία γενιά που αυτό SchemaManager ξέρει περίπου είναι 2.
εξελίσσονται () είναι η κινητήριος δύναμη εδώ. Η δουλειά του είναι να πάρετε τη βάση δεδομένων από γενιά-1 σε γενιά. Παίρνει ένα πλαίσιο το οποίο έχει την ιδιότητα «σύνδεση», η οποία είναι μια σύνδεση με το ZODB. Μπορείτε να χρησιμοποιήσετε ότι για να αλλάξει αντικείμενα, όπως σε αυτό το παράδειγμα.
Στη συγκεκριμένη γενιά εφαρμογής 1 διαφεύγει τις απαντήσεις (ας πούμε, κρίσιμη, επειδή μπορούν να εισαχθούν από κανέναν!), Παραγωγή 2 ξεφεύγει από τις ερωτήσεις (ας πούμε, λιγότερο σημαντικό, γιατί αυτά μπορούν να εισαχθούν από εξουσιοδοτημένο personell μόνο).
Στην πραγματικότητα, δεν χρειάζεται πραγματικά μια προσαρμοσμένη εφαρμογή της ISchemaManager. Ένα είναι διαθέσιμη, έχουμε χρησιμοποιήσει για μια εικονική προηγουμένως. Χρησιμοποιεί ενότητες Python για την οργάνωση των λειτουργιών evolver. Δείτε docstring για περισσότερες πληροφορίες.
Στην πραγματική ζωή θα έχετε πολύ πιο σύνθετες δομές αντικείμενο από το ένα εδώ. Για να κάνετε τη ζωή σας πιο εύκολη, υπάρχουν δύο πολύ χρήσιμο διαθέσιμο σε zope.generations.utility λειτουργίες: findObjectsMatching () και findObjectsProviding (). Θα σκάψει μέσα από δοχεία αναδρομικά για να σας βοηθήσει να αναζητήσουν παλιά αντικείμενα που θέλετε να ενημερώσετε, με διασύνδεση ή με κάποια άλλα κριτήρια. Είναι εύκολο να καταλάβει, ελέγξτε docstrings τους.
Γενεές σε δράση
Έτσι, έξαλλος ο πελάτης μας κατεβάζει τελευταία μας κώδικα και επανεκκίνηση Zope. Η εκδήλωση αποστέλλεται αυτόματα και πάλι:
& Nbsp? >>> Εκδήλωση = DatabaseOpenedEventStub (dB)
& 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? >>> Εξελίσσονται (dB)
& Nbsp? >>> Pprint (ρίζα [«απαντήσεις»])
& Nbsp? {«Hello»: «Γεια σου & πώς θα το κάνετε;»,
& Nbsp? «Νόημα της ζωής;»: «42»,
& Nbsp? »Τέσσερις & Nbsp? >>> Ρίζα [generations_key] [«some.app»]
& Nbsp? 2
Η προεπιλεγμένη συμπεριφορά του εξελίσσονται αναβαθμίσεις στην τελευταία γενιά που παρέχεται από το SchemaManager. Μπορείτε να χρησιμοποιήσετε το επιχείρημα πώς να εξελίσσονται () όταν θέλετε απλά να ελέγξετε αν θα χρειαστεί να ενημερώσετε ή αν θέλετε να είστε τεμπέληδες, όπως τον συνδρομητή που έχουμε καλέσει προηγουμένως.
Ταξινόμηση των διαχειριστών σχήματος
Συχνά τα υποσυστήματα χρησιμοποιούνται για να συνθέσουν μια εφαρμογή βασίζονται σε άλλα υποσυστήματα για να λειτουργήσει σωστά. Αν και οι δύο υποσυστήματα παρέχουν στους διαχειριστές σχήμα, είναι συχνά χρήσιμο να γνωρίζουμε τη σειρά με την οποία θα πρέπει να γίνει επίκληση των evolvers. Αυτό επιτρέπει σε ένα πλαίσιο και τους πελάτες του να είναι σε θέση να εξελιχθούν σε συναυλία, και οι πελάτες μπορούν να γνωρίζουν ότι το πλαίσιο θα πρέπει να εξελιχθεί, πριν ή μετά τον εαυτό του.
Αυτό μπορεί να επιτευχθεί με τον έλεγχο των ονομάτων των υπηρεσιών κοινής ωφελείας διαχειριστή σχήματος. Οι διαχειριστές σχήμα τρέχει με τη σειρά που καθορίζεται από την ταξινόμηση των ονομάτων τους.
& 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 (dB)
& 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? True
& Nbsp? >>> Gsm.unregisterUtility (
& Nbsp? ... Παρέχεται = ISchemaManager, όνομα = "another.app-επέκτασης»)
& Nbsp? True
& Nbsp? >>> Κατηγορία FoundationSchemaManager (αντικείμενο):
& Nbsp? ... Εργαλεία (ISchemaManager)
& Nbsp? ...
& Nbsp? ... Minimum_generation = 1
& Nbsp? ... Γενιά = 1
& Nbsp? ...
& Nbsp? ... Def εξελίσσονται (αυτο, το πλαίσιο, η γενιά):
& Nbsp? ... Root = 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? ... Root = 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 (dB)
& Nbsp? >>> EvolveMinimumSubscriber (εκδήλωση)
& Nbsp? Γενιάς θεμέλια 1
& Nbsp? Εξαρτώνται από γενιά 1
& Nbsp? >>> Ρίζα [«παραγγελία»]
& Nbsp? [«Θεμέλιο 1», «εξαρτώνται 1 ']
installation
Στο παραπάνω παράδειγμα, θα αρχικοποιηθεί χειροκίνητα τις απαντήσεις. Δεν πρέπει να το κάνουμε αυτό με το χέρι. Η αίτηση θα πρέπει να είναι σε θέση να το κάνει αυτό αυτόματα.
IInstallableSchemaManager επεκτείνει ISchemaManager, παρέχοντας μια μέθοδο την εγκατάσταση για την εκτέλεση σε αρχικό εγκατάσταση μιας εφαρμογής. Αυτή είναι μια καλύτερη εναλλακτική λύση από την εγγραφή της βάσης δεδομένων άνοιξε συνδρομητές.
Ας ορίσει νέο διαχειριστή σχήμα που περιλαμβάνει την εγκατάσταση:
& Nbsp? >>> Gsm.unregisterUtility (εφόσον = ISchemaManager, όνομα = "some.app»)
& Nbsp? True
& Nbsp? >>> Από zope.generations.interfaces εισαγωγή IInstallableSchemaManager
& Nbsp? >>> Κατηγορία MySchemaManager (αντικείμενο):
& Nbsp? ... Εργαλεία (IInstallableSchemaManager)
& Nbsp? ...
& Nbsp? ... Minimum_generation = 1
& Nbsp? ... Γενιά = 2
& Nbsp? ...
& Nbsp? ... Def εγκατάσταση (αυτο, το πλαίσιο):
& Nbsp? ... Root = context.connection.root ()
& Nbsp? ... Ρίζα [«απαντήσεις»] = {«Γεια σας»: «Γεια σου & πώς το κάνεις»,
& Nbsp? ... »; Νόημα της ζωής»: «42»,
& Nbsp? ... «Τεσσάρων & Nbsp? ... Transaction.commit ()
& Nbsp? ...
& Nbsp? ... Def εξελίσσονται (αυτο, το πλαίσιο, η γενιά):
& Nbsp? ... Root = context.connection.root ()
& Nbsp? ... Απαντήσεις root = [«απαντήσεις»]
& Nbsp? ... Αν παραγωγή == 1:
& Nbsp? ... Για την ερώτηση, απάντηση σε answers.items ():
& Nbsp? ... Απαντήσεις [ερώτηση] = cgi.escape (απάντηση)
& Nbsp? ... Elif γενιά == 2:
& Nbsp? ... Για την ερώτηση, απάντηση σε answers.items ():
& Nbsp? ... Del απαντήσεις [ερώτηση]
& Nbsp? ... Απαντήσεις [cgi.escape (ερώτηση)] = απάντηση
& Nbsp? ... Αλλού:
& Nbsp? ... Αυξήσει ValueError ("Κρίμα")
& Nbsp? ... Ρίζα ['απαντήσεις'] = # απαντήσεις ping επιμονή
& Nbsp? ... Transaction.commit ()
& Nbsp? >>> Διευθυντής = MySchemaManager ()
& Nbsp? >>> Zope.component.provideUtility (διαχειριστής, ISchemaManager, όνομα = "some.app»)
Τώρα, ας ανοίξει μια νέα βάση δεδομένων:
& Nbsp? >>> Db.close ()
& Nbsp? >>> Db = DB ()
& Nbsp? >>> Conn = db.open ()
& Nbsp? 'Απαντήσεις »>>> σε conn.root ()
& Nbsp? Λάθος
& Nbsp? >>> Εκδήλωση = DatabaseOpenedEventStub (dB)
& Nbsp? >>> EvolveMinimumSubscriber (εκδήλωση)
& Nbsp? >>> Conn.sync ()
& Nbsp? >>> Root = conn.root ()
& Nbsp? >>> Pprint (ρίζα [«απαντήσεις»])
& Nbsp? {«Hello»: «Γεια σου & πώς θα το κάνετε;»,
& Nbsp? «Νόημα της ζωής;»: «42»,
& Nbsp? »Τέσσερις & Nbsp? >>> Ρίζα [generations_key] [«some.app»]
& Nbsp? 2
Το αρχείο καταγραφής συναλλαγών ZODB σημειώνει ότι script εγκατάστασης μας εκτελέστηκε
& Nbsp? >>> [. It.description για αυτό σε conn.db () storage.iterator ()] [- 2]
& Nbsp? U'some.app: τρέξιμο εγκατάσταση γενιάς »
(Μικρά σημείωση: δεν είναι η τελευταία εγγραφή επειδή υπάρχουν δύο commits: MySchemaManager εκτελεί μία, και evolveMinimumSubscriber εκτελεί το δεύτερο MySchemaManager δεν χρειάζεται πραγματικά να δεσμευτούν.).

Τι είναι καινούργιο σε αυτή την έκδοση:.

  • Προστέθηκε υποστήριξη για την Python 3.3
  • Αντικαταστάθηκε αποδοκιμαστεί zope.interface.implements χρήση με ισοδύναμο zope.interface.implementer διακοσμητής.
  • Αποσύρεται υποστήριξη για την Python 2.4 και 2.5.

Τι είναι καινούργιο στην έκδοση 3.7.1:

  • Αφαιρέθηκε τμήμα buildout που χρησιμοποιήθηκε κατά τη διάρκεια της ανάπτυξης, αλλά δεν δεν συγκεντρώνουν στα Windows.
  • σενάρια γενιάς προσθέσετε μια σημείωση συναλλαγής.

Απαιτήσεις :

  • Python

Άλλο λογισμικό του προγραμματιστή Zope Corporation and Contributors

zope.event
zope.event

14 Apr 15

zope.i18n
zope.i18n

15 Apr 15

zope.proxy
zope.proxy

14 Apr 15

Σχόλια για zope.generations

Τα σχόλια δεν βρέθηκε
προσθήκη σχολίου
Ενεργοποιήστε τις εικόνες!