GqlQuery by ID always returns None result - google-app-engine

My code is intended to:
Retrieve a user_id from a cookie, if it exists.
Query the datastore using the user_id to retrieve the user record.
Finally, if a record is found, it displays a welcome message. (signup is passed to a template)
I cannot get the app to display the welcome message. As far as I can tell the issue is that the query always returns None. I've verified the cookie and the data in the datastore exists.
What am I doing wrong with this query? Does GQL handle IDs in where clauses in a non-intuitive way?
#Get cookie
user_id = self.request.cookies.get("user_id", 0)
#Query Datastore for matching user_id
user = db.GqlQuery("SELECT * FROM User WHERE id = %s" % user_id).get()
#If a user is found, display the username
if user.username:
signup = "Welcome, %s" % user.username

The datastore has a Key property, which is made up of (optional) ancestors, the kind, and a name or id. However, there is no specific id property. (Reference)
To get an entity with a specific key, your code should look something like this:
# Get cookie
user_id = self.request.cookies.get("user_id", 0)
if not user_id:
# handle this case, otherwise the call to from_path will fail.
# Build key
user_key = db.Key.from_path('User', long(user_id))
# Get the entity
user = db.get(user_key)
# If a user is found, display the username
if user.username:
signup = "Welcome, %s" % user.username
You actually don't want to use a query in this case because you already know the key of the entity you are looking for.
When you are querying with keys, you have to specify the entire key (not just the id):
user = db.GqlQuery("SELECT * FROM User WHERE __key__ > KEY('User', %s)" % user_id).get()
Notice here I am using an inequality, since using an equality filter for a key does not make sense given that you can do a direct lookup. Here is the reference for using KEY in a GQL string and I've quoted the relevant section below:
The right-hand side of a comparison can be one of the following (as appropriate for the property's data type):
an entity key literal, with either a string-encoded key or a complete path of kinds and key names/IDs:
KEY('encoded key')
KEY('kind', 'name'/ID [, 'kind', 'name'/ID...])

Related

Convert three modelsto one single query django query

This are my model with some of the fields:
class Advertisers(models.Model):
account_manager_id = models.ForeignKey(AccountManagers, on_delete=models.CASCADE,null=True, db_column='account_manager_id',related_name="advertisers")
class AdvertiserUsers(models.Model):
user_id = models.OneToOneField('Users', on_delete=models.CASCADE,null=True,db_column='user_id', related_name='advertiser_users')
advertiser_id = models.ForeignKey('Advertisers', on_delete=models.CASCADE,null=True,db_column='advertiser_id', related_name='advertiser_users')
class Users(models.Model):
email = models.CharField(unique=True, max_length=100)
I want Id's, user ids and email of all advertisers.
Id's of all user:-
advertiser_ids = advertisers.objects.all() # can get id from here
find user_ids of advertiser_ids:
user_ids = AdvertiserUsers.objects.filter(advertiser_id__in=advertiser_ids) # can get user_id from here
find id and email using this query:
user_ids = Users.objects.filter(id__in=user_ids) # can get email from here
How to make it shorter like directly querying from Advertisers i will be able to get Users models email.
Thankyou in advance
You can filter with:
Users.objects.filter(advertiser_users__advertiser_id__isnull=False).distinct()
The .distinct() [Django-doc] will prevent returning the same Users multiple times.
You can annotate the User objects with the Advertisers primary key, etc:
from django.db.models import F
Users.objects.filter(advertiser_users__advertiser_id__isnull=False).annotate(
account_manager_id=F('advertiser_users__advertiser_id__account_manager_id'),
advertiser_id=F('advertiser_users__advertiser_id')
)
The Users objects that arise from this have a .email attribute (and the other attributes that belong to a Users object), together with a .account_manager_id and an .advertiser_id. That being said, this is probably not a good idea: the way you have modeled this right now, is that a Users object can relate to multiple Advertisers objects, so it makes not much sense to add these together.
You can for each user access the related Advertisers with:
myusers = Users.objects.filter(
advertiser_users__advertiser_id__isnull=False
).prefetch_related(
'advertiser_users',
'advertiser_users__advertiser_id'
).distinct()
for user in myusers:
print(f'{user.email}')
for advuser in user.advertiser_users.all():
print(f' {advuser.advertiser_user.pk}')
Note: normally a Django model is given a singular name, so User instead of Users.
Note: Normally one does not add a suffix _id to a ForeignKey field, since Django
will automatically add a "twin" field with an _id suffix. Therefore it should
be account_manager_id, instead of account_manager.
Advertisers.objects.all().values_list('id','account_manager_id','advertiser_users__user_id',advertiser_users__user_id__email)

Wagtail - Search Page Owner Full Name

I am setting up a blog style documentation site. I was using a user input field for author when a child page was created. I found out that Wagtail houses owner in the page model. In the interest of not duplicating data, I removed my author field so I can use the default wagtail field. However, I have set up an LDAP module for authentication so the owner is housed as an Employee ID and not a user name. This Employee ID does map to a full name though and I am able to access that on a template via the owner.get_full_name.
So the question is, how do I set up the default search to check the owner full name when performing searches? How to get this into the search index? I am still a bit new to Wagtail so this may be a case of creating an author field with a foreign key mapping back to the user table or should I be modifying the search view to include a run through the user table?
def search(request):
search_query = request.GET.get('query', None)
page = request.GET.get('page', 1)
# Search
if search_query:
search_results = Page.objects.live().search(search_query)
query = Query.get(search_query)
# Record hit
query.add_hit()
else:
search_results = Page.objects.none()
# Pagination
paginator = Paginator(search_results, 10)
try:
search_results = paginator.page(page)
except PageNotAnInteger:
search_results = paginator.page(1)
except EmptyPage:
search_results = paginator.page(paginator.num_pages)
return TemplateResponse(request, 'search/search.html', {
'search_query': search_query,
'search_results': search_results,
})
If you check the Wagtail search documentation, it describes the process for indexing callables and extra attributes:
https://docs.wagtail.io/en/stable/topics/search/indexing.html#indexing-callables-and-other-attributes
So what I would do is:
Create a get_owner_full_name() method in your page class
Add an index.SearchField('get_owner_full_name') to the search_fields
One note though, this will only work if you are using either the PostgreSQL backend, or the Elasticsearch backend. The default database backend does not support the indexing of extra fields.

Setting an entity's key in NDB in Google App Engine

How do I set an entity's key in my code.
Example, I have a user class
class User(ndb.Model)
email
I want to be able to set email property and query using email property
my_user = User(email='xyz#ab.com')
Above statement sets email property, but Google App Engine generates its own key.
Can I set the key using User(key='xyz#ab.com')
If I can set the key property in a entity, how do I query for an entity using the key
Does this work
User.query(key='xyz#ab.com').get()
I realize that I can query using email as a property, but unfortunately I can't do that in context of a transaction.
That is the reason I am asking how to get(), set() by keys.
Also, when I end up querying by properties, I need to create indexes on properties, which I know will be unique
Yes, but it's not my_user = User(key='xyz#ab.com'), it's:
my_user = User(id='xyz#ab.com')
my_user_key = my_user.put()
Now, my_user_key is the key, so you can get it by:
the_user_entity = my_user_key.get()
Or, later, you can get it by constructing a Key:
my_user_key = ndb.Key(User, id='xyz#ab.com')
the_user_entity = my_user_key.get()
or directly by:
User.get_by_id('xyz#ab.com')

Maintain uniqueness of a property in the NDB database

An NDB model contains two properties: email and password. How to avoid adding to the database two records with the same email? NDB doesn't have UNIQUE option for a property, like relational databases do.
Checking that new email is not in the database before adding—won't satisfy me, because two parallel processes can both simultaneously do the checking and each add the same email.
I'm not sure that transactions can help here, I am under this impression after reading some of the manuals. Maybe the synchronous transactions? Does it mean one at a time?
Create the key of the entity by email, then use get_or_insert to check if exists.
Also read about keys , entities. and models
#ADD
key_a = ndb.Key(Person, email);
person = Person(key=key_a)
person.put()
#Insert unique
a = Person.get_or_insert(email)
or if you want to just check
#ADD
key_a = ndb.Key(Person, email);
person = Person(key=key_a)
person.put()
#Check if it's added
new_key_a =ndb.Key(Person, email);
a = new_key_a.get()
if a is not None:
return
Take care. Changing email will be really difficult (need to create new entry and copy all entries to new parent).
For that thing maybe you need to store the email, in another entity and have the User be the parent of that.
Another way is to use Transactions and check the email property. Transaction's work in the way: First that commits is the First that wins. A concept which means that if 2 users check for email only the first (lucky) one will succeed, thus your data will be consistent.
Maybe you are looking for the webapp2-authentication module, that can handle this for you. It can be imported like this import webapp2_extras.appengine.auth.models. Look here for a complete example.
I also ran into this problem, and the solution above didn't solve my problem:
making it a key was unacceptable in my case (i need the property to be changeable in the future)
using transactions on the email property doesn't work AFAIK (you can't do queries on non-key names inside transactions, so you can't check whether the e-mail already exists).
I ended up creating a separate model with no properties, and the unique property (email address) as the key name. In the main model, I store a reference to the email model (instead of storing the email as a string). Then, I can make 'change_email' a transaction that checks for uniqueness by looking up the email by key.
This is something that I've come across as well and I settled on a variation of #Remko's solution. My main issue with checking for an existing entity with the given email is a potential race condition like op stated. I added a separate model that uses an email address as the key and has a property that holds a token. By using get_or_insert, the returned entities token can be checked against the token passed in and if they match then the model was inserted.
import os
from google.appengine.ext import ndb
class UniqueEmail(ndb.Model):
token = ndb.StringProperty()
class User(ndb.Model):
email = ndb.KeyProperty(kind=UniqueEmail, required=True)
password = ndb.StringProperty(required=True)
def create_user(email, password):
token = os.urandom(24)
unique_email = UniqueEmail.get_or_insert(email,
token=token)
if token == unique_email.token:
# If the tokens match, that means a UniqueEmail entity
# was inserted by this process.
# Code to create User goes here.
# The tokens do not match, therefore the UniqueEmail entity
# was retrieved, so the email is already in use.
raise ValueError('That user already exists.')
I implemented a generic structure to control unique properties. This solution can be used for several kinds and properties. Besides, this solution is transparent for other developers, they use NDB methods put and delete as usual.
1) Kind UniqueCategory: a list of unique properties in order to group information. Example:
‘User.nickname’
2) Kind Unique: it contains the values of each unique property. The key is the own property value which you want to control of. I save the urlsafe of the main entity instead of the key or key.id() because is more practical and it doesn’t have problem with parent and it can be used for different kinds. Example:
parent: User.nickname
key: AVILLA
reference_urlsafe: ahdkZXZ-c3RhcnQtb3BlcmF0aW9uLWRldnINCxIEVXNlciIDMTIzDA (User key)
3) Kind User: for instance, I want to control unique values for email and nickname. I created a list called ‘uniqueness’ with the unique properties. I overwritten method put in transactional mode and I wrote the hook _post_delete_hook when one entity is deleted.
4) Exception ENotUniqueException: custom exception class raised when some value is duplicated.
5) Procedure check_uniqueness: check whether a value is duplicated.
6) Procedure delete_uniqueness: delete unique values when the main entity is deleted.
Any tips or improvement are welcome.
class UniqueCategory(ndb.Model):
# Key = [kind name].[property name]
class Unique(ndb.Model):
# Parent = UniqueCategory
# Key = property value
reference_urlsafe = ndb.StringProperty(required=True)
class ENotUniqueException(Exception):
def __init__(self, property_name):
super(ENotUniqueException, self).__init__('Property value {0} is duplicated'.format(property_name))
self. property_name = property_name
class User(ndb.Model):
# Key = Firebase UUID or automatically generated
firstName = ndb.StringProperty(required=True)
surname = ndb.StringProperty(required=True)
nickname = ndb.StringProperty(required=True)
email = ndb.StringProperty(required=True)
#ndb.transactional(xg=True)
def put(self):
result = super(User, self).put()
check_uniqueness (self)
return result
#classmethod
def _post_delete_hook(cls, key, future):
delete_uniqueness(key)
uniqueness = [nickname, email]
def check_uniqueness(entity):
def get_or_insert_unique_category(qualified_name):
unique_category_key = ndb.Key(UniqueCategory, qualified_name)
unique_category = unique_category_key.get()
if not unique_category:
unique_category = UniqueCategory(id=qualified_name)
unique_category.put()
return unique_category_key
def del_old_value(key, attribute_name, unique_category_key):
old_entity = key.get()
if old_entity:
old_value = getattr(old_entity, attribute_name)
if old_value != new_value:
unique_key = ndb.Key(Unique, old_value, parent=unique_category_key)
unique_key.delete()
# Main flow
for unique_attribute in entity.uniqueness:
attribute_name = unique_attribute._name
qualified_name = type(entity).__name__ + '.' + attribute_name
new_value = getattr(entity, attribute_name)
unique_category_key = get_or_insert_unique_category(qualified_name)
del_old_value(entity.key, attribute_name, unique_category_key)
unique = ndb.Key(Unique, new_value, parent=unique_category_key).get()
if unique is not None and unique.reference_urlsafe != entity.key.urlsafe():
raise ENotUniqueException(attribute_name)
else:
unique = Unique(parent=unique_category_key,
id=new_value,
reference_urlsafe=entity.key.urlsafe())
unique.put()
def delete_uniqueness(key):
list_of_keys = Unique.query(Unique.reference_urlsafe == key.urlsafe()).fetch(keys_only=True)
if list_of_keys:
ndb.delete_multi(list_of_keys)

what are the rules that apply on the key_name in app engine?

I'm trying to use an app engine User object's user_id (returned by the User.user_id() method) as a key_name in my own User class. The problem is that it keeps telling me that it's an invalid key_name. I've tried sha2'ing it, and using the digest() as well as the hexdigest() method to reduce the number of possible characters, but still no good result. Is this because the value is too long, or because key names can't have certain characters? And also, how can I modify a user_id in such a way that it stays unique, but is also usable as a key_name for an entity? Extra bonus if it uses a hash so that thje user_id can't be guessed.
Here is the code where the error occured:
def get_current_user():
return User.get(db.Key(hashlib.sha1(users.get_current_user().user_id()).hexdigest()))
I'm now doing some more testing, concidering suggestions from the comments and answer.
I'm not sure why it isn't working for you, the following has no issues when I run it in the dev console.
from google.appengine.ext import db
from google.appengine.api import users
user = users.get_current_user()
name = user.user_id()
print db.Key.from_path ('User', name)
However if you are hashing it (which it sounds like you may be), be aware that you may get a collision. I would avoid against using a hash and would consider some other means of anonymization if you are giving the key to clients. Such as another model whose key you can give away, that has the user's key stored in it. Another method would be to encrypt the id (using the same key for all users) rather than hash it.
If you are doing something that generates binary data (encryption / hash digest) app engine (the sdk at-least) has issues, so you need to encode it first, and use that as the key_name.
name = user.user_id()
hashed_name = hashlib.sha1(name).digest()
encoded_name = base64.b64encode (name)
db.Key.from_path ('User', encoded_name)

Resources