Search code examples
python-3.xgoogle-cloud-datastoregoogle-app-engine-python

Is there a way to properly match a user to a project using cloud-ndb


I keep getting an error when i try to pass a user to a project model as its created. I am able to access the user object when one is logged in via params["user"] and i get an error message

What i am trying to do is just the same as the app engine ndb and get to match the user to the record they created such that when they login they are able to view only their records. So am trying to get someone to login and based on their login we can use their information to save a project they create.

The following is my piece of code to the projects model and api

from flask import jsonify, make_response
import re
from datetime import datetime

from google.cloud import ndb
from google.cloud import storage
#from dateutil import tz
from models.v1.user import User
from models import get_db
class Project(ndb.Model):
    user = ndb.KeyProperty(kind=User)

    funds_raised = ndb.FloatProperty(default=0.0)
    live_date = ndb.DateTimeProperty()
    finish_date = ndb.DateTimeProperty()

    created_on = ndb.DateTimeProperty(auto_now_add=True)
    project_name = ndb.StringProperty(default='New Project')
    project_image = ndb.BlobKeyProperty()
    project_image_url = ndb.StringProperty(default='')
    project_created_by = ndb.StringProperty(default='')
    project_category = ndb.StringProperty(default='')
    project_location = ndb.StringProperty(default='')
    project_link = ndb.StringProperty(default='')
    project_duration = ndb.StringProperty(default='')

    @property
    def project_url_name(self):
        if not self.project_url_name_unique_key:
            return None

        return self.project_url_name_unique_key.id().rsplit(':', 1)[1]

    # class methods (ordered by alphabet)
    @classmethod
    def create(cls, primary_contact_first_name, primary_contact_surname, primary_contact_email, project_name, project_status):
        with client.context():
            project = cls( primary_contact_first_name=primary_contact_first_name, primary_contact_surname=primary_contact_surname, primary_contact_email=primary_contact_email, 
                            project_name=project_name, project_status=project_status)
            project.put()

            if project:

                return True, project, "Success"  # succes, project, message
            else:
                return False, project, "Project not created"

The code to the api is as follows.

@login_required
def create_project(**params):

    if request.method == "POST":
        user = params["user"]
        details = user.to_dict()

        success, project, message = Project.create(
            user = ndb.Key('User', user.get_id)
            primary_contact_first_name=details.get('first_name'),
            primary_contact_surname=details.get('last_name'),
            primary_contact_email=details.get('email_address'),
            project_name='New Project',
            project_status=0
        )

        if success:
            return make_response(jsonify({"Message" : message, "Project" : project.to_dict() }))
        else:
            return abort(403, description=message)

This is the error am getting within my code now

File "/home/lenny/Desktop/flask_gae/utils/decorators.py", line 64, in wrapper
    return func(**params)
  File "/home/lenny/Desktop/flask_gae/handlers/campaigns/main.py", line 33, in create_project
    user=ndb.Key('User', user.key),
  File "/home/lenny/Desktop/flask_gae/env/lib/python3.7/site-packages/google/cloud/ndb/key.py", line 285, in __new__
    client = context_module.get_context().client
  File "/home/lenny/Desktop/flask_gae/env/lib/python3.7/site-packages/google/cloud/ndb/context.py", line 71, in get_context
    raise exceptions.ContextError()

Solution

  • I think the problem may be in create_project(). The traceback shows:

    user=ndb.Key('User', user.key),
    

    This wouldn't be the right way to build a key, you need to pass a key ID to the Key() contructor, not a key:

    user=ndb.Key('User', <user_key_id>),
    

    The updated code snippet shows an attempt in that direction.

    But unless I misread your code user is obtained from the request parameters at that moment:

    user = params["user"]
    

    Which would mean that user may not be an ndb entity (I don't think you can directly pass ndb entities as request parameters). Try displaying it in a log msg to check that. You may need to either:

    • construct the user key as shown (if you know its ID)
    • construct the user key from a urlsafe string which you can pass via request parameters, see Key vs ID/Name? (that post is about the python2 ndb client, but I think the cloud ndb has something similar)
    • obtain/create the actual user ndb entity and get its key:

      user=user.key.id(),