I'm passing a model entity's id in a url in on handler, and in another I simply need to look up the property value from that id.
class Pdf(db.Model):
name = db.StringProperty(required=True)
class InputHandler(Handler):
def get(self):
self.render('cert_form.html')
def post(self):
name = self.request.get('name')
if name:
a = Pdf(name=name)
a.put()
self.redirect('/key=%s' % a.key())
else:
error = "Please enter your full name."
self.render('cert_form.html')
class PDFHandler(Handler):
def get(self, id):
a = db.Key.from_path('Pdf', id)
self.response.write(a.name())
application = webapp2.WSGIApplication([
(r'/', InputHandler),
(r'/key=(.)+', PDFHandler),
], debug=True)
self.redirect('%s' % str(a.key().id())) (besides the total redundancy of %s and str -- lose that str call!-) redirects to a URL that's a pretty arbitrary string. How are you routing that, presumably to PDFHandler, so it can get its id?
A more sensible and common approach would be to redirect to, say,
'/pdfh?id=%s' % urllib2.urlencode(whatever)
or probably better
'/pdfh?k=%s' % a.key().urlsafe()
and use self.request.get in the handler to get the id (or probably better the url-safe form of the key, ready for a ndb.Key(urlsafe=... call to give you the actual key to use).
These approaches make routing obvious -- with your very peculiar approach, I need to see the routing to explain exactly why it's not working (the chance that such weird routing would work are not zero, but, very minimal:-). BTW, I'll need to see both the first layer of routing in your app.yaml, and the second layer in your Python code.
Or, you could just switch to a more sensible URL scheme, and be done in 23 seconds:-).
Related
I am working on a google app engine (gae) project in python which has the following structure:
class LoginHandler(webapp2.RequestHandler):
def get(self):
...#check User-> DB access
def post():
...#check User-> DB access
class SignupHandler(webapp2.RequestHandler):
def get(self):
...#check User-> DB access
def post():
...#check User-> DB access
class Site1Handler(webapp2.RequestHandler):
def get(self):
...#check User-> DB access
def post():
...#check User-> DB access
class Site2Handler(webapp2.RequestHandler):
def get(self):
...#check User-> DB access
def post():
...#check User-> DB access
class ...
application = webapp2.WSGIApplication([('/login', LoginHandler),
('/signup',SignupHandler),
('/site1', Site1Handler),
('/site2', Site2Handler),
...,
],
debug=True)
Every user who wants to use this application has to be logged in.
Therefore on the login-site and the signup-site a cookie value with an user_id is set.
So lets imagine this app has 100 URLs and the corresponding 100 Site...Handlers() implemented.
Than for every get()/post() call I first get the user_id from the cookie and check in the database if this user exists and if it is valid.
So if the user clicks on 20 sites the app accesses 20 times the db to validate the user.
I am sure there is a better way and I would be glad if someone could show me how to do this.
I have already seen someone inherited his own Handler from webapp2.RequestHandler
which would than look like:
class MyHandler(webapp2.RequestHandler):
def initialize(self, *a, **kw):
webapp2.RequestHandler.initialize(self, *a, **kw)
uid = self.request.cookies.get('user_id')
self.user = uid and User.all().filter('userid =', uid).get()
class LoginHandler(MyHandler):
def get(self):
...#if self.user is valid -> OK
def post():
...#if self.user is valid -> OK
...
And here it is getting confusing for me.
Consider two or more people accessing the application concurrently. Will then User1 see data of User2 because self.user is initialized with data from User2?
I also concidered using a global variable to save the current user. But here the same problem if two users access the app concurrent.
I also found the webapp2.registry functionality which seemed to me the same like a global dictionary. And here also the problem of two or more users accessing the app at the same time.
Could someone please show me how to do it right? I am very new to gae and very happy for every hint in the right direction.
(Maybe Memcached is the solution. But I am more interested in a review of this check if user is valid pattern. So what would be best practice to do this?)
Assuming that you are using NDB and validating your user by getting a User object via a key/id - it will be automatically cached in memcache as well as in current local instance's memory, so your route handlers won't be calling Datastore with every single request, this is all done automatically, no extra coding required. If for validation / getting the user object you are using a query - the result won't be automatically cached but you can always manually cache it and verify the user via cache first and if the cache doesn't exist only then query Datastore, caching the results for the next request.
See more here.
If you are using webapp2's Sessions with signed/secure cookies then the data in those cookies, including the fact that the user is validated (which you previously set when when validating the user the first time) can be trusted, as long as you use long and randomly generated secret_key, that is kept secret and thus, just like with cache, you first check whether the user is validated in the cookie and if not, you ask Datastore and save the result in the session cookie for the next request. See more here.
Either way, you don't have to repeat your validation code in every single handler like you are showing in your example. One way of fixing it would be using decorators which would make your validation reuse as simple as placing #login_required before your get method. See more info here and take a look at the webapp2_extras.appengine.users file to get an idea how to write your own, simmilar decorator.
class MainHandler(BaseHandler.Handler):
def get(self):
user = users.get_current_user() or "unknown"
posts = memcache.get('posts_%s' % user.user_id())
if not posts:
q = db.Query(P.Post)
q.filter('author =', users.get_current_user()).order("-date")
posts=q.fetch(5)
memcache.set(key='posts_%s:'%user.user_id(),value=posts)
#q=P.Post.all().filter('user =',users.get_current_user())
self.render('index.html', user=user, posts=posts)
def post(self):
user = users.get_current_user() or "unknown"
author = users.get_current_user()
title = self.request.get('title')
content = self.request.get('content')
p = P.Post(author=author, title=title, content=content)
p.put()
res = memcache.get('posts_%s'%users.get_current_user().user_id())
if res:
res+=p
if len(res)>5:
res=res[1:]
else:
res=[p]
memcache.replace("posts_%s"%user.user_id(),value=res)
self.redirect('/')
When the browser redirects to '/' the last added item isn't in the list(it is added only after reloading). This happens only when I am on development server(on GAE it works OK),and I wonder if it can happen on GAE and what's the problem with this code
Any suggestions would be highly appreciated.
UPD:thx,I made keys the same,but the problem still remains
You're not hitting memcache at all here. You're using a different key format in the post and get methods: in get you use "posts_user" whereas in post you use "user:posts", so the key is never found and you fall through to the db query. And, of course, the query is not up to date because of eventual consistency, which is presumably the whole reason you're using memcache in the first place.
Fix your memcache keys and this should work.
maybe the item is not in memcache when you do replace. Why do you use replace in this case? Any reason not to use memcache.set? In the get function, there is still one place where the key is posts_%s: which is different than the others.
I'm using Django, and want to store data that is relevant only for the duration of a request, and not on the session.
Is it correct to add something to request.META, like:
request.META['acl'] = acl
In my situation, I am using Tastypie, with a custom authorization class, and need a way to pass data between functions... it seems like storing something on the request would be the right thing to do... I just don't know where to store such information. My class looks something like:
class MyAuthorization(Authorization):
def is_authorized(self, request, object=None):
acl = getMyAccessControlList(request.method,request.session['username'])
for permission in acl:
if permission in self.permissions[request.method]:
request.META['acl'] = acl
return True
return False
def apply_limits(self, request, object_class, rs):
if 'HAS_ALL_ACCESS' in request.META['acl']:
return rs
else if 'HAS_USER_ACCESS' in request.META['acl']:
rs = rs.filter(object_class.user==request.session['username'])
return rs
Futher, Tastypie creates a single REST resource object, with a single authorization class used by all threads, so it's not thread-safe to just put it on the authorization class.
UPDATE
As per Chris Pratt's feedback, no, it doesn't make sense to modify the request. Exploring further, it appears to be appropriate to modify the request initially through custom middleware, and then keep it constant for the rest of the request: https://docs.djangoproject.com/en/1.4/topics/http/middleware
In this case, the middleware will look something like:
class AccessControlListMiddleware(object):
def process_view(self,request,view_func,view_args,view_kwargs):
permissions = set()
for role in request.session['permissions']:
for permission in PERMISSION_LIST[request.method][role]:
permissions.add(permission)
request.acl = list(permissions)
No. Don't mess with the request object. Especially since these are methods on the same class, you should simply assign data to self:
self.acl = getMyAccessControlList(request.method,request.session['username'])
...
if 'HAS_ALL_ACCESS' in self.acl:
I want to log actions made by users. In most OO languages, I would implement this via a LoggedAction class, having several child classes like LoginActionand LogoutAction. I could then iterate over a list of LoggedActions and get the specific child behaviour through virtual inheritance. This does not work using Django models however.
Example models.py:
class LoggedAction(models.Model):
user = models.ForeignKey(User)
timestamp = models.DateTimeField(auto_now_add=True)
def __unicode__(self):
return "%s: %s %s" % (unicode(self.timestamp), unicode(self.user), unicode(self.action()))
def action(self):
return ""
class LoginAction(LoggedAction):
def action(self):
return "logged in"
class LogoutAction(LoggedAction):
def action(self):
return "logged out"
Then I'd like to do [unicode(l) for l in LoggedAction.objects.all()] and get a list of messages like u'2012-02-18 18:47:09.105840: knatten logged in'.
As expected, this does not work, since what I get from all() is a list of LoggedAction objects having either a loginaction member or a logoutaction member. (The output is a list of messages like u'2012-02-18 18:47:09.105840: knatten, with no mention of the action.)
Is there a sane way to get the behaviour I'm after, or am I trying to apply the wrong paradigm here? (I guess I am, and that I should just have the specific action as a member in LoggedAction)
Yes, this is probably the wrong paradigm. It's easy to be misled by the object-relational mapper (ORM) - database tables don't really map all that well to objects, and this difference is known as the object-relational impedance mismatch.
What you actually need is to make action a field. This field can take a choices parameter which represents the possible values of that field - ie logged in or logged out:
class LoggedAction(models.Model):
ACTIONS = (
('I', 'logged in'),
('O', 'logged out')
)
user = models.ForeignKey(User)
timestamp = models.DateTimeField(auto_now_add=True)
action = models.CharField(max_length=1, choices=ACTIONS)
def __unicode__(self):
return u"%s: %s %s" % (self.timestamp, self.user, self.get_action_display())
Note that I've used arbitrary single-character strings to represent the actions, and the get_action_display() magic method to get the full description.
Have a look at InheritanceManager from django-model-utils. It allows you to get the concrete subclasses.
I use the blobstoreuploadhandler and hence must return a self.redirect but I need to pass values to my template. How can I do it? If I can't use template values then I suppose I can use session variables and I've included the beaker session library but I can't understand how to access the session variables in django template. Any idea how I should do it?
I use default builtin django with google app engine and I can access session variables with a request handler but I don't understand how to do it in templates:
class Sessiontest(webapp.RequestHandler):
def get(self):
# Get the session object from the environ
self.session = self.request.environ['beaker.session']
# Check to see if a value is in the session
if 'counter' in self.session:
counter = self.session['counter'] + 1
self.session['counter'] = counter
else:
self.session['counter'] = 1
counter = 1
self.session.save()
self.response.out.write('counter: %d' % counter)
Thanks
Update/edit: My problem is almost exactly like this Accessing session variable in Django template with Google App Engine (Webapp) - Python but with the library beaker instead of gaeutilities
Update: Here's some of the code. we see that using HTTP GET to pass the values won't be very good since there's an anti-spam test that should hide the values:
def post(self, view):
message = ''
challenge = self.request.get('recaptcha_challenge_field').encode('utf-8')
response = self.request.get('recaptcha_response_field').encode('utf-8')
remoteip = os.environ['REMOTE_ADDR']
cResponse = captcha.submit(
challenge,
response,
CAPTCHA_PRV_KEY,
remoteip)
if cResponse.is_valid:
isHuman=True
else:#failed anti-spam test and can try again
isHuman=False
#Reprint the form
import util
template_values = {'isHuman':isHuman,'user' : users.get_current_user(),}
template_values.update(dict(current_user=self.current_user, facebook_app_id=FACEBOOK_APP_ID))
template_values.update(dict(capture=captcha.displayhtml(public_key = CAPTCHA_PUB_KEY, use_ssl = False, error = None)))
path = os.path.join(os.path.dirname(__file__), 'market', 'market_insert.html')
self.redirect("/ai") # Here the values aren't passed on and I must make a redirect
If you are doing a redirect you might have to redirect with the variables that you wish to keep in the GET string. So you redirect from
/myview/
to
/myview2/?variable1=value
However, I think you should really look to see why you are doing redirects. I tend to do them after a POST to a form, and if the user needs to be logged on, I redirect to a login screen with
/authentication/login/?next=/view/they/wanted/to/see
Otherwise you could keep things in cookies but its not the best way to proceed.
How about letting your class inherit from multiple classes, both requesthandler class and blobstoreuploadhandler, in that way you can both render your template with values with the functions in the requesthandler, and use the functions in blobstoreuploadhandler?
A class definition with multiple base classes looks as follows:
class DerivedClassName(Base1, Base2, Base3):
<statement-1>
...
<statement-N>