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; >>> שורש = 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, name = 'some.app')
"Some.app 'הוא מזהה ייחודי. אתה צריך להשתמש בURI או את השם המנוקד של החבילה שלך.
כאשר אתה מתחיל Zope ומסד נתונים נפתח, IDatabaseOpenedWithRoot אירוע נשלח. Zope רושם evolveMinimumSubscriber כברירת מחדל כמטפל עבור אירוע זה. בואו לדמות זה:
& Nbsp; >>> DatabaseOpenedEventStub כיתה (אובייקט):
& Nbsp; ... def __init __ (עצמי, מסד נתונים):
& Nbsp; ... self.database = בסיס הנתונים
& Nbsp; >>> אירוע = DatabaseOpenedEventStub (db)
& Nbsp; >>> מzope.generations.generations לייבא evolveMinimumSubscriber
& Nbsp; >>> evolveMinimumSubscriber (אירוע)
התוצאה של פעולה זו היא שעכשיו את מסד הנתונים מכילים את העובדה שמספר הסכימה הנוכחית שלנו הוא 0. כאשר אנו לעדכן את הסכימה, Zope3 יהיה מושג על מה את נקודת ההתחלה הייתה. הנה, רואה?
& Nbsp; >>> מzope.generations.generations לייבא generations_key
& Nbsp; >>> ['some.app'] שורש [generations_key]
& Nbsp; 0
בחיים אמיתיים אתה לא צריך לטרוח עם מפתח זה באופן ישיר, אבל אתה צריך להיות מודע לכך שהיא קיימת.
תרחיש שדרג
חזרה לסיפור. כמה זמן עובר ואחד מהלקוחות שלנו נפרץ בגלל שאנחנו שכחתי לברוח תווים מיוחדים HTML! האימה! אנחנו חייבים לתקן את הבעיה בהקדם האפשרי מבלי לאבד נתונים. אנחנו מחליטים להשתמש דורות להרשים את העמיתים שלנו.
בואו לעדכן את מנהל הסכימה (שחררת את הישן ולהתקין אישי חדש):
& Nbsp; >>> מglobalregistry יבוא zope.component
& Nbsp; >>> gsm = globalregistry.getGlobalSiteManager ()
& Nbsp; >>> gsm.unregisterUtility (בתנאי = ISchemaManager, name = '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; ... דור elif == 2:
& Nbsp; ... לשאלה, תשובה בanswers.items ():
& Nbsp; ... del תשובות [שאלה]
& Nbsp; ... תשובות [cgi.escape (שאלה)] = תשובה
& Nbsp; ... אחר:
& Nbsp; ... להעלות ValueError ("Bummer")
& Nbsp; ... ['תשובות'] = שורש תשובות התמדה # פינג
& Nbsp; ... transaction.commit ()
& Nbsp; >>> מנהל = MySchemaManager ()
& Nbsp; >>> zope.component.provideUtility (מנהל, ISchemaManager, name = '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; {'שלום': 'היי ואיך אתה עושה?',
& Nbsp; "משמעות חיים? ':' 42 ',
& Nbsp; "ארבעה ':" ארבעה <חמש'}
בגלל evolveMinimumSubscriber הוא מאוד עצלן, זה רק מעדכן את האתר פשוט מספיק כדי שהיישום שלך יכול להשתמש בו (לminimum_generation, כלומר). ואכן, הסמן מצביע על כך שדור מסד הנתונים כבר נתקל עד 1:
& Nbsp; >>> ['some.app'] שורש [generations_key]
& Nbsp; 1
אנו רואים כי דורות עובדים, ולכן אנחנו מחליטים לקחת את הצעד הבא ולהתפתח לדור 2. בואו לראות איך זה יכול להיעשות באופן ידני:
& Nbsp; >>> מzope.generations.generations לייבא להתפתח
& Nbsp; >>> להתפתח (db)
& Nbsp; >>> pprint (['תשובות'] שורש)
& Nbsp; {'שלום': 'היי ואיך אתה עושה?',
& Nbsp; "משמעות חיים? ':' 42 ',
& Nbsp; "ארבעה ':" ארבעה <חמש'}
& Nbsp; >>> ['some.app'] שורש [generations_key]
& 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, name = 'another.app')
& Nbsp; >>> zope.component.provideUtility (
& Nbsp; ... manager2, ISchemaManager, name = 'another.app-הארכה ")
שים לב איך השם של החבילה הראשונה משמש ליצירת מרחב עבור חבילות תלויות. זה לא דרישה של המסגרת, אבל דפוס נוח לשימוש זה.
בואו להתפתח בבסיס הנתונים להקים דורות הבאים:
& Nbsp; >>> אירוע = DatabaseOpenedEventStub (db)
& Nbsp; >>> evolveMinimumSubscriber (אירוע)
& Nbsp; >>> ['another.app'] שורש [generations_key]
& Nbsp; 0
& Nbsp; >>> שורש [generations_key] ['another.app-הארכה']
& Nbsp; 0
הבה נניח כי מסיבה כלשהי כל תת-מערכות אלה צריך להוסיף דור, ושהדור 1 של 'another.app-הארכה "תלוי בדור 1 של' another.app '. אנחנו נצטרך לספק למנהלי סכימה עבור כל הרשומה שהם כבר לרוץ כדי שנוכל לוודא את התוצאה:
& Nbsp; >>> gsm.unregisterUtility (בתנאי = ISchemaManager, name = 'another.app')
& Nbsp; נכון
& Nbsp; >>> gsm.unregisterUtility (
& Nbsp; ... ובלבד = ISchemaManager, name = '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 ("Bummer")
& 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 ("Bummer")
& Nbsp; ... שורש = התמדה ['הזמנה'] הזמנה # פינג
& 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-הארכה ")
מתפתח בבסיס הנתונים עכשיו תמיד להפעיל את 'another.app' Evolver לפני 'another.app-ההארכה' Evolver:
& Nbsp; >>> אירוע = DatabaseOpenedEventStub (db)
& Nbsp; >>> evolveMinimumSubscriber (אירוע)
& Nbsp; דור יסוד 1
& Nbsp; דור תלוי 1
& Nbsp; >>> שורש ['הזמנה']
& Nbsp; ['קרן 1', 'תלוי 1']
התקנה
בדוגמא לעיל, אנו אותחלו באופן ידני את התשובות. אנחנו לא צריכים לעשות את זה באופן ידני. היישום צריך להיות מסוגל לעשות את זה באופן אוטומטי.
IInstallableSchemaManager משתרע ISchemaManager, מתן שיטת התקנה לביצוע התקנת intial של יישום. זוהי אלטרנטיבה טובה יותר מאשר רישום המנויים נפתחו מסד הנתונים.
בואו נגדיר מנהל סכימה חדש הכולל התקנה:
& Nbsp; >>> gsm.unregisterUtility (בתנאי = ISchemaManager, name = '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; שורש ... ['תשובות'] = {'שלום': '? היי ואיך אתה עושה את',
& Nbsp; ... "? משמעות של חיים ':' 42 ',
& Nbsp; ... "ארבע ':" ארבעה <חמש'}
& Nbsp; ... transaction.commit ()
& Nbsp; ...
& Nbsp; ... def להתפתח (עצמי, הקשר, דור):
& Nbsp; ... שורש = context.connection.root ()
& Nbsp; ... תשובות = ['תשובות'] שורש
& Nbsp; ... אם == דור 1:
& Nbsp; ... לשאלה, תשובה בanswers.items ():
& Nbsp; ... תשובות [שאלה] = cgi.escape (תשובה)
& Nbsp; ... דור elif == 2:
& Nbsp; ... לשאלה, תשובה בanswers.items ():
& Nbsp; ... del תשובות [שאלה]
& Nbsp; ... תשובות [cgi.escape (שאלה)] = תשובה
& Nbsp; ... אחר:
& Nbsp; ... להעלות ValueError ("Bummer")
& Nbsp; ... ['תשובות'] = שורש תשובות התמדה # פינג
& Nbsp; ... transaction.commit ()
& Nbsp; >>> מנהל = MySchemaManager ()
& Nbsp; >>> zope.component.provideUtility (מנהל, ISchemaManager, name = 'some.app')
כעת, מאפשר לפתוח מסד נתונים חדשים:
& Nbsp; >>> db.close ()
& Nbsp; >>> db = DB ()
& Nbsp; >>> conn = db.open ()
& Nbsp; "התשובות" >>> בconn.root ()
& Nbsp; False
& Nbsp; >>> אירוע = DatabaseOpenedEventStub (db)
& Nbsp; >>> evolveMinimumSubscriber (אירוע)
& Nbsp; >>> conn.sync ()
& Nbsp; >>> שורש = conn.root ()
& Nbsp; >>> pprint (['תשובות'] שורש)
& Nbsp; {'שלום': 'היי ואיך אתה עושה?',
& Nbsp; "משמעות חיים? ':' 42 ',
& Nbsp; "ארבעה ':" ארבעה <חמש'}
& Nbsp; >>> ['some.app'] שורש [generations_key]
& 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:
- חלק buildout הוסר אשר שימש במהלך פיתוח, אבל עושה לא לקמפל ב- Windows.
- תסריטי הדור להוסיף הערת עסקה.
דרישות :
- Python
תגובות לא נמצא