Django middleware advised not to use request.POST why? - python

"Accessing request.POST inside middleware before the view runs or in process_view() will prevent any view running after the middleware from being able to modify the upload handlers for the request, and should normally be avoided."
This is from the django documentation. First of all, if I just read the POST, without changing that, how does it even know and how does it prevent the view from doing it's business and second, how is a CsrfViewMiddleware different in that sense?

The warning comes from this ticket and this.
Django currently parses the POST data lazily, but middlware might try to access POST on a request and trigger parsing, even though the function itself never touches POST.
That would put high load on the machine if the POST data is rather large ...
It means that the view will be unable to set any custom upload handlers, perform custom parsing of the request body, or enforce permission checks prior to file uploads being accepted.
And the difference about CsrfViewMiddleware is stated clearly right below the said warning in the docs:
The CsrfViewMiddleware ... provides the csrf_exempt() and
csrf_protect() decorators which allow views to explicitly control at what point the CSRF validation should
occur.

Related

Why view functions require a request parameter In Django?

Example from Django documentation:
def index(request):
return HttpResponse('<h1>hellworld!</h1>')
def detail(request, question_id):
return HttpResponse("Question: %s" % question_id)
Since the request argument is never used, why should I include it in each function signature?
The purpose of a web server is to reply to HTTP requests (In simple terms). Django being a web framework generates responses depending on the request that it receives.
The business logic of handling the requests is done by Django Views, it is their purpose. This is why Django supplies the request to the view function, so the code can access the request data and choose what actions it should take and which response it should send back (even though that specific view doesn't make use of it).
The decision of requiring the view to receive the request as an argument is an implementation decision made by Django developers, making it part of the interface the view has with the rest of the "system". Other frameworks might choose making it available globally and not suplying it to the views/controllers directly, but the merits of one or another approach is other discussion.
Many of the examples in Django's documentation (and any documentation, really) are necessarily contrived. They're simple because the complexity of a real example would take away from the point that's being made.
In real use cases, you'll frequently want access to the request object. You may want to know which User is attached to the request (request.user) or whether the request is a GET or a POST (request.method).
You can also inspect the headers (request.headers), decode the JSON object sent with a POST request (json.loads(request.body)), etc.
Those are just examples. In real world a programmer would want to find request GET arguments, decode JSON POST body, inspect headers, get user or session data, etc.

Blank responses from Django runserver when checking user permissions

I have a similar problem to those listed in
Django produces blank pages when POST data is sent and Models are accessed
and
Nginx connection reset, response from uWsgi lost
Here is one of the views in question:
#transaction.commit_on_success
#occ_update
#checks_status
def hold(request):
if not request.user.has_perm('orders.hold'):
return error_response_rollback(NO_PERMISSION_MSG % "hold orders")
order = Order.objects.get(pk=request.POST.get('pk'))
occ_revision = int(request.POST.get('occ_revision'))
agent = Agent.get_agent(request.user)
action = Action(agent=agent, type='hold_order',
comments=request.POST.get('comments'))
action.save()
order.hold(action, occ_revision)
return ok_response_commit("Order held successfully.")
error_response_rollback rolls back the transaction and returns an HttpResponse with JSON as its contents.
I am adding permission checking to many of my views in my application and when the user does not have the correct permission, a blank response is returned.
However like the questions referenced above, if you put a
print request
or
request.POST
statement BEFORE the permission check, the NO_PERMISSION_MSG JSON string is returned to the browser correctly every time (error_response_rollback returns an HttpResponse object with JSON in it.)
You get blank responses when you check permissions before the "print request" and they do not have the correct permissions.
You do NOT get blank responses when:
the user has the correct permissions
a "print request" statement is before any permission check
you use Firefox at any point.
The #occ_update and #checks_status decorators just catch exceptions. These problems occur with and without them present.
I'm developing in Chrome and none of this is an issue in Firefox.
One page I found suggested overloading the WSGIRequest object to read the request before it is passed to the view but this seems icky to me and I'd rather find out the real solution.
Does anyone know of any fixes/settings to the runserver command to help this issue without hacking on the request? My users are primarily using Chrome so I'd prefer to keep using it... we'll see. Currently developing in Windows using Django 1.3.1
One option I have considered is making another manage.py command to handle this but that, too, seems icky.
Thanks
Update:
I have been able to re-organize my code so that any permission checks happen after some bit of data is read from the POST. This seems to have eliminated any symptoms of this problem. It's still not ideal but it is a good alternative to inserting middleware to read the post. and won't always be possible in all applications.
Please comment if you have a similar situation and just can't figure it out.
As saying in the second link in your post, especially http://forum.nginx.org/read.php?2,196581 : when you works w/ Nginx and uWSGI and get a non-empty POST, always read the request.POST before return an HttpResponse. The reason is described in the link.
You don't have to override an handler, just put the request.POST line before the return code, or inside some decorator or middleware.
I encountered this issue for production site half a year ago and put the line in a middleware to solve it.

Defining both post and get method under same request handler

I'm using python to develop a web app.
I defined both "get" and "post" method in the same request handler to serve different purpose. That is, I use "get" method to present a form to user, and "post" method to handle the submitted form.
It works fine, but is this approach appropriate? Or should I better define get and post separately in different request handler? Thanks!
Your approach is appropriate. According to the newest documentation you can even define post and get as functions outside request handler and just as other functions in your module and that's a way I would choose since it eliminates problems that can happen when instanciating request handlers.
If starting a new app from scratch I probably would try to put my get and post function outside of request handler with the new python 2.7 runtime that according to docs supports that.

In Django, can I set vary_on_cookie globally?

When developing a Django app, I can use the vary_on_cookie decorator to make sure
that upstream caches use the session cookie in addition to the URL to distinguish
between different pages.
I have a lot of view functions and all of them now require this header. Is it possible
to specify this behavior once (maybe in the settings file for the entire site or at
least for an entire app)? Or do I really have to repeat that decorator in front of every
single view function?
Thank you very much for your help...
Sounds like you will want to write your own middleware and modify each request to include any extra headers that you want included on each request.
Creating middleware is easy and you will probably be interested in the process_response method, as you can simply modify the response and you're done.
To modify the headers of an HttpResponse check out the docs here.
UsingSessionMiddleware adds Cookie to the Cache-Control headers for every request.

django Authentication using auth.views

User should be redirected to the Login page after registration and after logout. In both cases there must be a message displayed indicating relevant messages.
Using the django.contrib.auth.views.login how do I send these {{ info }} messages.
A possible option would be to copy the auth.views to new registration module and include all essential stuff. But that doesn't seem DRY enough.
What is the best approach.
Update: Question elaboration:
For normal cases when you want to indicate to some user the response of an action you can use
request.user.message_set.create()
This creates a message that is displayed in one of the templates and automatically deletes.
However this message system only works for logged in users who continue to have same session id. In the case of registration, the user is not authenticated and in the case of logout since the session changes, this system cannot be used.
Add to that, the built in login and logout functions from django.contrib.auth.views return a 'HttpResponseRedirect' which make it impossible to add another variable to the template.
I tried setting things on the request object itself
request.info='Registered'
and check this in a different view
try:
info = request.info:
del request.info
except:
info = ''
#later
render_to_response('app/file',{'info':info})
Even this didn't work.
Clearly I can define a registered.html and add this static message there, but I was being lazy to write another template and trying to implement it DRY.
I realized that the cases were different for "registered" message and "logged out" message. And the DRY approach I used, I shall write as an answer.
If the messages are static you can use your own templates for those views:
(r'^accounts/login/$', 'django.contrib.auth.views.login', {'template_name': 'myapp/login.html'}
From the docs.
I think the best solution to this problem is to use a "flash"-type session-based messaging system. There are several floating around: django-flash seems really nice, I use django-session-messages which is very simple. Hopefully by the time we get to Django 1.2 this'll be baked-in.
You have Request Context Processors to add this kind of information to the context of every template that gets rendered.
This is the "zero impact" way to do this kind of thing. You don't update any view functions, so it meets some definitions of DRY.
See http://docs.djangoproject.com/en/dev/ref/templates/api/#id1
First, write your own login.html template.
Second, write your own context function to provide any additional information that must be inserted into the template.
Third, update settings to addy your context processor to the TEMPLATE_CONTEXT_PROCESSORS setting.

Categories