Note that there are some explanatory texts on larger screens.

plurals
  1. POHow do I ensure data integrity for objects in google app engine without using key names?
    primarykey
    data
    text
    <p>I'm having a bit of trouble in Google App Engine ensuring that my data is correct when using an ancestor relationship without key names.</p> <p>Let me explain a little more: I've got a parent entity <strong>category</strong>, and I want to create a child entity <strong>item</strong>. I'd like to create a function that takes a category name and item name, and creates both entities if they don't exist. Initially I created one transaction and created both in the transaction if needed using a key name, and this worked fine. However, I realized I didn't want to use the name as the key as it may need to change, and I tried within my transaction to do this:</p> <pre><code>def add_item_txn(category_name, item_name): category_query = db.GqlQuery("SELECT * FROM Category WHERE name=:category_name", category_name=category_name) category = category_query.get() if not category: category = Category(name=category_name, count=0) item_query = db.GqlQuery("SELECT * FROM Item WHERE name=:name AND ANCESTOR IS :category", name=item_name, category=category) item_results = item_query.fetch(1) if len(item_results) == 0: item = Item(parent=category, name=name) db.run_in_transaction(add_item_txn, "foo", "bar") </code></pre> <p>What I found when I tried to run this is that App Engine rejects this as it won't let you run a query in a transaction: <code>Only ancestor queries are allowed inside transactions</code>.</p> <p>Looking at the <a href="http://code.google.com/appengine/docs/python/datastore/functions.html#run_in_transaction" rel="nofollow noreferrer">example</a> Google gives about how to address this:</p> <pre><code>def decrement(key, amount=1): counter = db.get(key) counter.count -= amount if counter.count &lt; 0: # don't let the counter go negative raise db.Rollback() db.put(counter) q = db.GqlQuery("SELECT * FROM Counter WHERE name = :1", "foo") counter = q.get() db.run_in_transaction(decrement, counter.key(), amount=5) </code></pre> <p>I attempted to move my fetch of the category to before the transaction:</p> <pre><code>def add_item_txn(category_key, item_name): category = category_key.get() item_query = db.GqlQuery("SELECT * FROM Item WHERE name=:name AND ANCESTOR IS :category", name=item_name, category=category) item_results = item_query.fetch(1) if len(item_results) == 0: item = Item(parent=category, name=name) category_query = db.GqlQuery("SELECT * FROM Category WHERE name=:category_name", category_name="foo") category = category_query.get() if not category: category = Category(name=category_name, count=0) db.run_in_transaction(add_item_txn, category.key(), "bar") </code></pre> <p>This seemingly worked, but I found when I ran this with a number of requests that I had duplicate categories created, which makes sense, as the category is queried outside the transaction and multiple requests could create multiple categories.</p> <p>Does anyone have any idea how I can create these categories properly? I tried to put the category creation into a transaction, but received the error about ancestor queries only again.</p> <p>Thanks!</p> <p>Simon</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload