Foreign Key in NDB/Google App Engine - python

I am building a web app with Google Endpoints and the Datastore using NDB in Google App Engine. Each user that signs in has a set of "watched listings" that is just a repeated property in the expando model of NDB like this:
class user(ndb.Expando):
username = ndb.StringProperty()
email = ndb.StringProperty()
password = ndb.StringProperty()
overallRanking = ndb.IntegerProperty()
numRankings = ndb.IntegerProperty()
watchedListings = ndb.KeyProperty(modelListing,repeated=True)
As you can see, I am storing the key of modelListing entities. However, when I delete a modelListing, I want it to automatically delete the key for all of the users that have that key in their watchedListings object. This is similar to how SQL would handle a foreign key. Is there a way to do this without going through all the users in the database and searching for that key, deleting it, and executing a put()?

I did to try this feature yet. It looks like you can do the job using:
https://developers.google.com/appengine/docs/python/ndb/entities?hl=nl#hooks

Related

flask app builder user should see only own content

Hi i want that the user is only seeing it's own content which was created by themself. In flask i created a user table and every other table as a reference to the table. So when the view is called i filter for the current user and only show the entries for the user. I now checked out flask app builder and it has some nice user management but it seems that it has nothing like i need.
My solution would be: Create a reference from my table to the user table and do it like i did it with plain flask. I am just wondering if there is a better way to do this and maybe there is allready something in appbuilder what i have to activate but don't see yet.
my flask solution:
this is what i add to the model
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
this is how i query it in the routes
articles_pos = ArticlePos.query.filter_by(user_id=current_user.id)
thanks in advance
A solution can be found in the examples of fab itself. see https://github.com/dpgaspar/Flask-AppBuilder/tree/master/examples/extendsecurity
in my particular case i made the following changes
in the model i adde the follwoing:
class MyUser(User):
__tablename__ = "ab_user"
and in the class where i reference the user table
user = relationship("MyUser")
user_id = Column(Integer, ForeignKey('ab_user.id'), default=get_user_id, nullable=False)
you still need the the function:
#classmethod
def get_user_id(cls):
try:
return g.user.id
except Exception:
return None

How to store a value as a global variable that is specific to the user?

I am currently working on a small flask app that will be connecting to an api and processing data pulled from that api.
Users are able to login to my flask app and then also define their credentials to interact with the api. Any given user may have one or more API credentials associated with their user.
I've created db models to store user and Api credentials in the database as follows.
I'm using the flask-login module which has the "current_user" object which provides me with the User model of the user that is currently logged in across my entire app.
Models:
class User(UserMixin, db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), index=True, unique=True)
email = db.Column(db.String(128), index=True, unique=True)
firstname = db.Column(db.String(55))
lastname = db.Column(db.String(55))
password = db.Column(db.String(128))
creds = db.relationship('ApiCredential', backref='owner', lazy='dynamic')
class ApiCredential(db.Model):
__tablename__ = 'api_credentials'
id = db.Column(db.Integer, primary_key=True)
site = db.Column(db.String(140))
user = db.Column(db.String(140))
username = db.Column(db.String(100), index=True, unique=True)
password = db.Column(db.String(55))
base = db.Column(db.String(100))
owner_id = db.Column(db.Integer, db.ForeignKey('users.id'))
active = db.Column(db.Boolean)
I would like to know how to create a similar "global variable" for my API credentials that is specific only to the logged in user and not to all users of the application
NOTE*** It seems as though "current_user" is something called a local proxy which i am not at all familiar with and cannot seem to find any decent documentation or explanation of what it is and how to use it for my needs.
You're in for a fun ride, at the end of which you might choose to do something less magic.
First, it helps to understand how current_user works. The source (as of this moment) is here. It's a werkzeug.local.LocalProxy, which wraps a lambda that calls flask_login.utils._get_user.
LocalProxy is pretty cool, and understanding it is a great way to level-up on Python, but flask-login uses a thin slice of it. The source (as of this moment) is here. It looks scary, but if you trace the code, it merely invokes the local arg (the lambda from flask-login).
That gets us back to _get_user (here, as of this moment), which loads a user if there isn't one (in the top of the current request context), and then returns the user from the top of the request context.
You could follow that pattern, and write a package that exports current_credentials. You'd follow the same pattern, using a werkzeug.local.LocalProxy to wrap a lambda that invoked, say, _get_credentials. The trick would be to import current_user from flask-login, using it with _get_credentials to get to user with which to construct the query to join to your ApiCredentials table.
Or you could take a simple approach, and write utility method for your views to use, which would use current_user to get the user and then do the join to get API credentials for that user.
One method could be to create a new route in your flask app, when the user get requests the page then you can check which user it is, once you know which user it is, you can query using your api credentials model and filter by current_user.id.
creds = ApiCredentials.query.filter_by(owner_id=current_user.id).first_or_404()
Then you can do as you please with the information stored in your API Credentials table.
I don't see why you would want to replicate the user loader functionality. This is the function you will have included at some point when you set up Flask login. Very short function that returns the current user object from the user model.
You could display or return your api keys. Display them on a page as HTML or for more autonomous jobs, you could use jsonify to return the keys as a json string.
I'm sorry this doesn't directly answer your question, but I hope my suggestion might lead you to a slightly less complex answer and you can continue developing your web app. Perhaps it would be worth revisiting at a later date.
Note: this is off the top of my head. The code line I provided might need to be as follows
creds = ApiCredentials.query.filter_by(owner_id==current_user.id).first()
Furthermore, you may not want to use .first() if they have multiple api credentials stored in the table.
In which case this would be more suitable:
creds = ApiCredentials.query.filter_by(owner_id==current_user.id).all()

ndb Singleton Pattern

I'm doing an online store in appengine, and I'm creating a model that will hold the settings of the store in the db, the code looks something like this:
class StoreSettings(ndb.Model):
name = ndb.StringProperty()
homepageTitle = ndb.StringProperty()
metaKeywords = ndb.StringProperty()
metaDescription = ndb.StringProperty()
timezone = ndb.IntegerProperty()
currency = ndb.StringProperty()
Is there an easy way to make the StoreSettings class to be a singleton?
Thanks
When you initialize your settings you can provide a key_name, then when you have to retrieve it you can use get_or_insert method. If it doesn't exist it will create it otherwise it will retrieve it.
settings_db = StoreSettings.get_or_insert(
'my_settings',
name='yourname'
....
)
Or if you create the object when your application starts then you can just get it by the key name
settigns_db = StoreSettings.get_by_id('my_settings')
Keep the same key? whenever you push an entity in the datastore, it needs a key.
If you create a second object with the same key, it ends up over-writing the previous entity.

How to save an embedded object in GAE?

I'm trying to save a datastore entity reference within another:
class Save(webapp2.RequestHandler):
def get(self):
order = Order(parent=ndb.Key('Orders', 'default_orders'))
order.special_request = self.request.get('specialRequirement')
order.product_type = self.request.get('productType')
customer = Customer(parent=ndb.Key('Customer', 'default_customers'))
customer.name = self.request.get('customerName')
customer.email = self.request.get('email')
customer.put()
order.customer = customer
order.put()
The Customer class is simply:
from google.appengine.ext import ndb
class Customer(ndb.Model):
name = ndb.StringProperty()
email = ndb.StringProperty()
Whilst I've done similar with Rails and mongodb before, I'm not sure what this is called in GAE and am having a hard time searching for examples.
Ok, the following seems to have been my oversight, simply passing the key as:
oder.customer = customer.key
I now have a usable reference to the embedded object and both are being saved correctly.

Google App Engine (Python) : use UserProperty with Webapp2 User Model

i have an application where we allow users to use Oauth2 for authentication and even Custom User Registrations. All the Users are saved into the default User entity in the datastore. If the user is logging in using Oauth2 for the first time a new record in the default User entity is created like this:
"""Check if user is already logged in"""
if self.logged_in:
logging.info('User Already Logged In. Updating User Login Information')
u = self.current_user
u.auth_ids.append(auth_id)
u.populate(**self._to_user_model_attrs(data, self.USER_ATTRS[provider]))
u.put()
else:
"""Create a New User"""
logging.info('Creating a New User')
ok, user = self.auth.store.user_model.create_user(auth_id, **self._to_user_model_attrs(data, self.USER_ATTRS[provider]))
if ok:
self.auth.set_session(
self.auth.store.user_to_dict(user)
)
self.redirect(continue_url)
for custom registrations records are inserted through the following handler.
class RegistrationHandler(TemplateHandler, SimpleAuthHandler):
def get(self):
self.render('register.html')
def post(self):
"""Process registration form."""
user = 'appname:%s' % self.request.get('email')
name = '%s %s' % (self.request.get('first_name'), self.request.get('last_name'))
password = self.request.get('password')
avatar = self.request.get('avatar')
act_url = user_activation.Activate(self.request.get('first_name'), self.request.get('email'))
ok, user = User.create_user(auth_id=user, name=name, password_raw=password, email=self.request.get('email'))
if ok:
self.auth.set_session(self.auth.store.user_to_dict(user))
acc = models.Account(display_name=self.request.get('first_name'), act_url=act_url, act_key=act_url.split('activate/')[1], user=users.User(User.get_by_auth_id(self.current_user.auth_ids[0]).email))
acc.put()
if avatar:
avt = models.Picture(is_avatar=True, is_approved=True, image=avatar, user=users.User(User.get_by_auth_id(self.current_user.auth_ids[0]).email))
avt.put()
self.redirect('/')
Now we are using webapp2_extras.sessions for session handling. We have different models like, Comments, Images, Reviews etc in which we want to use db.UserProperty() as the author field. However, the author field shows blank or None whenever we enter a record into any of these models using 'users.get_current_user()'. I think this is because we are handling the sessions through webapp2 sessions.
What we want to achieve is to be able to use the db.UserProperty field in various models and link appropriately to the current user using webapp2 sessions ?
the UserProperty() has to be passed with a User Object in order for it to properly insert the records. Even though we are able to enter the records using the following code :
user = users.User(User.get_by_auth_id(self.current_user.auth_ids[0]).email)
or
user = users.User(User.get_by_auth_id(self.current_user.auth_ids[0]).name)
but then we are not able to get the whole user object by referencing to model.author
Any ideas how we should achieve this ?
OAuth 2.0 is not currently supported by Users service. Supported options are
Google Accounts
OpenId
OAuth 1.0
I don't frankly understand what you're trying to accomplish with introducing db.User in to the codebase. Given there's self.current_user, I assume you're already handling authentication process.
When you do self.auth.store.user_model.create_user - that already gives you a webapp2's user object/entity (it has nothing to do with db.User though). I believe that's what you'll have to use as your author field given OAuth 2.0 constraint.
users.get_current_user() relies on a special cookie (App Engine internal). In fact, it has nothing to do with webapp2's session (or any other "custom" session for that matter). You could hack it by setting the cookie to a value that App Engine internals can understand and be tricked as if a user were logged in with one of the methods I mentioned, but I wouldn't recommend this approach. It is not documented (cookie name, format, etc.) and might be changed at any time.
Instead of using UserProperty to store references to the webapp2 user objects, you should instead store the auth_id as a StringProperty and add a convenience method for fetching the corresponding webapp2 user entity.
Something like this
from webapp2_extras.appengine.auth.models import User
class Comment(db.model):
text = db.StringProperty()
author = db.StringProperty()
def get_author(self):
return User.get_by_auth_id(self.author)

Categories

Resources