I am using tornado and I declared a RequestHandler with a single parameter like this:
class StuffHandler(RequestHandler):
def get(self, stuff_name):
...
app = Application([
(r'/stuff/(.*)/public', StuffHandler)
])
Now I added another handler for '/stuff/(.*)/private', which requires the user to be authenticated:
class PrivateStuffHandler(RequestHandler):
#tornado.web.authenticated
def get(self, stuff_name):
...
This of course will cause get_current_user() to be called before get(). The problem is that, in order for get_current_user() to run, I need to know the stuff_name parameter.
So I thought that I may use the prepare() or the initialize() method, which is called before get_current_user(). However, I can't seem to access stuff_name from those methods. I tried putting stuff_name as a parameter but it didn't work, then I tried calling self.get_argument("stuff_name") but it didn't work either.
How do I access an URL parameter from the prepare() method?
I think you can use self.request.path to get the full path, then achieve the value in path which you need.
In the end, I asked straight to Tornado developers and a helpful user made me notice that there's self.path_args and self.path_kwargs available from anywhere in the class.
So, from the prepare() method (or even the get_current_user() method), I can do:
stuff_name = self.path_args[0]
Related
I want to set more than one get route in a class. Or simply map a route to a function.
This is what I've done:
class TestRoute1(tornado.web.RequestHandler):
def get(self):
self.write("I have done something.")
class TestRoute2(tornado.web.RequestHandler):
def get(self):
self.write("This is something else.")
application = tornado.web.Application([
(r"/test1", TestRoute1),
(r"/test2", TestRoute2),
])
application.listen(8080)
And this is what I think should be possible to do:
class TestRoute(tornado.web.RequestHandler):
def func1(self):
self.write("I have done something.")
def func2(self):
self.write("This is something else.")
application = tornado.web.Application([
(r"/test1", TestRoute.func1),
(r"/test2", TestRoute.func2),
])
application.listen(8080)
Or something like this. Is it possible? If it is not, what is the alternatives to one I use?
In general, the idiomatic way to do this in Tornado is to use two separate classes as you've done in your first example, and use a common base class to contain any code that needs to be shared between them.
There are, however, two ways to pass additional information from the routing table into the handler. First, if there are capturing groups in the routing regex, the substrings they capture will be passed to the get/post/etc methods. Second, you may pass an additional dictionary in the routing table (as a third element of the tuple); this dictionary will become keyword arguments to the handler's initialize() method.
This is against Tornado creators' original intent, but you can use function decorators to route requests.
Basically you can use use decorator to create inner class and use setattr() to replace get method with your own.
Take a look at this article I wrote that provides Tornado function router.
I am new to python and am trying to define a function and then use it in Google App Engine - but I keep getting the error "Error: global name 'cache_email_received_list' is not defined" when I try to execute the function. Any help would be greatly appreciated, thanks.
Here is my function:
class EmailMessageHandler(BaseHandler2):
def cache_email_sent_list(): #set email_sent_list to memcache
email_sent_list = db.GqlQuery("SELECT * FROM EmailMessage WHERE sender =:1 ORDER BY created DESC", user_info.username)
if email_sent_list:
string1 = "email_sent_list"
email_sent_list_cache_id = "_".join((user_info.username, string1))
memcache.set('%s' % email_sent_list_cache_id, email_sent_list, time=2000000)
logging.info('**************email_sent_list added to memcache*********')
Here is where I am trying to call it:
if email_received_list is None and email_sent_list is not None:
params = {
'email_sent_list': email_sent_list,
}
cache_email_sent_list()
cache_email_sent_list() is a method of the class EmailMessageHandler therfore the method needs to pass in self a a parameter it will therefore look like this:
class EmailMessageHandler(BaseHandler2):
def cache_email_sent_list(self): #set email_sent_list to memcache
email_sent_list = db.GqlQuery("SELECT * FROM EmailMessage WHERE sender =:1 ORDER BY created DESC", user_info.username)
if email_sent_list:
string1 = "email_sent_list"
email_sent_list_cache_id = "_".join((user_info.username, string1))
memcache.set('%s' % email_sent_list_cache_id, email_sent_list, time=2000000)
logging.info('**************email_sent_list added to memcache*********')
Then when you call it from within the class EmailMessageHandler you have to do it like this:
self.cache_email_sent_list()
If however you are calling it from outside the class EmailMessageHandler you need to first create an instance and then call it using:
instanceName.cache_email_sent_list()
Just as an addition to the previous answers: In your post you define cache_email_sent_list() as a function defined in a class definition, which will not work. I think you are confusing instance methods, static methods and functions. There's a prominent difference between these three.
So, as a stylised example:
# instance method:
class MyClass(MySuperClass):
def my_instance_method(self):
#your code here
# call the instance method:
instance = MyClass() # creates a new instance
instance.my_instance_method() # calls the method on the instance
# static method:
class MyClass(MySuperClass):
#staticmethod # use decorator to nominate a static method
def my_static_method()
#your code here
# call the static method:
MyClass.my_static_method() # calls the static method
# function
def my_function():
# your code here
# call the function:
my_function() # calls your function
Indentation is part of Python syntax and determines how the interpreter handles your code. It takes a bit getting used to but once you've got the hang of it, it's actually really handy and makes your code very readable. I think you have an indentation error in your original post. Just add the correct indentation for the method cache_email_sent_list() and call it on an instance of EmailMessageHandler and you're good to go.
The problem has nothing to do with GAE.
The problem is that you've defined cache_email_sent_list as a method of the class EmailMessageHandler, but you're trying to call it as a top-level function. You can't do that. You need to have an instance of a EmailMessageHandler to call it on.
If you're trying to call it from another method of EmailMessageHandler, that instance should be available as self. For example:
self.cache_email_sent_list()
If you're trying to call it from elsewhere, it's up to you to figure out what instance you should be calling it on. For example:
handler_passed_as_param_to_this_function.cache_email_sent_list()
Note that your error message is about cache_email_received_list, but your code only has cache_email_sent_list. I'm guessing that you have parallel code, and the exact same error for both cases, but of course I could be guessing wrong—in which case you'll have to actually show us either the code that goes with your displayed error, or the error that goes with your displayed code…
Case. I want to modify and add the following behavior to the code below (it's a context processor):
After checking if a user is authenticated check the last time the balance was updated (cookie maybe) if it was updated in the last 5 mins do nothing, else get the new balance as normal.
def get_balance(request):
if request.user.is_authenticated():
balance = Account.objects.get(user=request.user).balance
else:
balance = 0
return {'account_balance': balance}
HOWEVER:
I want to learn a little more about OOP in Django/Python can some modify the example to achieve my goal include the use of:
Property: I come from Java, I want to set and get, it makes more sense to me. get balance if does not exist else create new one.
Constructor method: In Python I think I have to change this to a class and use init right?
UPDATE:
To use a construct I first think I need to create a class, I'm assuming this is ok using as a context processor in Django to do something like this:
class BalanceProcessor(request):
_balance = Account.objects.get(user=request.user).balance
#property
def get_balance(self):
return return {'account_balance': _balance}
#setter???
Python is not Java. In Python you don't create classes for no reason. Classes are for when you have data you want to encapsulate with code. In this case, there is no such thing: you simply get some data and return it. A class would be of no benefit here whatsoever.
In any case, even if you do create a class, once again Python is not Java, and you don't create getters and setters on properties unless you actually need to do some processing when you get and set. If you just want to access an instance attribute, then you simply access it.
Finally, your proposed code will not work for two reasons. Firstly, you are trying to inherit from request. That makes no sense: you should inherit from object unless you are subclassing something. Secondly, how are you expecting your class to be instantiated? Context processors are usually functions, and that means Django is expecting a callable. If you give the class as the context processor, then calling it will instantiate it: but then there's nothing that will call the get_balance method. And your code will fail because Django will pass the request into the instantation (as it is expecting to do with a function) and your __init__ doesn't expect that parameter.
It's fine to experiment with classes in Python, but a context processor is not the place for it.
I have a POST method which calls a few tasklets. These tasklets do have yields in them, and I do have some x.put_async() in my code. So I don't want it to return before all the async stuff is done. So I decorated all my tasklets, which are just small functions with #ndb.tasklet. Also, on top of my POST method, I have:
#ndb.toplevel
def post(self):
However, in the documentation it states:
But if a handler method uses yield, that method still needs to be
wrapped in another decorator, #ndb.synctasklet; otherwise, it will
stop executing at the yield and not finish.
Indeed my method has a yield. It's already wrapped in #ndb.tasklet. Do I replace this with #ndb.synctasklet or do I use both (if so how would I use both)?
Also, see this thread which has some relevance. I too noticed an issue where my request would return without any output, but is un-reproducible. It happens every 15 minutes or so of constant use. I had app = ndb.toplevel(webapp2.WSGIApplication([..]) only, but now I've added #ndb.toplevel to the main POST methods, but the issue still persists.
Should I put #ndb.tasklet on top of methods that have just put_async()'s too? (Should I put it on top of every method just to be safe? What are the downsides to this?)
Regarding the handler and using #ndb.toplevel and #ndb.synctasklet:
The way I understood it was that you need to use both #ndb.synctasklet and #ndb.toplevel on the handler. All the sub-tasklets only need the #ndb.tasklet decorator. e.g.
class Foo(ndb.Model):
name = ndb.StringProperty()
#ndb.tasklet
def my_async(self):
....
#do something else that yields
raise ndb.Return("some result")
#ndb.toplevel
#ndb.synctasklet
def post(self):
foo = Foo(name="baz")
yield foo.put_async()
yield foo.my_async()
....
However. looking at the source, it appears that #ndb.toplevel is actually a synctasklet anyway:
def toplevel(func):
"""A sync tasklet that sets a fresh default Context.
Use this for toplevel view functions such as
webapp.RequestHandler.get() or Django view functions.
"""
Running a small test with yields in the handler and decorated with #ndb.toplevel still seems to work, and appears that you can remove #ndb.synctasklet from the handler.
Regarding whether you should include #ndb.tasklet on methods that call put_async():
If you're not yielding on the put_async(), then you don't need to include #ndb.tasklet on the surrounding method (#ndb.toplevel will handle getting the results from the put_async())
I'm using Flask and I'd like to protect everything under /admin path.
How can I do that? I'm sure there's a better way than checking for the session in every function.
The most straightforward way to do this, I think, is to use a Blueprint similar to how it is described in this snippet. Then you can have some code that will run before each request when the URL starts with /admin, and within that code you can do your authentication.
The obvious way would be to write a decorator that tests the session, and redirects to another page if the authentication fails. I don't know how much python you know, but a decorator is a function that takes a function and returns another function. Basically, it's a function modifier. Here's a decorator that should show you the way to writing your own to check the session:
import functools
def check_session(view):
#functools.wraps(view)
def inner(*args, **kwargs):
if <test for auth>:
return view(*args, **kwargs)
else:
return flask.redirect("/")
return inner
As you can see, we have a function that takes the view function, and then defines a new function called inner which checks for authentication, and if it succeeds, calls the original view. The line #functools.wraps(view) is an example of using a decorator; the functools.wraps decorator gives the function it is wrapping the properties of the function that it is given as it's first argument. To use this decorator, apply it to your views as such:
#app.route("/admin")
#check_session
def admin_view():
return "Top secret"
Any user who fails the authentication check will now be redirected away, and users who do will be able to see the view as usual. Decorators are applied in a bottom to top order, so make sure that you put it after any other decorators that do function registration (i.e. #app.route).