Ringkasan Library Klien NDB Python 2 App Engine

Library Klien Google Datastore NDB memungkinkan aplikasi App Engine Python terhubung ke Datastore. Library klien NDB dibangun berdasarkan library DB Datastore lama yang menambahkan fitur penyimpanan data berikut:

  • Class StructuredProperty, yang memungkinkan entity memiliki struktur bertingkat.
  • Cache otomatis terintegrasi, yang biasanya memberikan pembacaan yang cepat dan terjangkau melalui cache dalam konteks dan Memcache.
  • Mendukung API asinkron untuk tindakan serentak selain API sinkron.

Halaman ini menyediakan pengantar dan ringkasan library klien App Engine NDB. Untuk mengetahui informasi tentang cara bermigrasi ke Cloud NDB, yang mendukung Python 3, lihat Bermigrasi ke Cloud NDB.

Menentukan Entity, Kunci, dan Properti

Datastore menyimpan objek data, yang disebut entity. Entity memiliki satu atau beberapa properti, yang berupa nilai bernama di salah satu dari beberapa jenis data yang didukung. Misalnya, properti dapat berupa string, bilangan bulat, atau referensi ke entity lain.

Setiap entity diidentifikasi oleh kunci, yaitu ID unik dalam datastore aplikasi. Kunci dapat memiliki kunci induk lain. Induk ini dapat memiliki induk sendiri, dan seterusnya; di tingkat atas "rantai" induk ini adalah kunci tanpa induk, yang disebut root.

Menunjukkan hubungan antara root entity dan entity turunan dalam suatu entity group

Entity yang kuncinya memiliki root yang sama membentuk entity group atau grup. Jika entity berada di grup yang berbeda, perubahan pada entity tersebut kadang terlihat terjadi "tidak berurutan". Jika entity tidak terkait dalam semantik aplikasi Anda, hal tersebut diperbolehkan. Namun, jika beberapa perubahan entity harus konsisten, aplikasi Anda harus menjadikannya bagian dari grup yang sama saat membuatnya.

Diagram hubungan entity dan contoh kode berikut menunjukkan bagaimana Guestbook dapat memiliki beberapa Greetings, yang masing-masing memiliki properti content dan date.

Menampilkan hubungan entity seperti yang dibuat oleh contoh kode yang disertakan

Hubungan ini diterapkan dalam contoh kode di bawah ini.

import cgi
import textwrap
import urllib

from google.appengine.ext import ndb

import webapp2


class Greeting(ndb.Model):
    """Models an individual Guestbook entry with content and date."""
    content = ndb.StringProperty()
    date = ndb.DateTimeProperty(auto_now_add=True)

    @classmethod
    def query_book(cls, ancestor_key):
        return cls.query(ancestor=ancestor_key).order(-cls.date)


class MainPage(webapp2.RequestHandler):
    def get(self):
        self.response.out.write('<html><body>')
        guestbook_name = self.request.get('guestbook_name')
        ancestor_key = ndb.Key("Book", guestbook_name or "*notitle*")
        greetings = Greeting.query_book(ancestor_key).fetch(20)

        greeting_blockquotes = []
        for greeting in greetings:
            greeting_blockquotes.append(
                '<blockquote>%s</blockquote>' % cgi.escape(greeting.content))

        self.response.out.write(textwrap.dedent("""\
            <html>
              <body>
                {blockquotes}
                <form action="/sign?{sign}" method="post">
                  <div>
                    <textarea name="content" rows="3" cols="60">
                    </textarea>
                  </div>
                  <div>
                    <input type="submit" value="Sign Guestbook">
                  </div>
                </form>
                <hr>
                <form>
                  Guestbook name:
                    <input value="{guestbook_name}" name="guestbook_name">
                    <input type="submit" value="switch">
                </form>
              </body>
            </html>""").format(
                blockquotes='\n'.join(greeting_blockquotes),
                sign=urllib.urlencode({'guestbook_name': guestbook_name}),
                guestbook_name=cgi.escape(guestbook_name)))


class SubmitForm(webapp2.RequestHandler):
    def post(self):
        # We set the parent key on each 'Greeting' to ensure each guestbook's
        # greetings are in the same entity group.
        guestbook_name = self.request.get('guestbook_name')
        greeting = Greeting(parent=ndb.Key("Book",
                                           guestbook_name or "*notitle*"),
                            content=self.request.get('content'))
        greeting.put()
        self.redirect('/?' + urllib.urlencode(
            {'guestbook_name': guestbook_name}))


app = webapp2.WSGIApplication([
    ('/', MainPage),
    ('/sign', SubmitForm)
])

Menggunakan Model untuk menyimpan data

Model adalah class yang mendeskripsikan jenis entity, termasuk jenis dan konfigurasi untuk propertinya. Model ini kira-kira mirip tabel di SQL. Entity dapat dibuat dengan memanggil konstruktor class model, lalu disimpan dengan memanggil metode put().

Kode contoh ini menentukan class model Greeting. Setiap entity Greeting memiliki dua properti: konten teks salam dan tanggal salam dibuat.

class Greeting(ndb.Model):
    """Models an individual Guestbook entry with content and date."""
    content = ndb.StringProperty()
    date = ndb.DateTimeProperty(auto_now_add=True)
class SubmitForm(webapp2.RequestHandler):
    def post(self):
        # We set the parent key on each 'Greeting' to ensure each guestbook's
        # greetings are in the same entity group.
        guestbook_name = self.request.get('guestbook_name')
        greeting = Greeting(parent=ndb.Key("Book",
                                           guestbook_name or "*notitle*"),
                            content=self.request.get('content'))
        greeting.put()

Untuk membuat dan menyimpan salam baru, aplikasi akan membuat objek Greeting baru dan memanggil metode put().

Untuk memastikan salam di buku tamu tidak muncul "acak", aplikasi akan menetapkan kunci induk saat membuat Greeting baru. Dengan demikian, salam baru akan berada di entity group yang sama dengan salam lainnya di buku tamu yang sama. Aplikasi menggunakan fakta ini saat membuat kueri: aplikasi menggunakan kueri ancestor.

Kueri dan Indeks

Aplikasi dapat membuat kueri untuk menemukan entity yang cocok dengan beberapa filter.

    @classmethod
    def query_book(cls, ancestor_key):
        return cls.query(ancestor=ancestor_key).order(-cls.date)


class MainPage(webapp2.RequestHandler):
    def get(self):
        self.response.out.write('<html><body>')
        guestbook_name = self.request.get('guestbook_name')
        ancestor_key = ndb.Key("Book", guestbook_name or "*notitle*")
        greetings = Greeting.query_book(ancestor_key).fetch(20)

Kueri NDB standar memfilter entity berdasarkan jenis. Dalam contoh ini, query_book menghasilkan kueri yang menampilkan entity Greeting. Kueri juga dapat menentukan filter pada nilai dan kunci properti entity. Seperti dalam contoh ini, kueri dapat menentukan ancestor, sehingga hanya menemukan entity "milik" beberapa ancestor. Kueri dapat menentukan tata urutan. Jika entity tertentu memiliki setidaknya satu nilai (mungkin null) untuk setiap properti dalam filter dan tata urutan, dan semua kriteria filter dipenuhi oleh nilai properti, entity tersebut akan ditampilkan sebagai hasil.

Setiap kueri menggunakan index, yaitu tabel yang berisi hasil kueri dalam urutan yang diinginkan. Datastore dasar akan otomatis mempertahankan indeks sederhana (indeks yang hanya menggunakan satu properti).

Class ini menentukan indeks kompleksnya dalam file konfigurasi, index.yaml. Server web pengembangan akan otomatis menambahkan saran ke file ini saat menemukan kueri yang belum mengonfigurasi indeks.

Anda dapat menyesuaikan indeks secara manual dengan mengedit file sebelum mengupload aplikasi. Anda dapat mengupdate indeks secara terpisah dari proses upload aplikasi dengan menjalankan gcloud app deploy index.yaml. Jika datastore Anda memiliki banyak entity, perlu waktu lama untuk membuat indeks baru untuk entity tersebut. Dalam hal ini, sebaiknya perbarui definisi indeks sebelum mengupload kode yang menggunakan indeks baru. Anda dapat menggunakan Konsol Admin untuk mencari tahu kapan indeks selesai dibangun.

Mekanisme indeks ini mendukung berbagai kueri dan cocok untuk sebagian besar aplikasi. Namun, hal ini tidak mendukung beberapa jenis kueri yang umum pada teknologi database lainnya. Secara khusus, penggabungan tidak didukung.

Memahami Penulisan NDB: Commit, Invalidate Cache, dan Apply

NDB menulis data dalam beberapa langkah:

  • Pada fase Commit, layanan Datastore dasar akan mencatat perubahan.
  • NDB akan membatalkan cache entity/entity yang terpengaruh. Dengan demikian, pembacaan pada masa mendatang akan membaca dari (dan meng-cache) Datastore dasar, bukan membaca nilai yang sudah tidak berlaku dari cache.
  • Terakhir, mungkin beberapa detik kemudian, Datastore dasar akan menerapkan perubahan. Hal ini membuat perubahan terlihat oleh kueri global dan nantinya menghasilkan pembacaan yang konsisten.

Fungsi NDB yang menulis data (misalnya, put()) akan ditampilkan setelah pembatalan cache; fase Apply terjadi secara asinkron.

Jika terjadi kegagalan selama fase Commit, terdapat percobaan ulang otomatis. Namun jika kegagalan berlanjut, aplikasi Anda akan menerima pengecualian. Jika fase Commit berhasil tetapi Apply gagal, Apply akan di-roll forward hingga selesai jika salah satu hal berikut terjadi:

  • Datastore berkala "memeriksa" tugas Commit yang belum selesai dan menerapkannya.
  • Penulisan, transaksi, atau pembacaan yang sangat konsisten berikutnya dalam grup entity yang terpengaruh menyebabkan perubahan yang belum diterapkan untuk diterapkan sebelum pembacaan, penulisan, atau transaksi.

Perilaku ini memengaruhi bagaimana dan kapan data terlihat oleh aplikasi Anda. Perubahan mungkin tidak sepenuhnya diterapkan ke Datastore dasar selama beberapa ratus milidetik atau lebih setelah fungsi NDB ditampilkan. Kueri non-ancestor yang dijalankan saat perubahan sedang diterapkan mungkin mendapati status yang tidak konsisten, yaitu sebagian, tetapi tidak semua perubahan.

Data cache dan transaksi

Library Klien NDB dapat mengelompokkan beberapa operasi dalam satu transaksi. Transaksi tidak dapat berhasil kecuali jika setiap operasi dalam transaksi berhasil; jika salah satu operasi gagal, transaksi akan otomatis di-roll back. Hal ini sangat berguna untuk aplikasi web terdistribusi, ketika beberapa pengguna mungkin mengakses atau memanipulasi data yang sama pada waktu yang bersamaan.

NDB menggunakan Memcache sebagai layanan cache untuk "hot spot" dalam data. Jika aplikasi sering membaca beberapa entity, NDB dapat membaca entity tersebut dengan cepat dari cache.

Menggunakan Django dengan NDB

Untuk menggunakan NDB dengan framework web Django, tambahkan google.appengine.ext.ndb.django_middleware.NdbDjangoMiddleware ke entri MIDDLEWARE_CLASSES dalam file settings.py Django Anda. Sebaiknya sisipkan kode tersebut sebelum class middleware lain karena beberapa middleware lain mungkin melakukan panggilan datastore dan hal tersebut tidak akan ditangani dengan benar jika middleware tersebut dipanggil sebelum middleware ini. Anda dapat mempelajari lebih lanjut middleware Django.

Apa Langkah Selanjutnya?

Pelajari lebih lanjut: