I have read, read, and read again the documentation and do many search on the Web but I don't understand yet why my app doesn't work correctly.
When a user connects, he have to complete a form. The code for this works. When the user logs out, and then, logs in, the form fields are filled with his information. So, the data is correctly saved. But, when the user changes the form fields values and submits the form, data are not updated.
My model:
class Members(db.Model):
account = db.UserProperty()
hashtags = db.StringProperty()
Here the class to submit the form:
class Submit(webapp.RequestHandler):
def post(self):
user = users.get_current_user()
if user:
url = users.create_logout_url('/')
url_linktext = 'Logout'
member = db.GqlQuery("SELECT * FROM Members WHERE account = :1", user)
if member.count(1) > 0:
m = Members(account=user)
m.hashtags = ','.join([
self.request.get('hashtag1'),
self.request.get('hashtag2'),
self.request.get('hashtag3')])
m.put()
else:
member = Members()
member.account = user
member.hashtags = ','.join([
self.request.get('hashtag1'),
self.request.get('hashtag2'),
self.request.get('hashtag3')])
member.put()
self.redirect('/')
else:
self.redirect('/')
The problem is you are adding a new record instead of updating the existing record. For your code, the simplest fix would be to change this:
if member.count(1) > 0:
m = Members(account=user)
to:
if member.count(1) > 0:
m = member[0]
The reason your code is updating the existing record is because you have not assigned a key. To learn more about keys, you can read about them in Kinds and Identifiers.
Related
I have following code in models.py i can sort database by only key but not by string ?
from google.appengine.ext import ndb
class Roles(ndb.Model):
name = ndb.StringProperty()
owner = ndb.KeyProperty(kind='User')
created = ndb.DateTimeProperty(required=True, auto_now_add = True)
class RESTMeta:
user_owner_property = 'owner'
include_output_properties = ['name']
class Users(ndb.Model):
name = ndb.StringProperty()
email = ndb.StringProperty()
password = ndb.StringProperty()
roles = ndb.KeyProperty(kind='Roles')
owner = ndb.KeyProperty(kind='User')
created = ndb.DateTimeProperty(required=True, auto_now_add = True)
class RESTMeta:
user_owner_property = 'owner'
include_output_properties = ['name']
And the following in api.py
app = webapp2.WSGIApplication([
RESTHandler(
'/api/roles', # The base URL for this model's endpoints
models.Roles, # The model to wrap
permissions={
'GET': PERMISSION_ANYONE,
'POST': PERMISSION_ANYONE,
'PUT': PERMISSION_OWNER_USER,
'DELETE': PERMISSION_ADMIN
},
# Will be called for every PUT, right before the model is saved (also supports callbacks for GET/POST/DELETE)
put_callback=lambda model, data: model
),
RESTHandler(
'/api/users', # The base URL for this model's endpoints
models.Users, # The model to wrap
permissions={
'GET': PERMISSION_ANYONE,
'POST': PERMISSION_ANYONE,
'PUT': PERMISSION_OWNER_USER,
'DELETE': PERMISSION_ADMIN
},
# Will be called for every PUT, right before the model is saved (also supports callbacks for GET/POST/DELETE)
put_callback=lambda model, data: model
)],debug=True, config = config)
I can successfully get by key in api\users?q=roles=key('key')
How do i get specific user by String api\users?q=email=String('String')
The Question is how do I do user auth for google appengine app
You seem to be asking so many questions in one.
To get user by email, simply do this:
users = Users.query(Users.email=='query_email').fetch(1)
#note fetch() always returns a list
if users:
user_exists = True
else:
user_exists = False
Please note, you may need to update your datastore index to support that query. The easiest way to do it is to first run the code in your local development server, and the index will be automatically updated for you.
To answer your second questions, for user authentication I would recommend Django's in-built User Authentication. Please note that you can always run vanilla django on appengine with a Flexible VM using CloudSQL instead of the Datastore.
Alternatively, you could use the Appengine Users API, though your users would need to have Google Accounts.
I am registering the data in user model and also want to save the profile data same time like first_name and last_name in profile model.
So I have used django signals to store the profile information and send the mail to user.
But we are unable to get the first_name and last_name in signal file:
#---------------------------- Create profile at the time of registration --------------------------#
def register_profile(sender, **kwargs):
if kwargs.get('created'):
user = kwargs.get('instance')
request = kwargs.get("request")
if user.id is not None and user._disable_signals is not True:
profile = Profile(user=user)
if user.status is not 1:
#------------------- Send the registration mail to user and it have confirmation link ----------#
salt = hashlib.sha1(str(random.random())).hexdigest()[:5]
activation_key = hashlib.sha1(salt+user.email).hexdigest()
key_expires = datetime.datetime.today() + datetime.timedelta(2)
#print user
profile.activation_key = activation_key
profile.key_expires = key_expires
#--------------------- End -------------------------------------------------------------#
profile.save()
if user.status is not 1:
user = model_to_dict(user)
BaseSendMail.delay(user,type='account_confirmation',key = activation_key)
return
post_save.connect(register_profile, sender=User, dispatch_uid='register_profile')
#-------------------------- End ---------------------------------------------------------------------#
In above code I am unable to get first_name and last_name data which is sent at the time of registration.Also I would like to mention that first_name and last_name fields belong to profile model.
No, and you shouldn't try. Signals could be executed from anywhere: a management script, a Celery task, all sorts of places that might not have a request.
You could store the data on the User instance temporarily, as you do with the _disable_signals attribute. However I suspect that this is not really best done in a signal; since you're saving the result of a form submission, and it depends on the data in that form, you should really do that in the view or the form itself.
I did this and it worked.
Not sure about it's impact on performance etc.
some_file.py:
data = {}
middleware.py:
class MyMiddleware(object):
def process_request(self):
from path.to.some_file import data
data['request'] = self.request
signals / model_method / manager / template tag / any where else:
from path.to.some_file import data
request = data.get('request')
I'm currently using Python / App Engine / SimpleAuth to provide OAuth login to my application. The current workflow is that users login with OAuth, and they can later create a unique username for themselves in the app.
I'm having problems creating the unique username after the webapp2 User entity has already been created. I see that in the webapp2 model there is a way to enable a unique username within the application Entity group, but I don't know how to set it for myself. (I'm using SimpleAuth to set everything for other OAuth providers.)
I want to check to see if the user-submitted 'username' exists, and if it doesn't to add it as a property to the currently logged-in user. I'd appreciate any help/pointers on this!
I think you could extend webapp2_extras.appengine.auth.models.User and add username property, e.g.
from webapp2_extras.appengine.auth.models import User as Webapp2User
class User(Webapp2User):
username = ndb.StringProperty(required=True)
Then, to create a webapp2 app you'd need a config which includes this:
APP_CFG = {
'webapp2_extras.auth': {
'user_model': User, # default is webapp2_extras.appengine.auth.models.User
'user_attributes': ['username'] # list of User model properties
}
}
app = webapp2.WSGIApplication(config=APP_CFG)
Havign the above, creating a new user using the following code will ensure username is unique (ensured by Unique model):
auth_id = 'some-auth-id' # e.g. 'google:123456789', see simpleauth example.
ok, props = User.create_user(auth_id, unique_properties=['username'],
username='some-username',
...)
if not ok:
# props list will contain 'username', indicating that
# another entity with the same username already exists
...
Problem is, with this configuration you are bound to set username during the creation time.
If you wanted to make username optional, or let users set/change it later on, you would probably want to change the above code to something like this:
class User(Webapp2User):
username = ndb.StringProperty() # note, there's no required=True
# when creating a new user:
auth_id = 'some-auth-id' # e.g. 'google:123456789', see simpleauth example.
ok, props = User.create_user(auth_id, unique_properties=[], ...)
Basically, unique_properties will be empty list (or you can just skip it). Also, you could temporarily assign username property to something like user.key.id() until the user decides to change their username to something more meaningful. Take, for instance, Google+ profile links: mine is currently https://plus.google.com/114517983826182834234, but if they let me change it, I would try something like https://plus.google.com/+IamNotANumberAnymore
Then, in a "change/set username" form handler, you could check if a username already exists and update User entity (if it doesn't):
def handle_change_username(self):
user = ... # get the user who wants to change their username
username = self.request.get('username')
uniq = 'User.username:%s' % username
ok = User.unique_model.create(uniq)
if ok:
user.username = username
user.put()
else:
# notify them that this username
# is already taken
...
User.unique_model.create(uniq) will create a Unique entity with the given value if it didn't exist. In this case ok will be True. Otherwise, ok will be False which indicates that an entity with that value (a unique username in this case) already exists.
Also, you might want to put User.unique_model.create() and user.put() in the same transaction (it'll be XG because they are in different entity groups).
Hope this helps!
I'm new to Python and Flask but I'm gradually getting to grips with it. I've got so far into building an app and I'm now thinking I should start some unit testing. I really can't get my head around it though. I've read various docs, posts and examples but I can't seem to transfer this to my own code. I'm hoping if someone can show me how to write a test for one of my functions then things will fall into place. It's very tempting at this stage to ignore it and press on building my app.
#app.route('/user/<nickname>/create_bike', methods = ['GET', 'POST'] )
#login_required
def create_bike(nickname):
user = User.query.filter_by(nickname = nickname).first()
bikes = user.bikes.all()
bikecount = user.bikes.count()
form = CreateBike()
if form.validate_on_submit():
if bikecount < user.bike_allowance:
# let user create a bike
newbike = Bikes(bikename = form.bikename.data,
user_id = g.user.id, shared = SHARED )
db.session.add(newbike)
db.session.commit()
flash('Your new bike has been created')
return redirect(url_for('create_bike', nickname = nickname))
else:
flash("You have too many bikes")
return render_template('create_bike.html',
user = user,
form = form
)
UPDATE - Here's my working test
def test_create_bike(self):
u = User(nickname = 'john', email = 'john#example.com', account_type = "tester")
db.session.add(u)
db.session.commit()
# login user
with self.app as c:
with c.session_transaction() as sess:
sess['user_id'] = int(u.get_id())
# http://pythonhosted.org/Flask-Login/#fresh-logins
sess['_fresh'] = True
rv = c.post('/user/john/create_bike', data = dict(
bikename = 'your new bike',
user_id = sess['user_id'],
shared = 1
), follow_redirects = True)
assert 'Your new bike has been created' in rv.data
Your test is likely failing because that view requires a logged in user, and since you arent passing in any session data, you are being redirected to the login page (the data argument to .post is form data, available in request.form in your view). Before your assertion you can see what the response is to help you along the way:
print rv.status_code
print rv.location #None if not a redirect
There's some documentation around sessions in tests here, and if you're using Flask-Login like it looks you are, this answer shows you how to set the session up so you get a logged in user
You're on the right track.
Checking if the html page contains some piece of data that should be there is one the most common scenarios in testing web apps. You just need to repeat for all the other pages that you create. You can also add tests to see that creating a user follows all the validation rules that you've setup or that the username field is unique etc.
I asked a similar question here: create permenant unique links based on a user ID but couldn't quite get an answer. I am trying to give every user on my site a unique profile page. I have it mostly set up but I keep getting a 404 error. Now I am not sure if it is a problem with my handler or just the whole way I am doing it.
Here is my app code:
app = webapp2.WSGIApplication([('/', MainPage),
(r'/profile/(.+)', ProfilePage)])
and here is my ProfilePage handler class:
class ProfilePage(webapp2.RequestHandler):
def get(self, profile_id):
profileowner = User.get_by_id(profile_id)
if profileowner:
#Get all posts for that user and render....
#theid is their federated_id
theid = profileowner.theid
personalposts = db.GqlQuery("select * from Post where theid =:1 order by created desc limit 30", theid)
#I collect this so that I can have their username in the top of the page
global visits
logout = users.create_logout_url(self.request.uri)
currentuser = users.get_current_user()
self.render('profile.html', user = currentuser, visits = visits, logout=logout, personalposts=personalposts)
else:
self.redirect("/")
For an ID of 1201, which I found in the datastore viewer for a use, I have been testing it by typing in www.url.com/profile/1201 and that is when I get the 404 error.
Update:
It now is redirecting me to the main page with Amber's suggested change.
Now when I change this line:
profileowner = User.get_by_id(profile_id)
to this:
profileowner = User.get_by_id(17001)
it goes through correctly so I am guessing that that line is not correctly getting the profile_id from the URL
r'/profile/<profile_id>'
is not a valid regular expression. You probably want something like this instead:
r'/profile/(.+)'