class Author (db.Model)
name = db.StringProperty()
class Book (db.Model)
author = db.ReferenceProperty (collection_name="books", indexed=True)
name = db.StringProperty()
author = Author.get (author_key)
q = Book.all()
q.filter ("author =", author.key())
q.filter ("name =", "BOOK_NAME")
book = q.fetch(1)[0]
book.author.name
Will the last statement (book.author.name) result in a another read on datastore?
Yes
If you are just starting out consider using ndb, which has cacheing of get requests built in.
Related
Hello I'm new on Datastore and on Python and I have a basic question but it can help me to understand more the Google cloud.
I have 4 entities and let's say I have a parent (match) with children : team, player and event.
class Team(ndb.Model):
d_name = ndb.StringProperty()
d_side = ndb.StringProperty()
class Player(ndb.Model):
d_name = ndb.StringProperty()
date_of_birth = ndb.StringProperty()
d_position = ndb.StringProperty()
d_teamKey = ndb.StringProperty()
class Match(ndb.Model):
d_competition_name = ndb.StringProperty()
d_date = ndb.StringProperty()
d_pool = ndb.StringProperty()
d_season = ndb.StringProperty()
d_team1Key = ndb.StringProperty()
d_team2Key = ndb.StringProperty()
d_winning_teamKey = ndb.StringProperty()
d_match_id = ndb.StringProperty()
d_match_day = ndb.IntegerProperty()
class Event(ndb.Expando):
d_teamKey = ndb.StringProperty()
d_playerKey = ndb.StringProperty()
I know that the query if I want all the matchs day 4 is :
q = ndb.gql("SELECT * FROM Match WHERE d_match_day = 4")
But how can I seach all the players in theses match's children so that I have all the players who have played during the day 4 ?
Thank you !
Add another property to Match: A StructuredProperty, which is a list of Players (and/or Teams):
Players = ndb.StructuredProperty(Player)
Teams = ndb.StructuredProperty(Team)
Then, you can query for 4 and pull the list of Players and/or Teams.
wait.... you add the player as a child to EACH match he plays? that seems unefficient design. A child can only have 1 parent (unless I grossly misunderstood ancestor queries, which is possible, to be fair).
Anyway in one single query I don't think that's doable. I would start getting the keys from match where d_match_day = 4, and from there do a "select * from Players where match_key = " and use the list you just created. (you might need to change match_key to match your actual ancestor key, but the jist of it is there)
I am using GAE Python. I have two root entities:
class X(ndb.Model):
subject = ndb.StringProperty()
grade = ndb.StringProperty()
class Y(ndb.Model):
identifier = ndb.StringProperty()
name = ndb.StringProperty()
school = ndb.StringProperty()
year = ndb.StringProperty()
result = ndb.StructuredProperty(X, repeated=True)
Since google stores our data across several data centers, we might not get the most recent data when we do a query as shown below(in case some changes have been "put"):
def post(self):
identifier = self.request.get('identifier')
name = self.request.get('name')
school = self.request.get('school')
year = self.request.get('year')
qry = Y.query(ndb.AND(Y.name==name, Y.school==school, Y.year==year))
record_list = qry.fetch()
My question: How should I modify the above fetch operation to always get the latest data
I have gone through the related google help doc but could not understand how to apply that here
Based on hints from Isaac answer, Would the following be the solution(would "latest_record_data" contain the latest data of the entity):
def post(self):
identifier = self.request.get('identifier')
name = self.request.get('name')
school = self.request.get('school')
year = self.request.get('year')
qry = Y.query(ndb.AND(Y.name==name, Y.school==school, Y.year==year))
record_list = qry.fetch()
record = record_list[0]
latest_record_data = record.key.get()
There's a couple ways on app engine to get strong consistency, most commonly using gets instead of queries and using ancestor queries.
To use a get in your example, you could encode the name into the entity key:
class Y(ndb.Model):
result = ndb.StructuredProperty(X, repeated=True)
def put(name, result):
Y(key=ndb.Key(Y, name), result).put()
def get_records(name):
record_list = ndb.Key(Y, name).get()
return record_list
An ancestor query uses similar concepts to do something more powerful. For example, fetching the latest record with a specific name:
import time
class Y(ndb.Model):
result = ndb.StructuredProperty(X, repeated=True)
#classmethod
def put_result(cls, name, result):
# Don't use integers for last field in key. (one weird trick)
key = ndb.Key('name', name, cls, str(int(time.time())))
cls(key=key, result=result).put()
#classmethod
def get_latest_result(cls, name):
qry = cls.query(ancestor=ndb.Key('name', name)).order(-cls.key)
latest = qry.fetch(1)
if latest:
return latest[0]
The "ancestor" is the first pair of the entity's key. As long as you can put a key with at least the first pair into the query, you'll get strong consistency.
I have three databases in GAE. Hobby, Attendee and Event.
class Hobby(db.Model):
name = db.StringProperty()
htest = Hobby.get_or_insert('tennis')
htest.name = 'tennis'
htest.put()
htest = Hobby.get_or_insert('basketball')
htest.name = 'basketball'
htest.put()
htest = Hobby.get_or_insert('food')
class Event(db.Model):
title = db.StringProperty(required=True)
description = db.TextProperty()
time = db.DateTimeProperty()
location = db.TextProperty()
creator = db.UserProperty()
edit_link = db.TextProperty()
gcal_event_link = db.TextProperty()
gcal_event_xml = db.TextProperty()
hobby = db.ReferenceProperty(Hobby)
class Attendee(db.Model):
email = db.StringProperty()
hobbies = db.ListProperty(db.Key)
event = db.ReferenceProperty(Event)
Each Attendee can pick however many hobby as they desire. When a event is created, user chooses a hobby to associate the event with and invitation will be send to every attendee who has chosen that hobby. Hobby DB is preloaded database.
I want to make a query that does that.
after reading Nick's blog
http://blog.notdot.net/2010/10/Modeling-relationships-in-App-Engine
which was very helpful I feel like I'm supposed to use the method that was mentioned in there
attendees = Attendee.all()filter('hobbies =', basketball).fetch(100)
however, i'm stuck there... any help would be really appreciated.
I think you should record the invitations send in a table, say "invitationsSend" with two fields : event and attendee, which two fields are making a unique primary key.
To build this, you will have to select the data between both your tables event and attendees :
insert into invitationsSend(select E.Event, A.Attendee from Event as E, Attendee as A where E.Hobby = A.Hobby)
But I'm not familiar with your "db.listProperty" used for "hobbies" and I do not know how to look into that list. I should do this as a separate table with data "Attendee, Hobby", both as primary key.
Regards,
I'm having a problem with the datastore trying to replicate a left join to find items from model a that don't have a matching relation in model b:
class Page(db.Model):
url = db.StringProperty(required=True)
class Item(db.Model):
page = db.ReferenceProperty(Page, required=True)
name = db.StringProperty(required=True)
I want to find any pages that don't have any associated items.
You cannot query for items using a "property is null" filter. However, you can add a boolean property to Page that signals if it has items or not:
class Page(db.Model):
url = db.StringProperty(required=True)
has_items = db.BooleanProperty(default=False)
Then override the "put" method of Item to flip the flag. But you might want to encapsulate this logic in the Page model (maybe Page.add_item(self, *args, **kwargs)):
class Item(db.Model):
page = db.ReferenceProperty(Page, required=True)
name = db.StringProperty(required=True)
def put(self):
if not self.page.has_items:
self.page.has_items = True
self.page.put()
return db.put(self)
Hence, the query for pages with no items would be:
pages_with_no_items = Page.all().filter("has_items =", False)
The datastore doesn't support joins, so you can't do this with a single query. You need to do a query for items in A, then for each, do another query to determine if it has any matching items in B.
Did you try it like :
Page.all().filter("item_set = ", None)
Should work.
In the following code:
class ClassA(db.Model):
name = db.StringProperty()
class ClassB(db.Model):
name = db.StringProperty()
deleted_flag = db.BooleanProperty()
classA = db.ReferenceProperty(ClassA)
ClassA will have a property called classb_set. When I call classb_set within code, I do not want it to return items that have the deleted_flag = true.
How can I change the default filter on the classb_set query? My first instinct is to create another method in ClassA that does this filter, but is there a way that keeps the classb_set a property?
You can always use a Python property to accomplish your goal:
class ClassA(db.Model):
name = db.StringProperty()
def __get_classBdeleted(self):
return self.classB_set.filter('deleted_flag =', 'True')
classBdeleted = property(__get_classBdeleted)
class ClassB(db.Model):
name = db.StringProperty()
deleted_flag = db.BooleanProperty()
classA = db.ReferenceProperty(ClassA)