why is my Python GAE code asynchronous? - python

This is the code:
def create_game(user_id):
game = classes.Games(user_id = user_id)
game.put()
def get_game(user_id):
game_query = classes.Games.gql('WHERE user_id = :1', user_id)
game = False
for item in game_query:
game = item
if not game:
create_game(user_id)
get_game(user_id)
else:
return game
def render_page(self):
message = 'this is a game page<br>'.decode('utf-8')
user = creditentials.get_user(self)
if not user:
self.redirect('/')
return
game = get_game(user.key().id())
message += 'current game ID: '.decode('utf-8') + str(game.key().id())
self.response.write(message)
I expect it to create just one instance of the game, instad it creates 10! Appearantly GQL query is perfromed asynchronously, and starting from the 3rd (?) instance of the get_game(user_id) it just skips game_query = classes.Games.gql('WHERE user_id = :1', user_id) line.
Am I right? How do I avoid this?

Queries aren't immediately consistent, so an entity that you've only just created won't be returned in a query performed right-away, you need to wait a bit.
In your case, you don't need to query for the entity - you just created it, so you know it exists, and can use it. Change your create_game function to return the new game, and then use that.
If you expect your user_id to be unique (and given your query, this seems to be the case) you could use it as the entity-id, then you can get-by-id instead of querying, which will strongly-consistent.
Check the "Data Consistency" section on this docs page for more detail on how queries work.

Related

Finding class via string

import random
class Game():
def __init__(self, username, gameId):
self.users = []
self.users.append(str(username))
self.gameId = gameId
def new_user(self, username):
self.users.append(str(username))
def remove_user(self, username):
try:
self.users.remove(username)
except:
print("[-] User not found!")
def generate_gameId():
gameId = ""
letters = 5
while(letters>0):
gameId += chr(random.randint(65, 90))
letters-=1
return(gameId)
lobby = []
for i in range(2):
lobby.append(generate_gameId())
lobby[i] = Game("Test", lobby[i])
lobby[i].new_user("Test123")
lobby[i].remove_user("Test123")
This is my code for a simple networking game, I will have multiple Game classes at the same time, but I need to find the specific object of a specific gameId. The gameId is randomly generated. Each time a user wants to join the lobby he has to enter the gameId to enter.
How would you achieve something like this? Am I doing it wrong?
There are some things that can be refactored in your code:
In the constructor of your Game class, there's no need for a username parameter, since there's already a new_user method:
class Game():
def __init__(self, gameId):
# Just create the list of users
self.users = []
self.gameId = gameId
# ...
lobby = []
for i in range(2):
lobby.append(generate_gameId())
lobby[i] = Game(lobby[i])
# Use the `new_user` method to create a Test
lobby[i].new_user("Test")
lobby[i].new_user("Test123")
lobby[i].remove_user("Test123")
You're storing the ids in an integer list. You should use a dictionary given that a game will have an unique id:
lobby = {}
for i in range(2):
game_id = generate_gameId()
game = Game(game_id)
# Create a entry in the dictionary
lobby[game_id] = game
game.new_user("Test")
game.new_user("Test123")
game.remove_user("Test123")
Then, you can access the list of games and their ids:
for game_id, game in lobby.items():
print(f'The game {game_id} has the following users:')
for user in game.users:
print(user)
print()
The other guys said everything I was going to say so I deleted most of my post, but here's some other things you could improve on if you want:
You are not looking up a "Class" here. You're looking up an instance of a class, otherwise known as an object. The word "class" in programming always means "The definition of an object". Classes can be instantiated to make objects AKA instances. A good analogy is that a "class" is the blueprints for making a car, while the instance/object would be the car itself that was made using the blueprints(the class).
Don't combine naming conventions. You're combining camel case and snake case which is never a good idea, choose one or the other (python is usually snake case). Specifically, generate_gameId() should be generate_game_id(). This just makes it easier to write code without making spelling mistakes.

Django find item in queryset and get next

I'm trying to take an object, look up a queryset, find the item in that queryset, and find the next one.
#property
def next_object_url(self):
contacts = Model.objects.filter(owner=self.owner).order_by('-date')
place_in_query = list(contacts.values_list('id', flat=True)).index(self.id)
next_contact = contacts[place_in_query + 1]
When I add this to the model and run it, here's what I get for each variable for one instance.
CURRENT = Current Object
NEXT = Next Object
contacts.count = 1114
self.id = 3533 #This is CURRENT.id
place_in_query = 36
contacts[place_in_query] = NEXT
next_contact = CURRENT
What am i missing / what dumb mistake am i making?
In your function, contacts is a QuerySet. The actual objets are not fetched in the line:
contacts = Model.objects.filter(owner=self.owner).order_by('-date')
because you don’t use a function like list(), you don’t iterate the QuerySet yet... It is evaluated later. This is probably the reason of your problem.
Since you need to search an ID in the list of contacts and the find the next object in that list, I think there is no way but fetch all the contact and use a classic Python loop to find yours objects.
#property
def next_object_url(self):
contacts = list(Model.objects.filter(owner=self.owner).order_by('-date').all())
for curr_contact, next_contact in zip(contacts[:-1], contacts[1:]):
if curr_contact.id == self.id:
return next_contact
else:
# not found
raise ContactNotFoundError(self.id)
Another solution would be to change your database model in order to add a notion of previous/next contact at database level…

how to get entities which don't have certain attribute in datastore

I'm trying to make an appraisal system
This is my class
class Goal(db.Expando):
GID = db.IntegerProperty(required=True)
description = db.TextProperty(required=True)
time = db.FloatProperty(required=True)
weight = db.IntegerProperty(required=True)
Emp = db.UserProperty(auto_current_user=True)
Status = db.BooleanProperty(default=False)
Following things are given by employee,
class SubmitGoal(webapp.RequestHandler):
def post(self):
dtw = simplejson.loads(self.request.body)
try:
maxid = Goal.all().order("-GID").get().GID + 1
except:
maxid = 1
try:
g = Goal(GID=maxid, description=dtw[0], time=float(dtw[1]), weight=int(dtw[2]))
g.put()
self.response.out.write(simplejson.dumps("Submitted"))
except:
self.response.out.write(simplejson.dumps("Error"))
Now, here Manager checks the goals and approve it or not.. if approved then status will be stored as true in datastore else false
idsta = simplejson.loads(self.request.body)
try:
g = db.Query(Goal).filter("GID =", int(idsta[0])).get()
if g:
if idsta[1]:
g.Status=True
try:
del g.Comments
except:
None
else:
g.Status=False
g.Comments=idsta[2]
db.put(g)
self.response.out.write(simplejson.dumps("Submitted"))
except:
self.response.out.write(simplejson.dumps("Error"))
Now, this is where im stuck..."filter('status=',True)".. this is returning all the entities which has status true.. means which are approved.. i want those entities which are approved AND which have not been assessed by employee yet..
def get(self):
t = []
for g in Goal.all().filter("Status = ",True):
t.append([g.GID, g.description, g.time, g.weight, g.Emp])
self.response.out.write(simplejson.dumps(t))
def post(self):
idasm = simplejson.loads(self.request.body)
try:
g = db.Query(Goal).filter("GID =", int(idasm[0])).get()
if g:
g.AsmEmp=idasm[1]
db.put(g)
self.response.out.write(simplejson.dumps("Submitted"))
except:
self.response.out.write(simplejson.dumps("Error"))
How am I supposed to do this? as I know that if I add another filter like "filter('AsmEmp =', not None)" this will only return those entities which have the AsmEmp attribute what I need is vice versa.
You explicitly can't do this. As the documentation states:
It is not possible to query for entities that are missing a given property.
Instead, create a property for is_assessed which defaults to False, and query on that.
could you not simply add another field for when employee_assessed = db.user...
and only populate this at the time when it is assessed?
The records do not lack the attribute in the datastore, it's simply set to None. You can query for those records with Goal.all().filter('status =', True).filter('AsmEmp =', None).
A few incidental suggestions about your code:
'Status' is a rather unintuitive name for a boolean.
It's generally good Python style to begin properties and attributes with a lower-case letter.
You shouldn't iterate over a query directly. This fetches results in batches, and is much less efficient than doing an explicit fetch. Instead, fetch the number of results you need with .fetch(n).
A try/except with no exception class specified and no action taken when an exception occurs is a very bad idea, and can mask a wide variety of issues.
Edit: I didn't notice that you were using an Expando - in which case #Daniel's answer is correct. There doesn't seem to be any good reason to use Expando here, though. Adding the property to the model (and updating existing entities) would be the easiest solution here.

Django: String object prints out None

Hello I have a view that deletes order numbers. Once a string has been deleted, the web page redirects to the clients page letting the user know that the order number was deleted. There is a problem. I am getting this.
Previous order None deleted
It says None but I want it to state the order number that I have deleted. It seems it says none because I have already deleted that order number so when it looks in the order number list, it cannot find that number.
EDIT: here is the solution. Order number should be deleted after the message is sent.
def delete_confirmation(request, order_no = 0, service_type = 0):
order = None
count = 0
title = models.SERVICE_CHOICES[int(service_type) - 1][1]
#title = type[1]
order_number = request.session['order_number']
try:
order = models.Order.objects.get(pk = order_no)
count = order.orderservicelist_set.count()
if request.POST.get('delete'):
request.user.message_set.create(message = "Previous order " + str(order_number.pk) + " deleted")
order_number.delete()
return HttpResponseRedirect(reverse(return_clients))
except:
return HttpResponseRedirect(reverse(return_clients))
return render_to_response('delete_order.html', {'order':order, 'title':title, 'count':count, 'order_no':order_no }, context_instance = RequestContext(request))
One of the things that delete() does on a model instance is set its pk to None, so that if you try and save it, it will create a new instance rather than trying to update the old (no-longer-existing) one.
So, easy solution: grab the PK before you delete. In fact, you could just get rid of the first call to delete you have there, because you call it again after creating the message.
By the way, a better way of creating the message is to use string formatting:
message = "Previous order %s deleted" % order_number.pk
Very Careless mistake. why have I got order_number.delete() written twice?
if request.POST.get('delete'):
order_number.delete() # Why was this there? of course, if I have this it will delete till it gets to the next part where it informs the user which order number was deleted!
request.user.message_set.create(message = "Previous order " + str(order_number.pk) + " deleted")
order_number.delete()
fixed

In Google App Engine, how do I avoid creating duplicate entities with the same attribute?

I am trying to add a transaction to keep from creating two entities with the same attribute. In my application, I am creating a new Player each time I see a new Google user logged in. My current implementation occasionally creates duplicate players when multiple json calls are made by a new Google user within a few milliseconds. When I add the transaction like the one commented out here, I get various errors. What is the easiest way to ensure that I never create two player entities with the same user_id?
def get_player_from_user(self, user_id):
player = Player.all().filter('user_id =', user_id).get()
if not player:
#This can result in duplicate players with the same user_id being created.
player = self.create_new_player(user_id)
#This is what I'm trying to do.
#player = db.run_in_transaction(self.create_new_player, user_id=user_id)
return player
def create_new_player(self,user_id):
#Check one more time for an existing user_id match.
player = Player.all().filter('user_id =', user_id).get()
if player:
return player
player = Player()
player.user_id = user.user_id()
player.put()
return player
Use the username (or other identifier) as the key name, and use get_or_insert to transactionally create a new entity or return the existing one. Sahid's code won't work, because without a transaction, a race condition is still possible.
Maybe you can use key name and get_by_key_name is better than filter.
def create_new_player(self,user_id):
key_name = "player/%s" % user_id
player = Player.get_by_key_name (key_name)
if player is None:
player = Player (key_name=key_name, user_id=user_id)
player.put ()
return player
With the last comment of Nick, i have updated my code,
so the better solution is:
def create_new_player(self,user_id):
key_name = "player/%s" % user_id
player = Player.get_or_insert (key_name=key_name, user_id=user_id)
return player

Categories