Pobieranie i usuwanie encji z datastore

Instancje klas Model i Expando przedstawiają encje z datastore. Aplikacja tworzy nową encję danego typu wywołując konstruktor klasy modelu:

pet = Pet(name="Fluffy",
          type="cat",
          owner=users.get_current_user())

Nowa encja nie jest tworzona w datastore od razu. Należy wywołać metodę put() na instancji klasy, lub przekazać ją do db.put():

pet.put()

db.put(pet)

Jeżeli instancja została już zapisana do datastore wykonanie metody put() spowoduje zaktualizowane encji. Zapytania zwracają wyniki w postaci instancji klas danego modelu. Te instancje można zmodyfikować i zapisać zmiany do datastore:

if users.get_current_user():
  user_pets = db.GqlQuery("SELECT * FROM Pet WHERE owner = :1",
                          users.get_current_user())
  for pet in user_pets:
    pet.spayed_or_neutered = True

  db.put(user_pets)

Pobieranie encji za pomocą zapytań

Datastore może wykonywać zapytania na encjach danego typu. Zapytanie może filtrować wyniki używając warunków, które wartości właściwości muszą spełnić. Zapytanie może także zwrócić wyniki posortowane według wartości właściwości. Można także ograniczyć ilość pobieranych encji.

API datastore dostarcza dwa interfejsy do wykonywania zapytań na właściwościach encji: interfejs zapytań GQL przypominający SQL, oraz interfejs obiektowy.

Interfejs zapytań

Metoda all() na klasach Model i Expando zwraca obiekt Query, który reprezentuje zapytanie dla wszystkich encji danego typu. Aplikacja przygotowuje zapytanie wykonując metody filter(), order() i ancestor() na tym obiekcie:

class Story(db.Model):
  title = db.StringProperty()
  date = db.DateTimeProperty()

query = Story.all()

query.filter('title =', 'Foo')
query.order('-date')
query.ancestor(key)

# Metod tych można używać też tak:
query.filter('title =', 'Foo').order('-date').ancestor(key)

Konstruktor klasy GqlQuery przyjmuje jako argument łańcuch z zapytaniem GQL, oraz opcjonalnie przypisania parametrów. Oto przykłady

# Parametry można przypisywać względem kolejności
query = db.GqlQuery("SELECT * FROM Story WHERE title = :1 "
                    "AND ANCESTOR IS :2 "
                    "ORDER BY date DESC",
                    'Foo', key)

# Można je też przypisywać po nazwach
query = db.GqlQuery("SELECT * FROM Story WHERE title = :title "
                    "AND ANCESTOR IS :parent "
                    "ORDER BY date DESC",
                    title='Foo', parent=key)

# Łańcuchy, liczby i wartości logiczne mogą być literałami w zapytaniu
query = db.GqlQuery("SELECT * FROM Story WHERE title = 'Foo' "
                    "AND ANCESTOR IS :parent "
                    "ORDER BY date DESC",
                    parent=key)

Można także użyć metody gql() na instancji klasy modelu. W tym przypadku pomijany jest element "SELECT ... FROM" zapytania:

query = Story.gql("WHERE title = :title "
                  "AND ANCESTOR IS :parent "
                  "ORDER BY date DESC",
                  title='Foo', parent=key)

Przypisane parametry mogą zostać przypisane na nowo z nowymi wartościami za pomocą metody bind. Aplikacja może ponownie wykorzystać obiekt GqlQuery przypisując mu na nowo parametry i wykonując zapytanie ponownie.

Wykonywanie zapytania i dostęp do wyników

Obiekty Query i GqlQuery nie wykonują zapytania do czasu gdy aplikacja próbuje dostać się do wyników. Gdy ma to miejsce zapytanie jest wykonywane, a wyniki ładowane są do pamięci jako instancje klasy modelu. Oba interfejsy zapytań obsługują te same dwa sposoby dostępu do wyników: metodę fetch(), oraz iterator

Metoda fetch() przyjmuje jako argument maksymalną ilość wyników do pobrania, oraz opcjonalnie drugi argument - ilość wyników do pominięcia (offset). Metoda wykonuje zapytanie i pobiera wyniki aż uzbiera się limit pobranych encji, lub nie będzie więcej danych do pobrania (cokolwiek pierwsze). Encje ładowane są ładowane i na te dane nakładany jest offset (jeżeli podany) i finalnie zwracana jest lista instancji modelu.

results = query.fetch(10)
for result in results:
  print "Title: " + result.title

Offset nie wpływa na ilość pobranych encji z datastore. Wszystkie wyniki aż do limitu są pobierane i umieszczane w pamięci. Offset wpływa tylko na listę zwróconą przez aplikację!

Jeżeli obiekt zapytania zostanie użyty jako iterator zapytanie zostanie wykonane bez limitów i ofsetu i załadowane do pamięci. Zwrócony zostanie iterator wyników zapytania.

for result in query:
  print "Title: " + result.title

Notka: datastore zwraca maksymalnie 1000 wyników niezależnie od ustawionego limitu i offsetu.

Pobieranie encji używając klucza

Każda encja posiada unikatowy klucza. Klucze te w API reprezentowane są przez instancje klasy Key. Metoda put() i funkcja db.put() zwracają instancję Key dla danej encji.

key = entity.key()
# ...
entity = db.get(key)

Częstym zastosowaniem klucza jest zapisywanie go jako wartości właściwości innej encji. Klasa ReferenceProperty zajmuje się automatycznym tworzeniem i usuwaniem referencji:

class PetOwner(db.Model):
  name = db.StringProperty()

class Pet(db.Model):
  name = db.StringProperty()
  owner = db.ReferenceProperty(PetOwner)

owner = PetOwner(name="Albert")
pet = Pet(name="Fluffy", owner=owner)

# to jest jednoznaczne z wcześniejszym wierszem
pet = Pet(name="Fluffy", owner=owner.key())

Zapisana w referencji encja nie jest pobierana aż do czasu próby jej użycia:

pets = db.GqlQuery("SELECT * FROM Pet WHERE name = :1", "Fluffy")
pet = pets.get()

# tutaj jest pobierana encja z referencji
owner_name = pet.owner.name

F unkcja db.get() pobiera encje z datastore dla podanej instancji Key (lub listy instancji). Klucz można przekształcić w łańcuch do wykorzystania w aplikacji, a następnie odtworzyć go za pomocą db.Key:

obj = MyModel(name="Foo")
self.response.write('<a href="/view?key=%s">%s</a>' % (str(obj.key()), 
                                                      obj.name()))

# ...

key_name = self.request.get('key')
obj = db.get(db.Key(key_name))
Jeżeli nie chcesz ujawniać kluczy użytkownikom jak w powyższym przykładzie musisz zastosować jakiś mechanizm ich "utajniania".

Kasowanie encji

Aplikacja może skasować encję z bazy używając instancji modelu lub klasy Key (klucza). Metoda delete() instancji modelu kasuje daną encje. Funkcja delete() przyjmuje jako argument klucz lub listę kluczy i kasuje encje z bazy.

q = db.GqlQuery("SELECT * FROM Message WHERE create_date < :1", earliest_date)
results = q.fetch(10)
for result in results:
  result.delete()

# or...

q = db.GqlQuery("SELECT __key__ FROM Message WHERE create_date < :1", earliest_date)
results = q.fetch(10)
db.delete(results)

Skasowanie encji nie spowoduje usunięcia klucza w referencjach. Aplikacja powinna sprawdzać wtedy dostępność encji z referencji przed jej użyciem i stosowne obsłużenie braku tej encji.

Kasując encję, która jest przodkiem dla innych encji nie wpływa na nie. Jeżeli aplikacja nie wymaga istnienia przodka do budowania kluczy dla potomnych encji - będzie mogła nadal uzyskać dostęp dla potomnych encji ze skasowanym przodkiem.

Na podstawie Creating, Getting and Deleting Data.
RkBlog

Google App Engine (GAE), 1 August 2009

Comment article
Comment article RkBlog main page Search RSS Contact