django middleware difference between process_request and process_view - django

I am little bit confused on process_request and process_view.
Process request is something that u want to pass on view with request. Example can be taken from request.user.
Then what does process_view does ? Is it for executing any view initially befor calling any url ? Like initially I want to show home view but this can be done from url too.
Can anyone give me example when to use process_view ?
Thank you

process_request is called before Django determines which view should handle the request (hence, its only parameter is the request).
process_view is called after Django determines which view will handle the request, but before that view is called. It will have access to the request object, along with the view that will handle it and the parameters that will be passed to that view.
Whenever you need to know the view that will be used for a request, you can use process_view. A good example for this is Django's CSRF Middleware process_view, which will not enforce CSRF protection if a csrf_exempt decorator is present on the view the request is meant for:
def process_view(self, request, callback, callback_args, callback_kwargs):
[...]
if getattr(callback, 'csrf_exempt', False):
return None
[...]

Adrian Ghiuta have written a very good answer. I just want to add few points to that.
process_request is called before doing the url matching and process_view is called after url matching but before invoking that view.
We can use the process_request to change the url itself and thus invoke a different view. This point helped me understand these, so I thought to answer it, maybe it'll someone else too.

Related

Can i call the get method in django class based views from the post method

In my django app's views.py i have a class based view Profile(View) which has two methods get(self, request) and post(self, request). So when a post request is send to this view, when the input passes a test, i want to go invoke the get() method which will return the template with updated data. Is it possible?
Technically, this is of course perfectly possible and as simple as return self.get(request, **kwargs) BUT you very certainly DONT want to do that, as it will break the expectations of POST vs GET. The canonical navigation pattern is that a sucessful POST should be followed by a redirect, and is known as the "post-redirect-get" pattern. Note that you can use sessions (directly or with the messages framework) if you want to pass some state (outside database state) from the POST to the GET.

django rest post return 405 error code

I am using django with rest framework and I try to test POST on existing object but I keep getting 405. my ViewSet looks like this:
class Agents(viewsets.ModelViewSet):
serializer_class = serializer.AgentSerializer
model = serializer_class.Meta.model
....
and in the urls:
router = routers.SimpleRouter()
router.register(r'rest/agents', api_views.Agents, "Agent")
...
urlpatterns += router.urls
I call the post request from within APITestCase class (rest testing), my post request looks like this:
response = self.client.post(url, {'available': True, 'online':True}, format='json')
and printing url shows "/chat/rest/agents/1910_1567/", while 1910_1567 is the valid id of an existing agent (I create the agent during the setup and use its id).
I've seen other questions about rest post getting 405, but all the solutions there were url-related and in my case the url is correct. I even ran the setup outside the test and accessed the url via browser to get the object and the object indeed exist. but when I try to post to it - 405.
any ideas?
thanks!
Most probably your url is somehow matching with some different url's regex, and the request is being dispatched to some other view which disallows post request. Could you mention others urls in your urls.py? Infact you can verify this by adding pdb(debugger) in dispatch method of your view. If you made it to the pdb, then you can assume I was wrong.
If that is not the case, then you can evaluate the issue from dispatch method with debugger. Just in case you have any doubt about how to do that -
class Agents(viewsets.ModelViewSet):
serializer_class = serializer.AgentSerializer
model = serializer_class.Meta.model
def dispatch(self, *args, **kwargs):
import ipdb; ipdb.set_trace()
return super(Agents, self).dispatch(*args, **kwargs)
Solution Found:-
You are passing id in url, so this request would be routed to detail view and detail view only allow GET, PUT, DELETE operations on resource since resource already exists. So to create resource, don't provide the id. Otherwise use PUT request and provide support for creation in PUT.
POST are supposed to be for creation purpose. If you want to update with a ViewSet you'll need to PUT or PATCH (partial update) instead.
Edit:
For more about this, here's some explanation on the HTTP methods used for REST API:
http://restful-api-design.readthedocs.org/en/latest/methods.html
This is also described in the DRF documentation at:
http://www.django-rest-framework.org/api-guide/routers/#simplerouter

Django check CSRF token manually

I am implementing an API that works either with an API key, or with a CSRF token. The goal is for it to be usable either by a web app (protected by CSRF) or by a third party application (protected by API key).
Basically on each request (all via POST), I check if there is an API key. If there is a valid one, it's good to go. If not, I want to fall back to verifying CSRF.
Is there a function I can call to verify the CSRF myself? The view itself is #csrf_exempt because API keys need to work.
You could probably subclass the CsrfViewMiddleware class and override the process_view method. Then include your custom middleware instead of the default CSRF one.
from django.middleware.csrf import CsrfViewMiddleware
class CustomCsrfMiddleware(CsrfViewMiddleware):
def process_view(self, request, callback, callback_args, callback_kwargs):
if request.META.get('api_key'):
# process api key
else:
return super(CsrfViewMiddleware, self).process_view(...)
You can use builtin csrf verification like this:
from django.middleware.csrf import CsrfViewMiddleware
def check_csrf(request):
reason = CsrfViewMiddleware().process_view(request, None, (), {})
if reason:
# CSRF failed
raise PermissionException() # do what you need to do here
I've been accessing the CsrfViewMiddleware like Aldarund has shown but more needs to be said about this kind of solution:
If you are performing the test in a view, then you can return reason directly. According to how Django middleware works, when process_view returns something else than None, then it must be a HttpResponse object so it can just be returned by the view.
There may be cases where you do not want to return reason directly, but if there is no reason not to do so, I'd rather return it, for consistency with how the site behaves in other cases.
If you use the test in a run-of-the-mill view, and you already use CsrfViewMiddleware site-wide, it is usually the case that request will already have passed once through CsrfViewMiddleware. (Yes, it can happen. I have a case where a request I receive is modified and retested through CsrfViewMiddleWare after it has already been tested by CsrfViewMiddleware due to the site-wide configuration.) However, the middleware sets csrf_processing_done on request after it tests it and won't retest it if it is called again, because of this flag. So csrf_processing_done has to be reset to False to perform a second test.
Here's an illustration of the above:
from django.middleware.csrf import CsrfViewMiddleware
def view(request):
request.csrf_processing_done = False
reason = CsrfViewMiddleware().process_view(request, None, (), {})
if reason is not None:
return reason # Failed the test, stop here.
# process the request...
In my case, I wanted to POST some raw data with CSRF check.
So, I use this decorator requires_csrf_token in the view which process POST data :
from django.views.decorators.csrf import requires_csrf_token
#requires_csrf_token
def manage_trade_allocation_update(request):
In my template, I added csrf_token génération and put it in data post :
{% csrf_token %}
...
data['csrfmiddlewaretoken'] = document.querySelector('input[name="csrfmiddlewaretoken"]').value;
With this mecanism, I can use CSRF protection with manual HTTP POST request.

Setting a variable in middleware to be accessed in the template

I seem to be having difficulty setting a variable in one of my middleware classes that I can then access in the template layer.
The basic layout is this:
class TheMiddleware(object):
def __init__(self, etc):
stuff...
def process_response(self, request, response):
request.my_var = "whatever"
return response
Then on the template for a different view I have:
{% custom_tag arg_a %}
Which is is a template tag that should return the variable from the request:
#register.simple_tag
def custom_tag(arg_a):
return threading.currentThread().request.my_var
This errors out with "Caught AttributeError while rendering: 'WSGIRequest' object has no attribute 'my_var'"
I thought it might be the way I was accessing the request in the template tag. So I added django.core.context_processors.request to my TEMPLATE_CONTEXT_PROCESSORS as in This question and tried passing the request object to the tag, then accessing request directly from the template but with no luck.
I think I lack an understanding on how request objects work. Is it possible to assign a variable to a request object and pick that variable up several views on? I thought the request object was passed through the views, but it seems that instead a new instance is generated.
If that is the case, how would you go about storing a global variable within middleware that you could then access from any point in your app, be it in a view or a template?
Update:
To clear up the confusion (whether mine or others I'm not sure!) I'm not trying to set the request variable in the process_response middleware of a view and then pick it up in the template of that same view. I understand that that wouldn't work because the template has been processed before the variable is saved. (This is a deliberate act on my part).
I have two views, view1 and view2 view one has a decorator that causes the middleware to set the variable in the request. It is the view2 template, which comes after the variable has been set, that I wish to access the variable.
You trying to set variable during processing of response in your middleware.
I think you should be implementing process_request() instead to set the variable.
def process_request(self, request):
request.my_var = "whatever"
return
If you're setting it on the request, I can't see any reason at all to try and use threadlocals here. You should use the context processor as you describe.
The reason for your problem, though, is that process_response is run in the response phase of the request/response cycle: ie, after your view has been called. You should define process_request instead. See here for a description of the order that middleware methods are called.

Django perform an action just before login_required is called?

In django, I want to do a particular action just before login_required() is called for a view. One hack or easy way out is to just have that url point to a view where login is not required and do that particular action (action: setting some things in the request.session) and then redirect back to this main view where login is required.
How should I go about it, if there's some option to cater to such things in django?
Is there a better or simpler way than to write my own decorator to do this?
Decorator is the best way, because it make code easy to understand and as for security reason, user can block redirections so you'll need to think about how to avoid this.
This arcticle seems usefull How to make a chain of function decorators?, because I think you'll need to pass request variable and also place your decorator to process checking first.
Just use the user_passes_test decorator instead and do your custom stuff before manually validating that they are authenticated. See: https://docs.djangoproject.com/en/dev/topics/auth/#django.contrib.auth.decorators.user_passes_test
Your question doesn't give much detail, but going by your example (setting some request variables), it sounds like you need Request Middleware. Take a look at the builtin django.contrib.auth.middleware.AuthenticationMiddleware for an example:
class AuthenticationMiddleware(object):
def process_request(self, request):
assert hasattr(request, 'session'), "The Django authentication middleware requires session middleware to be installed. Edit your MIDDLEWARE_CLASSES setting to insert 'django.contrib.sessions.middleware.SessionMiddleware'."
request.user = SimpleLazyObject(lambda: get_user(request))
You'll need to define a class with the process_request method (as shown above). The process_request method will be called for each request, and passed a request instance. Within the method, you can check for request.user, and if the user is authenticated, and set whatever variables you want for the current request.
Writing your own decorator is the best way to approach this. Anywhere that you have #login_required, just add your own decorator in addition to the login_required decorator.
Or, you could call the login_required function from your decorator.