סיווג דגם NDB

מחלקת Model שממנה מתבצעת ירושה מייצגת את מבנה הישויות שמאוחסנות ב-Datastore. אפליקציות מגדירות מחלקות של מודלים כדי לציין את המבנה של הישויות שלהן, ואז יוצרות מופעים של המחלקות האלה כדי ליצור ישויות. כל מחלקות המודלים חייבות להיות נגזרות (ישירות או עקיפות) מהמחלקה Model.

בדף הזה מופיע תיעוד של הפניות ל-API. לסקירה כללית, אפשר לעיין במאמר NDB Entities and Keys.

מבוא

מחלקת Model שממנה מתבצעת ירושה מתארת ישויות של Datastore.

כל מחלקות המודלים צריכות לרשת (ישירות או בעקיפין) מ-Model. אפשר להשתמש בהקצאות פשוטות בהגדרת מחלקת המודל כדי להצהיר על המבנה של המודל:

from google.appengine.ext import ndb

class Person(ndb.Model):
  name = ndb.StringProperty()
  age = ndb.IntegerProperty()

עכשיו אפשר ליצור ישות מסוג Person ולכתוב אותה ב-Datastore:

p = Person(name='Arthur Dent', age=42)
k = p.put()

הערך המוחזר מהפונקציה put() הוא Key, שאפשר להשתמש בו כדי לאחזר את אותה ישות מאוחר יותר:

p2 = k.get()
p2 == p  # Returns True

כדי לעדכן ישות, פשוט משנים את המאפיינים שלה ומחזירים אותה לכתיבה (שימו לב שהפעולה הזו לא משנה את המפתח):

p2.name = 'Arthur Philip Dent'
p2.put()

אפשר גם למחוק ישות (באמצעות המפתח):

k.delete()

הגדרות המאפיינים בגוף המחלקה מציינות למערכת את השמות ואת הסוגים של השדות שייאוחסנו ב-Datastore, אם צריך ליצור להם אינדקס, מה ערך ברירת המחדל שלהם ועוד. יש הרבה סוגים שונים של נכסים.

הערך של kind בדרך כלל שווה לשם המחלקה (לא כולל את שם המודול או כל היקף אב אחר). כדי לבטל את הסוג (שימושי לשינויים בסכימה), מגדירים שיטת מחלקה בשם _get_kind(), באופן הבא:

  class MyModel(ndb.Model):
    @classmethod
    def _get_kind(cls):
      return 'AnotherKind'

באפליקציה אסור להגדיר שני סוגים של מודלים עם אותו סוג, גם אם הם נמצאים במודולים שונים. הסוגים של אפליקציה נחשבים ל"מרחב שמות" גלובלי.

Model מחלקות משנה יכולות להגדיר hooks לפני ואחרי שיחות לרוב הפעולות (get, put, delete, allocate_ids).

Constructor

בדרך כלל אפליקציה לא קוראת ל-Model(), אבל סביר להניח שהיא קוראת לבונה של מחלקה שיורשת מ-Model. הפעולה הזו יוצרת מופע חדש של המודל הזה, שנקרא גם ישות.

הישות שנוצרה לא נכתבת אוטומטית ב-Datastore. כדי שזה יקרה, צריך לכתוב אותה ב-Datastore באמצעות קריאה מפורשת ל-put().

ארגומנטים:

Model תומך בארגומנטים הבאים של מילות מפתח:

key
מופע
Key של המודל הזה. אם משתמשים בפרמטר key, הפרמטרים id ו-parent חייבים להיות None (ברירת המחדל).
id
מזהה המפתח של המודל הזה. אם משתמשים ב-id, המפתח חייב להיות None (ברירת המחדל).
parent
Key instance for the parent model or None for a top-level one. אם משתמשים בדגל parent, הערך של הדגל key חייב להיות None.
namespace
Namespace לשימוש עבור הישות הזו, או None (ברירת מחדל) לשימוש במרחב השמות הנוכחי. אם משתמשים בדגל namespace, הערך של הדגל key חייב להיות None.

אפליקציה יכולה גם להשתמש במיפוי של ארגומנטים של מילות מפתח למאפיינים של המודל. לדוגמה, הקוד הבא פועל:

class Person(ndb.Model):
  name = StringProperty()
  age = IntegerProperty()

p = Person(name='Arthur Dent', age=42)

אי אפשר להגדיר בקלות מאפיין בשם key,‏ id,‏ parent או namespace. אם מעבירים, לדוגמה, את הערך key="foo" בקונסטרוקטור או בקריאה populate(), הערך הזה מוגדר כמפתח של הישות, ולא כמאפיין של נכס בשם key.

הערה: אם מחליפים את הקונסטרוקטור במחלקת משנה של Model, צריך לשים לב לכך שהקונסטרוקטור נקרא גם באופן מרומז במקרים מסוימים, ולוודא שהקריאות האלה נתמכות. כשקוראים ישות מ-Datastore, קודם נוצרת ישות ריקה על ידי קריאה לקונסטרוקטור ללא ארגומנטים, ואחרי כן המפתח וערכי המאפיינים מוגדרים אחד אחרי השני. כשמשתמשים ב-get_or_insert() או ב-get_or_insert_async() כדי ליצור מופע חדש, הארגומנטים **constructor_args מועברים לקונסטרוקטור, והמפתח מוגדר לאחר מכן.

Class Methods

allocate_ids(size=None, max=None, parent=None, **ctx_options)

מקצה טווח של מזהי מפתחות למחלקת המודל הזו.

ארגומנטים

size
מספר המזהים להקצאה. אפשר לציין את size או את max, אבל לא את שניהם.
max
המזהה המקסימלי להקצאה. אפשר לציין את size או את max, אבל לא את שניהם.
parent
מפתח האב שעבורו יוקצו המזהים.
**ctx_options
אפשרויות הקשר

מחזירה טאפל עם (התחלה, סיום) לטווח שהוקצה, כולל.

אפליקציה לא יכולה לקרוא ל-allocate_ids() בעסקה.

allocate_ids_async(size=None, max=None, parent=None, **ctx_options)

גרסה אסינכרונית של allocate_ids.

הפונקציה מחזירה אובייקט Future שהתוצאה שלו היא טופל עם (התחלה, סיום) לטווח שהוקצה, כולל.

get_by_id(id, parent=None, app=None, namespace=None, **ctx_options)
מחזירה ישות לפי מזהה. זוהי למעשה קיצור דרך ל- Key(cls, id).get().

ארגומנטים

id
מזהה מפתח מסוג מחרוזת או מספר שלם.
parent
המפתח של המודל שאותו רוצים לקבל.
app (ארגומנט של מילת מפתח)
מזהה האפליקציה. אם לא מצוין, הנתונים מתקבלים עבור האפליקציה הנוכחית.
namespace (ארגומנט של מילת מפתח)
מרחב שמות. אם לא מציינים מרחב שמות, המערכת מקבלת נתונים עבור מרחב השמות שמוגדר כברירת מחדל.
**ctx_options
אפשרויות הקשר

הפונקציה מחזירה מופע של מודל או None אם המודל לא נמצא.

get_by_id_async(id, parent=None, app=None, namespace=None, **ctx_options)
גרסה אסינכרונית של get_by_id.

הפונקציה מחזירה אובייקט Future שהתוצאה שלו היא מופע של מודל או None אם לא נמצא מופע.

get_or_insert(key_name, parent=None, app=None, namespace=None, context_options=None, **constructor_args)
שליפה טרנזקציונלית של ישות קיימת או יצירה של ישות חדשה.

ארגומנטים

key_name
שם המפתח (כלומר, מזהה מפתח מסוג מחרוזת) לאחזור או ליצירה.
parent
המפתח של ישות האם, אם יש.
אפליקציה
מזהה האפליקציה. אם לא מצוין, הנתונים מתקבלים עבור האפליקציה הנוכחית.
namespace
מרחב שמות. אם לא מציינים מרחב שמות, המערכת מקבלת נתונים עבור מרחב השמות שמוגדר כברירת מחדל.
context_options
אפשרויות הקשר

הפונקציה הזו מקבלת גם ארגומנטים של מילות מפתח להעברה לבונה של מחלקת המודל, אם מופע של שם המפתח שצוין עדיין לא קיים. אם מופע עם key_name והורה שסופקו כבר קיים, הארגומנטים האלה יימחקו.

מחזירה מופע קיים של מחלקת Model עם שם המפתח וההורה שצוינו, או מופע חדש שנוצר זה עתה.

הפונקציה הזו משתמשת בעסקה. אם הקוד שמפעיל את הפונקציה הזו כבר נמצא בטרנזקציה, הפונקציה הזו מנסה לעשות שימוש חוזר בטרנזקציה הקיימת. אם קבוצת הישויות של הפונקציה הזו לא תואמת לעסקה הקיימת, יכולה להתרחש שגיאה.

get_or_insert_async(key_name, parent=None, app=None, namespace=None, context_options=None, **constructor_args)

זוהי הגרסה האסינכרונית של get_or_insert.

הפונקציה מחזירה אובייקט Future שהתוצאה שלו היא מופע קיים של מחלקת Model עם שם המפתח וההורה שצוינו, או מופע חדש שנוצר זה עתה.

query([filter1, filter2, ...,] ancestor=None, app=None, namespace=None, filters=None, orders=None, default_options=None, projection=None distinct=False group_by=None)

יוצרת אובייקט Query לכיתה הזו, כמו שמתואר במאמר שאילתות.

הארגומנט של מילת המפתח distinct הוא קיצור של group_by = projection. כל הארגומנטים האחרים של מילות המפתח מועברים אל הבונה של Query.

אם מציינים ארגומנטים מיקומיים, הם משמשים להגדרת מסננים ראשוניים.

מחזירה אובייקט Query.

Instance Methods

populate(**constructor_options)

מגדיר את הערכים של מאפייני הישות. ארגומנטי מילות המפתח שלו מזהים באופן אוטומטי שמות של מאפיינים, כמו בבונה.

put(**ctx_options)

כתיבת נתוני הישות אל Datastore. מחזירה את המפתח של הישות.

ארגומנטים

**ctx_options
אפשרויות הקשר
put_async(**ctx_options)

כתיבת הנתונים של הישות אל Datastore באופן אסינכרוני. הפונקציה מחזירה אובייקט Future. התוצאה של האובייקט Future תהיה Key של הישות.

ארגומנטים

**ctx_options
אפשרויות הקשר
to_dict(include=all, exclude=None)

הפונקציה מחזירה dict שמכיל את ערכי המאפיינים של המודל. ערכי הנכסים של StructuredProperty ושל LocalStructuredProperty מומרים באופן רקורסיבי למילונים.

ארגומנטים:

include
רשימה אופציונלית של מאפיינים שרוצים לכלול. ברירת מחדל: כל המשתמשים.
exclude
רשימה אופציונלית של נכסים להחרגה. אם יש חפיפה בין include לבין exclude, אז exclude "מנצח".

הערה: אם ערך של מאפיין הוא אובייקט שניתן לשינוי (למשל רשימה שמייצגת מאפיין חוזר, או מילון או רשימה שמאוחסנים ב-JsonProperty), אלא אם הערך מומר באופן מפורש (למשל במקרה של StructuredProperty), אותו אובייקט מוחזר במילון שמאוחסן בישות. במקרים כאלה, שינוי המילון ישנה את הישות, ולהפך.

נתוני מופע

key
מאפיין מיוחד לאחסון מפתח המודל.

Hook Methods

מחלקת משנה של Model באפליקציה יכולה להגדיר אחת או יותר מהשיטות האלה כשיטות 'hook' לפני או אחרי פעולה. לדוגמה, כדי להריץ קוד מסוים לפני כל פעולת get, מגדירים את השיטה _pre_get_hook() של מחלקת המשנה של המודל. המלצות לכתיבת פונקציות hook מופיעות במאמר Model Hooks.

@classmethod
_pre_allocate_ids_hook(cls, size, max, parent)
Hook שמופעל לפני allocate_ids()
‪@classmethod
_post_allocate_ids_hook(cls, size, max, parent, future)
Hook שמופעל אחרי allocate_ids()
‪@classmethod
_pre_delete_hook(cls, key)
Hook שמופעל לפני delete()
@classmethod
_post_delete_hook(cls, key, future)
Hook שמופעל אחרי delete()
‪@classmethod
_pre_get_hook(cls, key)
Hook שמופעל לפני Key.get() כשמקבלים ישות של המודל הזה.
‪@classmethod
_post_get_hook(cls, key, future)
‫Hook שמופעל אחרי Key.get() כשמקבלים ישות של המודל הזה.
_pre_put_hook(self)
Hook שמופעל לפני put()
_post_put_hook(self, future)
Hook שמופעל אחרי put()

אינטרוספקציה

אתם יכולים להשתמש בשיטות האלה כדי לבדוק את המאפיינים וההגדרות של מודל מסוים. זה שימושי אם אתם כותבים ספרייה או פונקציה שמקבלת כמה סוגים של מודלים.

חיפוש לפי סוג

לכל מודל יש סוג, שבדרך כלל זהה לשם הסיווג, אלא אם הוגדר אחרת. אפשר להשתמש בסוג כדי למצוא את סיווג המודל המשויך באמצעות _lookup_model.

class Animal(ndb.Model):
    type = ndb.StringProperty()

print Animal._get_kind()  # 'Animal'
print ndb.Model._lookup_model('Animal')  # class Animal

הערה: _lookup_model פועל רק עבור מחלקות מודלים שכבר יובאו על ידי האפליקציה.

מאפיינים

אפשר להשתמש בפקודה _properties כדי לקבל רשימה של כל המאפיינים שמשויכים למודל.

class User(ndb.Model):
    name = ndb.StringProperty()
    email = ndb.StringProperty()

print User._properties
# {'email': StringProperty('email'), 'name': StringProperty('name')}

_properties פועל גם במופעים של Expando.

class Example(ndb.Expando):
  pass

e = Example()
e.foo = 1
e.bar = 'blah'
e.tags = ['exp', 'and', 'oh']
print e._properties
# {'foo': GenericProperty('foo'), 'bar': GenericProperty('bar'),
# 'tags': GenericProperty('tags', repeated=True)}

אפשר לבצע בדיקה של מופעי נכסים. האפשרויות שמועברות ל-constructor זמינות כמאפיינים עם הקידומת _.

print User._properties['email']._name  # 'email'
print User._properties['email']._required  # False
print User._properties['email']._default  # None
print User._properties['email']._choices  # None
print User._properties['email']._compressed  # False
print User._properties['email']._indexed  # True
print User._properties['email']._compressed  # False
print User._properties['email']._repeated  # False
print User._properties['email']._verbose_name  # None
print isinstance(User._properties['email'], ndb.StringProperty)  # True

כינויים של שיטות

לכל שיטה במחלקה Model יש כינוי עם הקידומת _. לדוגמה, _put() שווה ל-put(). המשמעות היא שאפשר להשתמש במאפיינים עם שמות שמתנגשים עם שמות של שיטות, בתנאי שתמיד משתמשים בשיטות עם הקידומת _. עם זאת, שימו לב שאי אפשר לציין בבנאי מאפיינים בשמות key, parent או id.

class MyModel(ndb.Model):
    put = ndb.StringProperty()
    query = ndb.StringProperty()
    key = ndb.StringProperty()

entity = MyModel()
entity.put = '1'
entity.query = '2'
entity.key = '3'

entity._put()
print entity
# MyModel(key=Key('MyModel', ...), put=u'1', query=u'2', key=u'3')

print MyModel._query().fetch()
# same as above.

אם אתם יוצרים ספריות של צד שלישי שפועלות עם מודלים שרירותיים, מומלץ להשתמש בשיטות עם הקידומת _.