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)
Related
I am overriding some methods of a popular package, django-activity-stream (I think the package is mostly irrelevant to this question).
from app/urls.py I call TeamJSONActivityFeed
urlpatterns = [
...
url(_(r'^feeds/organization/(?P<organization_id>.+)$'), TeamJSONActivityFeed.as_view(name='organization_stream')),
...
]
TeamJSONactivityFeed then calls 'pass', which I am not too familiar with, and inherits from two other classes, OrganizationStreamMixin and JSONActivityFeed.
from rest_framework.authentication import TokenAuthentication
class TeamJSONActivityFeed(OrganizationStreamMixin, JSONActivityFeed):
"""
JSON feed of Activity for a custom stream. self.name should be the name of the custom stream as defined in the Manager
and arguments may be passed either in the url or when calling as_view(...)
"""
authentication_classes = (TokenAuthentication,)
pass
My issue is that I cannot seem to access/pass the request object in/to these inherited classes. How would I go about passing this in? Right now, self.request.user and request.user are AnonymousUser objects.
class OrganizationStreamMixin(object):
name = None
def get_object(self,request):
# this is printing Anonymous User
pprint(str(self.request.user))
pprint(str(request.user))
return
def get_stream(self):
return getattr(Action.objects, self.name)
def items(self, request, *args, **kwargs):
return self.get_stream()(*args[1:], **kwargs)
class JSONActivityFeed(AbstractActivityStream, View):
"""
Feed that generates feeds compatible with the v1.0 JSON Activity Stream spec
"""
def dispatch(self, request, *args, **kwargs):
for i, v in kwargs.items():
print (" ", i, ": ", v)
return HttpResponse(self.serialize(request, *args, **kwargs),
content_type='application/json')
def serialize(self, request, *args, **kwargs):
pprint(str(self.request.user))
items = self.items(request, *args, **kwargs)
return json.dumps({
'totalItems': len(items),
'items': [self.format(action) for action in items]
})
Note: I am a bit of a django/python noob, but I am sure I am calling this properly from the front end. Similar requests have access to the request user.
I think there's a bit of confusion. You do have access to the request object otherwise it would raise an error for trying to access .user on None. If you're concerned about it being an AnonymousUser instance, then authenticate before accessing that view. If you need to prevent AnonymousUser instances from being able to access that view, then wrap the view with the login_required decorator.
Edit
You're overriding the dispatch method without calling super. That could be the problem.
I would like to invoke a function after all methods in a ModelViewSet. The function itself will be used as an external event log. So the response from the views would not be modified. I was not able to figureout how to achieve this. My ModelViewSet is a very basic one with serializer class and queryset defined.
Any help would be appreciated.
Override the dispatch method of view.
class MyDRFView(...):
def my_custom_logging_method(self, request, response, *args, **kwargs):
# do something useful here.....
...
def dispatch(self, request, *args, **kwargs):
response = super().dispatch(request, *args, **kwargs)
self.my_custom_logging_method(request, response, *args, **kwargs)
return respons
You can always override any python class. Overriding all the method can be little trickier and I guess will just create unnecessary logs. You should only override the method that is really of importance. Here is an example:
class LoggingModelViewSet(viewsets.ModelViewSet):
def perform_create(self, serializer):
print('Invoked perform_create')
serializer.save(owner=self.request.user)
def finalize_response(self, request, response, *args, **kwargs):
xresponse = super().finalize_response(request, response, *args, **kwargs)
print('finalize_response', xresponse)
return xresponse
and more like this... you should see the source here https://github.com/encode/django-rest-framework/blob/master/rest_framework/viewsets.py#L217 It is not so tricky.
In our django codebase, we extend dispatch method for the following reasons:
To set variables that are common to both GET/POST methods.
To restrict user access (For this created a separate mixin that just extends dispatch and does the checks)
If its considered bad, why is that and What is the alternative?
You can do this, as long as overriding it is your only choice to get the functionality you need.
For example, django-rest-framework overrides the dispatch method to provide functionality like authentication, permission, throttling.
See here.
def dispatch(self, request, *args, **kwargs):
"""
`.dispatch()` is pretty much the same as Django's regular dispatch,
but with extra hooks for startup, finalize, and exception handling.
"""
self.args = args
self.kwargs = kwargs
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate?
try:
self.initial(request, *args, **kwargs)
# Get the appropriate handler method
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
response = handler(request, *args, **kwargs)
except Exception as exc:
response = self.handle_exception(exc)
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
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.
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()