# Defining book models in GAE

I'm learning to work in GAE. I'm so used to SQL, but transform my way of think the last 20 years to NoSQL is a little hard for me.

I have the next simple structure:

• BOOKS than can have CHAPTERS
• CHAPTERS that can have VOTES

In a traditional SQL I just make foreign keys from VOTES to CHAPTERS, and from it to BOOKS.

I do this for my models in Datastore:

class Book(db.Model):
title = db.StringProperty(required=True)
author = db.StringProperty(required=True)

# Get all the Chapters for a book
def getChapters(self):
key = self.chapterMemkey()
chapters = memcache.get(key)
if chapters is None:
logging.info('DB access for key %s.', key)
chapters = Chapter.all().ancestor(self).order("number").fetch(100)
if not memcache.set(key, chapters, 300):
logging.error('Memcache set failed for Chapters.')
else:
logging.info('Memcache for key %s.', key)
return chapters

class Chapter(db.Model):
""" All chapters that a book have """
title = db.StringProperty(required=True)
number = db.IntegerProperty(default=1)

book = db.ReferenceProperty(Book,
required=True,
collection_name='chapters')

# Search by Book (parent)
@classmethod
def byBook(cls, book, limit=100):
chapter = book.getChapters()
return chapter

# Search by id
@classmethod
def byId(cls, id, book):
return Chapter.get_by_id(long(id), parent=book)

class Vote(db.Model):
""" All votes that a book-chapter have """
chapterNumber = db.IntegerProperty(required=True)
option = db.IntegerProperty(default=1)
value = db.IntegerProperty(default=1)

book = db.ReferenceProperty(Book,
required=True,
chapter = db.ReferenceProperty(Chapter,
required=True,

# --------------------------
# ClassMethods for the class
# --------------------------

# Search by Book (parent)
@classmethod
def byBook(cls, book, limit=100):
return vote

# Search by id
@classmethod
def byId(cls, id, book):
return Vote.get_by_id(long(id), parent=book)


Well, my doubts are:

1. Is this approach correct?
2. Must I define in the Vote class a reference for a book and for a chapter, as it was a foreign keys (just like I think I've done)?
3. Is well defined the way to retrieve the chapters from a book? I mean, in the Chapter class the function byBook uses a function from the Book class.
4. In the Vote class, I also define the byBook function but, must I define another byChapter function with similar behaviour than the myBook function? I mean something like that:

@classmethod
def byChapter(cls, chapter, limit=100):
return vote

5. Are the parents well-defined for Votes (book) or must Chapter instead be a Book? The votes are for the chapters, but a chapter belongs to book.

6. Which are the right ways to get the sum of all the votes for a specific chapter and for specific book?

1. This depends on a few things (keep reading).

2. Not necessarily. This is a question of optimization. You could fetch a vote's book by using Chapter as an intermediary (vote.getChapter().getBook()), but this would obviously require sequential trips to the database - one to get the vote, followed by one to get the chapter, and finally one to get the book. Do you plan on fetching a book for a vote frequently? If so, save some resources and stick with what you've got.

3. From a dependency standpoint, this seems counterintuitive, but since a chapter cannot exist without a book (can it?), I would say this is fine and simply a matter of preference. This always seems awkward to me as well, so maybe someone else can provide more info.

4. You can, again, this is a question of optimization and convenience. When you have questions like this, ask yourself, "will I really need this?". If the answer is no, or you're unsure, leave it out; you can always add it later if your needs change (the relations are setup correctly to do the joins, right?).

5. See above.

6. I would provide a Chapter.getVotesSum() and/or Book.getVotesSum(). These methods would include GQL queries to aggregate the appropriate rows from the votes table into a sum.

(Sorry if I'm missing something, I don't use GAE)

• Thanks, @JohnSyrinek. I preffer optimization vs comfort, so in point 2 I will use then the optimized code.In point 3, right, a chapter has no sense without a book. Finally, in point 6, I was thinking in mantain a variable to accumulate every vote, to avoid the aggregate sum every time I need this data. This is more work for me, but is a better performance (and a lot less reading) for the DB. – Eagle May 20 '13 at 14:46
• Yep, I was just waiting for any other answer, but your is also good. Thank you. By the way, I change all my code to NDB today and the use of memcache for list querys. I'm learning a lot in the last days!! – Eagle May 22 '13 at 19:44