סוג מודל NDB

מחלקת Model שממנה מתבצעת ירושה מייצגת את מבנה הישויות שמאוחסנות במאגר הנתונים. האפליקציות מגדירות מחלקות של מודלים כדי לציין את המבנה של הישויות שלהן, ואז יוצרות מופעים של המחלקות האלה כדי ליצור ישויות. כל מחלקות המודלים חייבות להיות נגזרות (ישירות או עקיפות) מהמחלקה 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 של מודל האב או None של מודל ברמה העליונה. אם משתמשים בדגל 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 ל-constructor, ומגדירים את המפתח לאחר מכן.

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)
Returns an entity by ID. זה בעצם קיצור של 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 that runs after put()

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

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

חיפוש לפי סוג

לכל מודל יש סוג, שבדרך כלל זהה לשם הסיווג, אלא אם הוגדר אחרת. אפשר להשתמש ב-kind כדי למצוא את מחלקת המודל המשויכת באמצעות _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.

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