I'm going nuts trying to access the properties of a Datastore query. I could not find anything in the documentation (it could be me).
In the datastore I have the following:
Here's a snippet of my main.py:
import all the necessary stuff
...
datastore_client = datastore.Client()
u_name = 'batman' # example of the user I want to find
qn = datastore_client.query(kind='user').add_filter('user_id', '=', u_name).fetch()
So far so good, but how do I access the properties of this query?
What I haven't been able to find is how do I access the first_name of user 'batman'?
Something like:
name = qn.somefuntion('first_name')
Could someone tell me how to do this and also point me to the respective documentation?
Thanks!
Check out the python library reference for an Entity. The key part there is "you can treat an entity like a regular Python dictionary."
for entity in qn:
print(entity['first_name'])
Related
I have the User model in my datastore which contains some attributes:
I need to query all users filtering by the company attribute.
So, as I would normally do, I do this:
from webapp2_extras.appengine.auth.models import User
employees = User.query().filter(User.company == self.company_name).fetch()
This gives me:
AttributeError: type object 'User' has no attribute 'company'
And when I do:
employees = User.query().filter().fetch()
It gives me no error and shows the list with all the Users.
How do I query by field? Thanks
Your question is a bit misdirected. You ask how to query by field, which you are already doing with correct syntax. The problem, as Jeff O'Neill noted, is your User model does not have that company field, so your query-by-field attempt results in an error. (Here is some ndb documentation that you should definitely peruse and bookmark if you haven't already.) There are three ways to remedy your missing-field problem:
Subclass the User model, as Jeff shows in his answer. This is quick and simple, and may be the best solution for what you want.
Create your own User model, completely separate from the webapp2 one. This is probably overkill for what you want, just judging from your question, because you would have to write most of your own authentication code that the webapp2 user already handles for you.
Create a new model that contains extra user information, and has a key property containing the corresponding user's key. That would look like this:
class UserProfile(ndb.Expando):
user_key = ndb.KeyProperty(kind='User', required=True)
company = ndb.StringProperty()
# other possibilities: profile pic? address? etc.
with queries like this:
from models.user_profile import UserProfile
from webapp2_extras.appengine.auth.models import User
from google.appengine.ext import ndb
# get the employee keys
employee_keys = UserProfile.query(UserProfile.company == company_name).fetch(keys_only=True)
# get the actual user objects
employees = ndb.get_multi(employee_keys)
What this solution does is it separates your User model that you use for authentication (webapp2's User) from the model that holds extra user information (UserProfile). If you want to store profile pictures or other relatively large amounts of data for each user, you may find this solution works best for you.
Note: you can put your filter criteria in the .query() parentheses to simplify things (I find I rarely use the .filter() method):
# long way
employees = User.query().filter(User.company == self.company_name).fetch()
# shorter way
employees = User.query(User.company == self.company_name).fetch()
You've imported a User class defined by webapp2. This User class does not have an attribute called company so that is why you are getting the error from User.company.
You probably want to do create your own User model by subclassing the one provided by webapp2:
from webapp2_extras.appengine.auth.models import User as Webapp2_User
class User(Webapp2_User):
company = ndb.StringProperty()
Then your query should work.
One caveat, I've never used webapp2_extras.appengine.auth.models so I don't know what that is exactly.
I'm still trying to get my head around how the datastore works. I don't have previous experience with databases, so it's not a conflicting paradigm situation; I think I'm just confused about NDB's ancestor structure.
Let's say I have this model class:
class Spam(Model.ndb)
eggs = ndb.StringProperty();
So I create an instance and store it like this:
foo = Spam(eggs="some string")
foo.put()
I understand that put() returns a key that I could easily call get() on if I'm trying to access it from the same script, but is there a way to specify my own key, so I can easily access the foo entity from another script in my app?
I realize I can specify a parent for foo like this:
foo = Spam(parent=ndb.Key("Bar","Baz"),eggs="some string")
But from there, how would I use "Bar" and/or "Baz" to access foo in a different script?
Parents are used if you have a hierarchy. So if you have recipe books you would put the book as the parent and each recipe as a child. I don't think that's what you want.
If you want to set the key do this:
SuperEggs= Spam(id='SuperEggs', eggs="2 egg whites")
SuperEggs.put()
You can always let App engine set its own keys, this will prevent contention and accidental over rides, when you want access to the entity again simply do a get on some field. Add a name and search that.
FYI the returned id from the put lets you access from any part of your app (or any authorized app). The datastore is global not specific to a script.
There's some way to return items that field contains some value? Eg.
GET /people?contains="foo"
Return all persons that have the word 'foo' in the name.
Thanks in advance
You could use mongodb $regex operator, which is blacklisted by default in Eve (MONGO_QUERY_BLACKLIST = ['$where', '$regex']).
Add MONGO_QUERY_BLACKLIST = ['$where'] to your settings.py. Then you can query your API like this:
?where={"name": {"$regex": ".*foo.*"}}.
Be careful however. If you don't control the client, enabling regexes could potentially increase your API vulnerability.
I am not conversant with Eve myself. But looking at it's webpage seems like it exposes all of Flask's functionalities.
You need to be looking at this page in the documentation that talks about accessing the request data.
In your Flask app, define a method that accepts both POST and GET requests and then you can access foo by doing request.args.get('contains', '').
This is what I mean:
#app.route('/people', methods=['POST', 'GET'])
def get_people():
search_key = request.args.get('contains', '')
#search for people containing 'foo' in your DB
Hope this gives you a starting point as to how to go about things.
I am struggling with querying across multiple models.
This is what my class structure looks like:
class User(ndb.Model):
...
class LogVisit(ndb.Model)
user = ndb.KeyProperty(kind=User)
...
class LogOnline(ndb.Model)
logVisit = ndb.KeyProperty(kind = LogVisit)
...
and I want to get a list of the user's LogOnline's
what I want to do is this:
qry = LogOnline.query(LogOnline.logvisit.get().user.get() == user)
However app engine wont allow me to use the get method within a query.
Any thoughts on the best way to go about this?
Many thanks.
The most efficient way will be to store the user's key in the LogOnline entity. We can;t see the rest of your model to see what LogVisit adds to the whole excercise so difficult to see what LogVisit as an intermediate entity brings to the design.
Then just
LogOnline.query().filter(LogOnline.user == user)
You will have to stop thinking in terms of SQL if you want to have scalable applications on appengine. Think in terms of pure entity relationships and don't try to normalize the data model. Intermediate entities like LogVisit tend to only be used if you need many to many relationships but are still inefficient if you have more than a few instances of them for a particular relationship.
You are doing it wrong.
# user variable is assumed to be a key
logonlines = [] # You can use set also
logvisits = LogVisit.query().filter(LogVisit.user == user).fetch()
for logvisit in logvisits:
logOnlinesA = LogOnline.query().filter(LogOnline.logVisit == logvisit.key).fetch()
logonlines.extend(logOnlinesA)
Give it a try :
logvisits = LogVisit.query().filter(LogVisit.user == user).fetch(keys_only=True)
logOnlinesA = LogOnline.query().filter(LogOnline.logVisit.in(logvisits)).fetch()
In an App Engine app, I store registered members in a table that looks like this:
class Member(db.Model):
user = db.UserProperty(required=True)
#other stuff
The problem starts when I need to check if a User is already in my Member table. GAE documentation says user value is not guaranteed not to change in time since it is composed by the user object+email. So it will change if the user changes the e-mail on that account.
I am using OpenID. So I though about using User.federated_identity() as a stable identifier.
However to check for this I'd have to do a Query like this:
u = users.get_current_user()
rm = Member.all().filter('user_federated_identity =',u.federated_identity()).get()
This is a valid query in Django, but apparenty not in GAE. What can I do here, other that loading all my members to memory and checking their federated_identity?
You should be able to do this:
u = users.get_current_user()
rm = Member.all().filter('user =', u).get()
Maybe you can identify your user by a unique key_name:
key_name = "member/%s" % users.get_current_user ().user_id
user_ref = Member.get_or_insert (key_name)
GAE User API explicitly mentions user_id() as a permanent identifier that persists across e-mail changes. You can store it in separate field in model.
Note that it is only supported for Google Accounts.