Is there a way to internally pass on the handling of a request from one RequestHandler subclass to another? Basically, what I would like to do is, from the get method of a RequestHandler (well, a subclass of RequestHandler), dispatch the task of handling the request to another handler (subclass of RequestHandler), whose name is determined by a value fetched from the datastore (I'm using GAE, but that is irrelevant to this problem). The code would look something like this:
class Dispatcher(RequestHandler):
def get_handler(some_id):
handler_name = get_handler_name(some_id) # fetches from datastore/etc.
return getattr(my_module, handler_name)
def get(self, some_id, *args, **kwargs):
handler = get_handler(some_id) # e.g., handler could be a HandlerA
# Not a real function, just to describe what to do:
# invokes get method of HandlerA (if handler == HandlerA)
dispatch_to_handler(handler, self, *args, **kwargs)
def post(self, some_id):
handler = get_handler(some_id)
dispatch_to_handler(....) # dispatches to post method of the handler
class HandlerA(RequestHandler):
def get(self, *args, **kwargs):
do_stuff()
def post(...):
do_post_stuff()
The big issue is that I need to somehow pass self and the positional and keyword arguments on to the other handler (HandlerA in this example), as self contains the request, response, session, authentication, and other data, which HandlerA (or whatever the handler may be) needs in order to process the request.
Try it this way:
def get(self, some_id, *args, **kwargs)
handler_cls = get_handler(some_id)
handler = handler_cls(self.request, self.response)
return handler.dispatch()
Related
#property
def get_maca(self, request):
if request.user.name == "example":
return self
I want to do something like this. If the user name is example return that object.
How to access the request like this?
The standard way is to pass the request, or in your case just the user object, from the view/router all the way down to the models.
This gets very quickly out of hand in a larger project, so my approach is to use thread local to save some of the request context that I like to have available across the whole project. The thread local storage will keep data available inside a single thread, without it being accessible from other threads - great if you're gonna run the Django app on a production server.
Start with the local storage:
from threading import local
_active_user = local()
def activate_user(user):
if not user:
return
_active_user.value = user
def deactivate_user():
if hasattr(_active_user, "value"):
del _active_user.value
def get_user():
"""Returns `(is_anonymous, user)` ."""
active_user = getattr(_active_user, "value", None)
if active_user and active_user is not AnonymousUser:
try:
return False, active_user
except AttributeError:
pass
return True, None
Now that's all good, you can use this manually. Calling activate_user will make you be able to call get_user in any place in your project. However, this is error prone - if you forget to call deactivate_user, the user object will still be available to the next coming request.
The rest of the answer is to show how to make things automatic.
Let's first make a middleware to clean up by calling deactivate_user after every single request.
class ThreadCleanupMiddleware:
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization.
def __call__(self, request):
# Executed for each request before
# the view (and later middleware) are called.
response = self.get_response(request)
# Executed for each request/response after
# the view is called.
deactivate_user()
return response
Add a path to the ThreadCleanupMiddleware to the end of your settings.MIDDLEWARE list.
Finish up with a view mixin that activates the user automatically (that's for class based views; if you're using functional views, it would be a decorator instead):
class ContextViewSetMixin:
def initial(self, request, *args, **kwargs):
super().initial(request, *args, **kwargs)
if request.user.is_authenticated:
activate_user(request.user)
class ContextModelViewSet(ContextViewSetMixin, viewsets.ModelViewSet):
pass
I have created a class based AWS lambda function in python called requestHandler.py as below
from action_dispatcher import ActionDispatcher
class RequestHandler(ActionDispatcher):
#staticmethod
def createTemplate(event, context):
return "Hello world"
My action_dispatcher.py is as shown below.
import json
class ActionDispatcher(object):
def __call__(self, event, context, *args, **kwargs):
action = event.get('action')
handler = getattr(self, action, None)
if handler is None:
return json.loads({'status': 'error', 'code': 404, 'message':"Action {0} not found.".format(action) })
return handler(request, *args, **kwargs)
With this above setup and lambda handler as requestHandler.RequestHandler, i get error "RequestHandler() takes no arguments" in this case i create action as createTemplate. so i want to call this method from RequestHandler.
It looks to me like you are trying to call your class instead of an instance of the class. RequestHandler() will call the __init__ method to initialize an instance of the class. Since you haven't defined the method it doesn't take any arguments. To access __call__ you need to call an instance of your class.
handler = RequestHandler()
result = handler(request, context, *args, **kwargs)
You can only define a handler in python using def handler(event, context):. However, I found a package that allows you to call the handler as a class
Usage, as noted in their documentation, is as follows:
pip install aws-lambda-handler
import aws_lambda
class EchoHandler(aws_lambda.Handler):
"""Echo handler."""
def perform(self, request, **k):
"""Echo perform method."""
response = aws_lambda.Response()
response.body = self.request.event
return response
echo_handler = EchoHandler()
# `echo_handler` is now a callable function you can map your AWS Lambda function to
The solution for my problem was simple, as mentioned by jacinator, i should try with class instance.
earlier for lambda handler, i used pass class as handler, now i am passing the instance of the class as handler.
Added the line in requestHandler.py rhandler = RequestHandler()
So previously my lambda handler was like requestHandler.RequestHandler, now it has been changed to requestHandler.rhandler.
In a class based view, HTTP methods map to class method names. Below, defined a handler for GET requests with the get method and url called get method. My question is how did the url map to the get method?
url(r'^hello-world/$', MyView.as_view(), name='hello_world'),
class MyView(View):
def get(self, request, *args, **kwargs):
return HttpResponse("Hello, World")
The url doesn't map to the get method, it maps to the view. Its up to the request method to guide django in the right way.
If you're talking in terms of actual code, its the dispatch method on the view.
def dispatch(self, request, *args, **kwargs):
# Try to dispatch to the right method; if a method doesn't exist,
# defer to the error handler. Also defer to the error handler if the
# request method isn't on the approved list.
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs)
Why not have a look at the code.
http://ccbv.co.uk/projects/Django/1.10/django.views.generic.base/View/
You will see the as_view() method (which gets called in your urls.py) has at line 67:
return self.dispatch(request, *args, **kwargs)
The dispatch() method in turn calls get in line 85 (assuming it is a GET request):
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
I have code like below
url(r'login$', views.LoginView.as_view(), name='login'),
and view as follows
class LoginView(TemplateView):
def __init__(self, *args, **kwargs):
#How to operate on request Object's type and its params.
I have mentioned my question as comment in code.
As mentioned by #karthikr you shouldn't be overriding __init__(). The request object is first available in the dispatch() method, which is called immediately after __init__(), but you shouldn't need to override that method either. Its primary purpose is to call the get(), post(), or other relevant method handlers. Generally speaking, however, it's not necessary to override those either.
If you really absolutely must catch the request at the earliest point possible though, then the dispatch method is your best bet.
class LoginView(TemplateView):
def dispatch(self, request, *args, **kwargs):
print self.request # Works!
return super(LoginView, self).dispatch(request, *args, **kwargs) # Don't forget this
If you want to initialize variables, the best place is the setup function:
class SomeBaseView(View):
var1 = None
def setup(self, request, *args, **kwargs):
super().setup(request, *args, **kwargs)
# do the vars initialization here
self.var1 = kwargs.get('param') # e.g.
By the way, setup function is called before dispatch and you dont need to return anything unlike dispatch.
When the view is called during the request/response cycle, the setup() method assigns the HttpRequest to the view’s request attribute, and any positional and/or keyword arguments captured from the URL pattern to the args and kwargs attributes, respectively. Then dispatch() is called.
https://docs.djangoproject.com/en/4.1/ref/class-based-views/base/#django.views.generic.base.View.as_view
Using dispatch function on the other hand is NOT recommended since you need to return the super call which can make things complicated.
I'd like to do something like this:
class Basehandler(webapp.RequestHandler):
def __init__(self):
if checkforspecialcase: #check something that always needs to be handled
return SpecialCaseHandler.get()
class NormalHandler(Basehandler):
def get(self):
print 'hello world'
return
class SpecialCaseHandler(Basehandler):
def get(self):
print 'hello special world'
return
The idea is that no matter what handler is initially called, if a certain case is met, we basically switch to another handler.
I'm pretty new to python, so I'm not sure whether what I'm trying to do is possible. Or whether this is the best approach. What I'm really trying to do is make sure to show someone the complete-your-profile page if they've started the registration process but haven't completed it... no matter what request they're making. So the "checkforspecialcase" looks at their sessions and checks for incomplete info.
To keep things DRY, use the Template Method pattern
class BaseHandler(webapp.RequestHandler):
def DoGet(self, *args):
''' defined in derived classes, actual per-handler get() logic'''
pass
def get(self, *args):
# don't get caught in endless redirects!
if specialCase and not self.request.path.startswith('/special'):
self.redirect('/special')
else:
self.DoGet(*args)
class NormalHandler(BaseHandler):
def DoGet(self, *args):
# normal stuff
class SpecialHandler(BaseHandler):
def DoGet(self, *args):
# SPECIAL stuff
WSGIApplication routes incoming requests based on the URL. For example,
application = webapp.WSGIApplication(
[('/special-case', SpecialCaseHandler)])
When checkforspecialcase passes, you can use self.redirect('/special-case').
Your Basehandler could just implement a get() that checks for the special case and either redirects or calls self.view(), and each handler could implement view() (or whatever you'd like to call it) rather than get().
I'm not really into writing a class for each of my handlers, or using inheritance so conspicuously, so I'd recommend rolling decorators like these:
routes = []
def get (route):
def makeHandler (handle, *args, **kwargs):
class Handler (webapp.RequestHandler):
def get (self, *args, **kwargs):
shouldRedirectToCompleteProfile = # do your test
if shouldRedirectToCompleteProfile:
self.redirect('/special-case')
else:
handle(self, *args, **kwargs)
routes.append((route, Handler))
return Handler
return makeHandler
def post (route):
def makeHandler (handle, *args, **kwargs):
class Handler (webapp.RequestHandler):
def post (self, *args, **kwargs):
handle(self, *args, **kwargs)
routes.append((route, Handler))
return Handler
return makeHandler
#get('/')
def home (ctx):
# <...>
#get('/whatever/(.*)/(.*)')
def whatever (ctx, whatever0, whatever1):
# <...>
#post('/submit')
def submit (ctx):
# <...>
application = webapp.WSGIApplication(routes)