26

I just learnt that with Rails is possible to simulate HTTP requests in the console with few lines of code.

Check out: http://37signals.com/svn/posts/3176-three-quick-rails-console-tips (section "Dive into your app").

Is there a similar way to do that with Django? Would be handy.

nemesisdesign
  • 8,159
  • 12
  • 58
  • 97

3 Answers3

52

You can use RequestFactory, which allows

  • inserting a user into the request

  • inserting an uploaded file into the request

  • sending specific parameters to the view

and does not require the additional dependency of using requests.

Note that you have to specify both the URL and the view class, so it takes an extra line of code than using requests.

from django.test import RequestFactory

request_factory = RequestFactory()
my_url = '/my_full/url/here'  # Replace with your URL -- or use reverse
my_request = request_factory.get(my_url)
response = MyClasBasedView.as_view()(my_request)  # Replace with your view
response.render()
print(response)

To set the user of the request, do something like my_request.user = User.objects.get(id=123) before getting the response.

To send parameters to a class-based view, do something like response = MyClasBasedView.as_view()(my_request, parameter_1, parameter_2)

Extended Example

Here's an example of using RequestFactory with these things in combination

  • HTTP POST (to url url, functional view view, and a data dictionary post_data)

  • uploading a single file (path file_path, name file_name, and form field value file_key)

  • assigning a user to the request (user)

  • passing on kwargs dictionary from the url (url_kwargs)

SimpleUploadedFile helps format the file in a way that is valid for forms.

from django.core.files.uploadedfile import SimpleUploadedFile
from django.test import RequestFactory

request = RequestFactory().post(url, post_data)
with open(file_path, 'rb') as file_ptr:
    request.FILES[file_key] = SimpleUploadedFile(file_name, file_ptr.read())
    file_ptr.seek(0)  # resets the file pointer after the read
    if user:
        request.user = user
    response = view(request, **url_kwargs)

Using RequestFactory from a Python shell

RequestFactory names your server "testserver" by default, which can cause a problem if you're not using it inside test code. You'll see an error like:

DisallowedHost: Invalid HTTP_HOST header: 'testserver'. You may need to add 'testserver' to ALLOWED_HOSTS.

This workaround from @boatcoder's comment shows how to override the default server name to "localhost":

request_factory = RequestFactory(**{"SERVER_NAME": "localhost", "wsgi.url_scheme":"https"}).
Mark Chackerian
  • 21,866
  • 6
  • 108
  • 99
  • 7
    There are some drawbacks to the RequestFactory, namely that it names your server `testserver` which can then cause issues with `ALLOWED_HOSTS`, you can fix that though by initializing it like this `rf = RequestFactory(**{"SERVER_NAME": "localhost", "wsgi.url_scheme":"https"})`. – boatcoder Oct 18 '17 at 11:48
  • I tried to create a post request with RequestFactory (regarding a login page), how can I fix this error: Forbidden (CSRF cookie not set.) – Yannick Oct 28 '17 at 22:29
  • Try this `my_request._dont_enforce_csrf_checks = True` and let us know if that works. If it does I'll edit my response. – Mark Chackerian Oct 30 '17 at 14:32
23

How I simulate requests from the python command line is:

A simple way of simulating requests is:

>>> from django.urls import reverse
>>> import requests
>>> r = requests.get(reverse('app.views.your_view'))
>>> r.text
(prints output)
>>> r.status_code
200

Update: be sure to launch the django shell (via manage.py shell), not a classic python shell.

Update 2: For Django <1.10, change the first line to

from django.core.urlresolvers import reverse 
Mathieu Dhondt
  • 8,405
  • 5
  • 37
  • 58
  • 4
    I get this when I try out your method: `requests.exceptions.MissingSchema: Invalid URL '/': No schema supplied. Perhaps you meant http:///?` – Jonathan Aug 21 '14 at 08:55
  • 3
    You might want to prepend `"http://localhost:8000"` so that it reads `requests.get("http://192.168.0.1:8000" + reverse(...))` – Mathieu Dhondt Aug 21 '14 at 10:08
  • I am getting this error `[Errno 111] Connection refused` although I can connect with my browser. – mehmet May 03 '17 at 15:15
  • 2
    This isn't doing anything different then just requesting it via the command line with `wget` or `curl`. – Tim Tisdall May 10 '18 at 12:13
  • 1
    Use `django.urls` for django 1.10+. See here: https://docs.djangoproject.com/en/1.10/ref/urlresolvers/ – Rustam Guliev Aug 27 '18 at 17:17
  • the question was how to simulate requests, not how to create requests – Steve Yeago Oct 31 '19 at 06:33
  • I had trouble with users and sessions. I managed to ser up the request user, but the request session always fails and the template render doesn't go through. – dabadaba Nov 06 '19 at 10:44
5

(See tldr; down)

Its an old question, but just adding an answer, in case someone maybe interested.

Though this might not be the best(or lets say Django) way of doing things. but you can try doing this way.

Inside your django shell

>>> import requests
>>> r = requests.get('your_full_url_here')

Explanation: I omitted the reverse(), explanation being, since reverse() more or less, finds the url associated to a views.py function, you may omit the reverse() if you wish to, and put the whole url instead.

For example, if you have a friends app in your django project, and you want to see the list_all() (in views.py) function in the friends app, then you may do this.

TLDR;

>>> import requests
>>> url = 'http://localhost:8000/friends/list_all'
>>> r = requests.get(url)
Ashish
  • 6,001
  • 2
  • 18
  • 18