123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232 |
- =============
- Writing views
- =============
- A view function, or *view* for short, is a Python function that takes a
- web request and returns a web response. This response can be the HTML contents
- of a web page, or a redirect, or a 404 error, or an XML document, or an image .
- . . or anything, really. The view itself contains whatever arbitrary logic is
- necessary to return that response. This code can live anywhere you want, as long
- as it's on your Python path. There's no other requirement--no "magic," so to
- speak. For the sake of putting the code *somewhere*, the convention is to
- put views in a file called ``views.py``, placed in your project or
- application directory.
- A simple view
- =============
- Here's a view that returns the current date and time, as an HTML document::
- from django.http import HttpResponse
- import datetime
- def current_datetime(request):
- now = datetime.datetime.now()
- html = '<html lang="en"><body>It is now %s.</body></html>' % now
- return HttpResponse(html)
- Let's step through this code one line at a time:
- * First, we import the class :class:`~django.http.HttpResponse` from the
- :mod:`django.http` module, along with Python's ``datetime`` library.
- * Next, we define a function called ``current_datetime``. This is the view
- function. Each view function takes an :class:`~django.http.HttpRequest`
- object as its first parameter, which is typically named ``request``.
- Note that the name of the view function doesn't matter; it doesn't have to
- be named in a certain way in order for Django to recognize it. We're
- calling it ``current_datetime`` here, because that name clearly indicates
- what it does.
- * The view returns an :class:`~django.http.HttpResponse` object that
- contains the generated response. Each view function is responsible for
- returning an :class:`~django.http.HttpResponse` object. (There are
- exceptions, but we'll get to those later.)
- .. admonition:: Django's Time Zone
- Django includes a :setting:`TIME_ZONE` setting that defaults to
- ``America/Chicago``. This probably isn't where you live, so you might want
- to change it in your settings file.
- Mapping URLs to views
- =====================
- So, to recap, this view function returns an HTML page that includes the current
- date and time. To display this view at a particular URL, you'll need to create a
- *URLconf*; see :doc:`/topics/http/urls` for instructions.
- Returning errors
- ================
- Django provides help for returning HTTP error codes. There are subclasses of
- :class:`~django.http.HttpResponse` for a number of common HTTP status codes
- other than 200 (which means *"OK"*). You can find the full list of available
- subclasses in the :ref:`request/response <ref-httpresponse-subclasses>`
- documentation. Return an instance of one of those subclasses instead of a
- normal :class:`~django.http.HttpResponse` in order to signify an error. For
- example::
- from django.http import HttpResponse, HttpResponseNotFound
- def my_view(request):
- # ...
- if foo:
- return HttpResponseNotFound("<h1>Page not found</h1>")
- else:
- return HttpResponse("<h1>Page was found</h1>")
- There isn't a specialized subclass for every possible HTTP response code,
- since many of them aren't going to be that common. However, as documented in
- the :class:`~django.http.HttpResponse` documentation, you can also pass the
- HTTP status code into the constructor for :class:`~django.http.HttpResponse`
- to create a return class for any status code you like. For example::
- from django.http import HttpResponse
- def my_view(request):
- # ...
- # Return a "created" (201) response code.
- return HttpResponse(status=201)
- Because 404 errors are by far the most common HTTP error, there's an easier way
- to handle those errors.
- The ``Http404`` exception
- -------------------------
- .. class:: django.http.Http404()
- When you return an error such as :class:`~django.http.HttpResponseNotFound`,
- you're responsible for defining the HTML of the resulting error page::
- return HttpResponseNotFound("<h1>Page not found</h1>")
- For convenience, and because it's a good idea to have a consistent 404 error page
- across your site, Django provides an ``Http404`` exception. If you raise
- ``Http404`` at any point in a view function, Django will catch it and return the
- standard error page for your application, along with an HTTP error code 404.
- Example usage::
- from django.http import Http404
- from django.shortcuts import render
- from polls.models import Poll
- def detail(request, poll_id):
- try:
- p = Poll.objects.get(pk=poll_id)
- except Poll.DoesNotExist:
- raise Http404("Poll does not exist")
- return render(request, "polls/detail.html", {"poll": p})
- In order to show customized HTML when Django returns a 404, you can create an
- HTML template named ``404.html`` and place it in the top level of your
- template tree. This template will then be served when :setting:`DEBUG` is set
- to ``False``.
- When :setting:`DEBUG` is ``True``, you can provide a message to ``Http404`` and
- it will appear in the standard 404 debug template. Use these messages for
- debugging purposes; they generally aren't suitable for use in a production 404
- template.
- .. _customizing-error-views:
- Customizing error views
- =======================
- The default error views in Django should suffice for most web applications,
- but can easily be overridden if you need any custom behavior. Specify the
- handlers as seen below in your URLconf (setting them anywhere else will have no
- effect).
- The :func:`~django.views.defaults.page_not_found` view is overridden by
- :data:`~django.conf.urls.handler404`::
- handler404 = "mysite.views.my_custom_page_not_found_view"
- The :func:`~django.views.defaults.server_error` view is overridden by
- :data:`~django.conf.urls.handler500`::
- handler500 = "mysite.views.my_custom_error_view"
- The :func:`~django.views.defaults.permission_denied` view is overridden by
- :data:`~django.conf.urls.handler403`::
- handler403 = "mysite.views.my_custom_permission_denied_view"
- The :func:`~django.views.defaults.bad_request` view is overridden by
- :data:`~django.conf.urls.handler400`::
- handler400 = "mysite.views.my_custom_bad_request_view"
- .. seealso::
- Use the :setting:`CSRF_FAILURE_VIEW` setting to override the CSRF error
- view.
- Testing custom error views
- --------------------------
- To test the response of a custom error handler, raise the appropriate exception
- in a test view. For example::
- from django.core.exceptions import PermissionDenied
- from django.http import HttpResponse
- from django.test import SimpleTestCase, override_settings
- from django.urls import path
- def response_error_handler(request, exception=None):
- return HttpResponse("Error handler content", status=403)
- def permission_denied_view(request):
- raise PermissionDenied
- urlpatterns = [
- path("403/", permission_denied_view),
- ]
- handler403 = response_error_handler
- # ROOT_URLCONF must specify the module that contains handler403 = ...
- @override_settings(ROOT_URLCONF=__name__)
- class CustomErrorHandlerTests(SimpleTestCase):
- def test_handler_renders_template_response(self):
- response = self.client.get("/403/")
- # Make assertions on the response here. For example:
- self.assertContains(response, "Error handler content", status_code=403)
- .. _async-views:
- Async views
- ===========
- As well as being synchronous functions, views can also be asynchronous
- ("async") functions, normally defined using Python's ``async def`` syntax.
- Django will automatically detect these and run them in an async context.
- However, you will need to use an async server based on ASGI to get their
- performance benefits.
- Here's an example of an async view::
- import datetime
- from django.http import HttpResponse
- async def current_datetime(request):
- now = datetime.datetime.now()
- html = '<html lang="en"><body>It is now %s.</body></html>' % now
- return HttpResponse(html)
- You can read more about Django's async support, and how to best use async
- views, in :doc:`/topics/async`.
|