Let's suppose I have the following url: /valid/django/app/path/?foo=bar&spam=eggs
I can simulate a request to this URL in Django thusly:
from django.shortcuts import render
from django.core.urlresolvers import resolve
def simulate(request, url=None, template_name="not_important.html"):
if url:
dv = resolve(url.split('?')[0])
return dv.func(request, *dv.args, **dv.kwargs)
else:
return render(request, template_name)
However, I'd like to include the parameters to the included view, so that the request.REQUEST and request.GET objects would also include foo
and spam
I don't see how I can do this cleanly; as far as I understand the request.GET and request.REQUEST dictionaries are immutable so I can't merely do something like:
import urlparse
def simulate(request, url=None, template_name="not_important.html"):
if url:
dv = resolve(url.split('?')[0])
qs = "".join(url.split('?')[1:])
if qs:
request.REQUEST.update(urlparse.parse_qs(qs))
request.GET.update(urlparse.parse_qs(qs))
return dv.func(request, *dv.args, **dv.kwargs)
else:
return render(request, template_name)
Or I'll get the error message
This QueryDict instance is immutable
for the request.GET object and
'MergeDict' object has no attribute 'update'
for the request.REQUEST object
In case anyone is wondering why I want to do this: I want to allow users to fill out a form and then, when they submit, if they aren't logged in it sends them to a login form that includes the original URL in a hidden field. After logging in, rather than redirecting back to that link (which would be a GET request) I want it to call the original view, with the request variables it originally had, so that it can use the same POST request.
And so of course in the process I'm also just interested in whether it would be possible to simulate a POST/GET request to a Django view when given a valid URL for the site.
request.GET/POST are QueryDict instances. According to the documentation on QueryDict, there are indeed "immutable" unless you clone them:
QueryDict instances are immutable, unless you create a copy() of them. That means you can't change attributes of request.POST and request.GET directly.
You can copy, update and re-assign QueryDicts as such:
ipdb> request.GET
<QueryDict: {u'x': [u'1']}>
ipdb> request.POST
<QueryDict: {}>
ipdb> request.REQUEST
MergeDict(<QueryDict: {}>, <QueryDict: {u'x': [u'1']}>)
ipdb> new_post = request.POST.copy()
ipdb> new_post.update(request.GET)
ipdb> request.POST = new_post
ipdb> request.POST
<QueryDict: {u'x': [u'1']}>
ipdb> request.GET
<QueryDict: {u'x': [u'1']}>
ipdb> request.REQUEST
MergeDict(<QueryDict: {}>, <QueryDict: {u'x': [u'1']}>)
The trick to update the MergeDict is to override its dicts attribute as such:
ipdb> request.REQUEST
MergeDict(<QueryDict: {}>, <QueryDict: {u'x': [u'1']}>)
ipdb> request.REQUEST.dicts = (request.POST, request.GET)
ipdb> request.REQUEST
MergeDict(<QueryDict: {u'x': [u'1']}>, <QueryDict: {u'x': [u'1']}>)
Note that MergeDict is defined in module django.utils.datastructures, and instanciated in django.core.handlers.wsgi (and django.core.handlers.modpython) as such: self._request = datastructures.MergeDict(self.POST, self.GET)
.
DISCLAMER: MergeDict is not documented, will break one day, and probably even kill some kittens. Use at your own discretion and with your own kittens. That said I like your use case, it's a pretty good idea.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With