views:

57

answers:

1

Here is some code that is not working how it is supposed to work. Every time the database is queried, I get either a 0 or 1 value for the options, and the values in database do not increment, even though as as you can see, in line 86 and 89, the values are being incremented. Any idea what's going wrong here? I am using Django on Google App engine.

        user_result = request.POST['json']
 65     user_result = json.loads(user_result)
 66     user_country = get_user_country(user_result)
 67     question_number = get_question_number(user_result)
 68     answered_option = get_answered_option(user_result)
 69 
 70     country_option_1 = 0
 71     country_option_2 = 0
 72     world_option_1 = 0
 73     world_option_2 = 0
 74 
 75     """
 76     Get already existing record for the question for the contry, or create
 77     new one and put/update in db
 78     """
 79 
 80     country_wide_data = db.GqlQuery("SELECT * FROM CountryWideData WHERE country = :1 AND questionNo = :2", user_country, question_number)
 81     flag = False
 82     for i in country_wide_data:
 83         flag = True
 84     if flag:
 85         if answered_option==1:
 86             country_wide_data[0].optionOne = country_wide_data[0].optionOne + 1
 87 
 88         elif answered_option==2:
 89             country_wide_data[0].optionTwo = country_wide_data[0].optionTwo + 1
 90         country_option_1 = country_wide_data[0].optionOne
 91         country_option_2 = country_wide_data[0].optionTwo
 92         country_wide_data[0].put()
 93     else:
 94         country_wide_data = CountryWideData(country=user_country, questionNo=question_number)
 95 
 96         if answered_option==1:
 97             country_wide_data.optionOne = 1
 98             country_wide_data.optionTwo = 0
 99         elif answered_option==2:
100             country_wide_data.optionOne = 0
101             country_wide_data.optionTwo = 1
102         country_option_1 = country_wide_data.optionOne
103         country_option_2 = country_wide_data.optionTwo
104         country_wide_data.put()
+3  A: 

You are never using fetch() to actually execute the GqlQuery that you create in line 80.

Try this:

country_wide_data = db.GqlQuery("SELECT * FROM CountryWideData WHERE country = :1 AND questionNo = :2", user_country, question_number).fetch()

By the way, you are going to want to do this incrementing inside of a transaction; otherwise, you will get a race condition if more than one request can execute this code, and the counts will be inaccurate. The documentation on transactions is here: http://code.google.com/appengine/docs/python/datastore/transactions.html

Generally, you are going to want to take the code that creates or updates these entities and put them into functions, like this:

def increment_existing_data(key, answered):
    cwd_to_incr = db.get(key)
    if answered == 1:
        cwd_to_incr.optionOne += 1
    elif answered == 2:
        cwd_to_incr.optionTwo += 1
    cwd_to_incr.put()

def create_new_data(answered, user_country, question_number):
    new_data = CountryWideData(country=user_country, questionNo=question_number)
    if answered == 1:
        new_data.optionOne = 1
        new_data.optionTwo = 0
    elif answered == 2:
        new_data.optionOne = 0
        new_data.optionTwo = 1
    new_data.put()

Then, you can call these functions using the db.run_in_transacation method, like this:

country_wide_data = db.GqlQuery("SELECT * FROM CountryWideData WHERE country = :1 AND questionNo = :2", user_country, question_number).get()
if country_wide_data is not None:
    db.run_in_transaction(increment_existing_data, country_wide_data.key(), answered_option)
else:
    db.run_in_transaction(create_new_data, answered_option,  user_country, question_number)
Adam Crossland
Thanks Adam! Though fetch needs an argument. fetch(1) worked for me.
msk
Adams, any sample code of how you would go about updating the values using a transaction? Seems like GQL doesn't allow update statements. :/
msk
Shuaib: To execute a transaction, remove all your put statements and immediately, after your loop, add: dp.put(country_wide_data).
dannyroa
dannyroa, that's not correct. Having just one put() statement will make sure that all of the changes to the entity happen at one time, but it son't do anything to prevent two different users from incrementing the entity at the same time, resulting in last-in-wins behavior and innacruate counts. Shuaib, I have added some code samples to help you understand how to use transactions.
Adam Crossland
Updating counters in transactions will solve the race condition, but it doesn't scale well. If there are more than two transactions per second you are likely to see TransactionFailedError that you'll have to catch and handle.Google has a great tutorial on sharding counters to solve both the race-condition problem and the transaction-scaling problem: http://code.google.com/appengine/articles/sharding_counters.html
mb
Very true, mb. Thanks for adding that. The sharding stuff is terrific; although, I thought that it was probably a bit much to breach in the context of this conversation. You are correct though that it would quickly become a real bottleneck.
Adam Crossland