1. =====================================
    
  2. Writing your first Django app, part 3
    
  3. =====================================
    
  4. 
    
  5. This tutorial begins where :doc:`Tutorial 2 </intro/tutorial02>` left off. We're
    
  6. continuing the web-poll application and will focus on creating the public
    
  7. interface -- "views."
    
  8. 
    
  9. .. admonition:: Where to get help:
    
  10. 
    
  11.     If you're having trouble going through this tutorial, please head over to
    
  12.     the :doc:`Getting Help</faq/help>` section of the FAQ.
    
  13. 
    
  14. Overview
    
  15. ========
    
  16. 
    
  17. A view is a "type" of web page in your Django application that generally serves
    
  18. a specific function and has a specific template. For example, in a blog
    
  19. application, you might have the following views:
    
  20. 
    
  21. * Blog homepage -- displays the latest few entries.
    
  22. 
    
  23. * Entry "detail" page -- permalink page for a single entry.
    
  24. 
    
  25. * Year-based archive page -- displays all months with entries in the
    
  26.   given year.
    
  27. 
    
  28. * Month-based archive page -- displays all days with entries in the
    
  29.   given month.
    
  30. 
    
  31. * Day-based archive page -- displays all entries in the given day.
    
  32. 
    
  33. * Comment action -- handles posting comments to a given entry.
    
  34. 
    
  35. In our poll application, we'll have the following four views:
    
  36. 
    
  37. * Question "index" page -- displays the latest few questions.
    
  38. 
    
  39. * Question "detail" page -- displays a question text, with no results but
    
  40.   with a form to vote.
    
  41. 
    
  42. * Question "results" page -- displays results for a particular question.
    
  43. 
    
  44. * Vote action -- handles voting for a particular choice in a particular
    
  45.   question.
    
  46. 
    
  47. In Django, web pages and other content are delivered by views. Each view is
    
  48. represented by a Python function (or method, in the case of class-based views).
    
  49. Django will choose a view by examining the URL that's requested (to be precise,
    
  50. the part of the URL after the domain name).
    
  51. 
    
  52. Now in your time on the web you may have come across such beauties as
    
  53. ``ME2/Sites/dirmod.htm?sid=&type=gen&mod=Core+Pages&gid=A6CD4967199A42D9B65B1B``.
    
  54. You will be pleased to know that Django allows us much more elegant
    
  55. *URL patterns* than that.
    
  56. 
    
  57. A URL pattern is the general form of a URL - for example:
    
  58. ``/newsarchive/<year>/<month>/``.
    
  59. 
    
  60. To get from a URL to a view, Django uses what are known as 'URLconfs'. A
    
  61. URLconf maps URL patterns to views.
    
  62. 
    
  63. This tutorial provides basic instruction in the use of URLconfs, and you can
    
  64. refer to :doc:`/topics/http/urls` for more information.
    
  65. 
    
  66. Writing more views
    
  67. ==================
    
  68. 
    
  69. Now let's add a few more views to ``polls/views.py``. These views are
    
  70. slightly different, because they take an argument:
    
  71. 
    
  72. .. code-block:: python
    
  73.     :caption: ``polls/views.py``
    
  74. 
    
  75.     def detail(request, question_id):
    
  76.         return HttpResponse("You're looking at question %s." % question_id)
    
  77. 
    
  78.     def results(request, question_id):
    
  79.         response = "You're looking at the results of question %s."
    
  80.         return HttpResponse(response % question_id)
    
  81. 
    
  82.     def vote(request, question_id):
    
  83.         return HttpResponse("You're voting on question %s." % question_id)
    
  84. 
    
  85. Wire these new views into the ``polls.urls`` module by adding the following
    
  86. :func:`~django.urls.path` calls:
    
  87. 
    
  88. .. code-block:: python
    
  89.     :caption: ``polls/urls.py``
    
  90. 
    
  91.     from django.urls import path
    
  92. 
    
  93.     from . import views
    
  94. 
    
  95.     urlpatterns = [
    
  96.         # ex: /polls/
    
  97.         path('', views.index, name='index'),
    
  98.         # ex: /polls/5/
    
  99.         path('<int:question_id>/', views.detail, name='detail'),
    
  100.         # ex: /polls/5/results/
    
  101.         path('<int:question_id>/results/', views.results, name='results'),
    
  102.         # ex: /polls/5/vote/
    
  103.         path('<int:question_id>/vote/', views.vote, name='vote'),
    
  104.     ]
    
  105. 
    
  106. Take a look in your browser, at "/polls/34/". It'll run the ``detail()``
    
  107. method and display whatever ID you provide in the URL. Try
    
  108. "/polls/34/results/" and "/polls/34/vote/" too -- these will display the
    
  109. placeholder results and voting pages.
    
  110. 
    
  111. When somebody requests a page from your website -- say, "/polls/34/", Django
    
  112. will load the ``mysite.urls`` Python module because it's pointed to by the
    
  113. :setting:`ROOT_URLCONF` setting. It finds the variable named ``urlpatterns``
    
  114. and traverses the patterns in order. After finding the match at ``'polls/'``,
    
  115. it strips off the matching text (``"polls/"``) and sends the remaining text --
    
  116. ``"34/"`` -- to the 'polls.urls' URLconf for further processing. There it
    
  117. matches ``'<int:question_id>/'``, resulting in a call to the ``detail()`` view
    
  118. like so::
    
  119. 
    
  120.     detail(request=<HttpRequest object>, question_id=34)
    
  121. 
    
  122. The ``question_id=34`` part comes from ``<int:question_id>``. Using angle
    
  123. brackets "captures" part of the URL and sends it as a keyword argument to the
    
  124. view function. The ``question_id`` part of the string defines the name that
    
  125. will be used to identify the matched pattern, and the ``int`` part is a
    
  126. converter that determines what patterns should match this part of the URL path.
    
  127. The colon (``:``) separates the converter and pattern name.
    
  128. 
    
  129. Write views that actually do something
    
  130. ======================================
    
  131. 
    
  132. Each view is responsible for doing one of two things: returning an
    
  133. :class:`~django.http.HttpResponse` object containing the content for the
    
  134. requested page, or raising an exception such as :exc:`~django.http.Http404`. The
    
  135. rest is up to you.
    
  136. 
    
  137. Your view can read records from a database, or not. It can use a template
    
  138. system such as Django's -- or a third-party Python template system -- or not.
    
  139. It can generate a PDF file, output XML, create a ZIP file on the fly, anything
    
  140. you want, using whatever Python libraries you want.
    
  141. 
    
  142. All Django wants is that :class:`~django.http.HttpResponse`. Or an exception.
    
  143. 
    
  144. Because it's convenient, let's use Django's own database API, which we covered
    
  145. in :doc:`Tutorial 2 </intro/tutorial02>`. Here's one stab at a new ``index()``
    
  146. view, which displays the latest 5 poll questions in the system, separated by
    
  147. commas, according to publication date:
    
  148. 
    
  149. .. code-block:: python
    
  150.     :caption: ``polls/views.py``
    
  151. 
    
  152.     from django.http import HttpResponse
    
  153. 
    
  154.     from .models import Question
    
  155. 
    
  156. 
    
  157.     def index(request):
    
  158.         latest_question_list = Question.objects.order_by('-pub_date')[:5]
    
  159.         output = ', '.join([q.question_text for q in latest_question_list])
    
  160.         return HttpResponse(output)
    
  161. 
    
  162.     # Leave the rest of the views (detail, results, vote) unchanged
    
  163. 
    
  164. There's a problem here, though: the page's design is hard-coded in the view. If
    
  165. you want to change the way the page looks, you'll have to edit this Python code.
    
  166. So let's use Django's template system to separate the design from Python by
    
  167. creating a template that the view can use.
    
  168. 
    
  169. First, create a directory called ``templates`` in your ``polls`` directory.
    
  170. Django will look for templates in there.
    
  171. 
    
  172. Your project's :setting:`TEMPLATES` setting describes how Django will load and
    
  173. render templates. The default settings file configures a ``DjangoTemplates``
    
  174. backend whose :setting:`APP_DIRS <TEMPLATES-APP_DIRS>` option is set to
    
  175. ``True``. By convention ``DjangoTemplates`` looks for a "templates"
    
  176. subdirectory in each of the :setting:`INSTALLED_APPS`.
    
  177. 
    
  178. Within the ``templates`` directory you have just created, create another
    
  179. directory called ``polls``, and within that create a file called
    
  180. ``index.html``. In other words, your template should be at
    
  181. ``polls/templates/polls/index.html``. Because of how the ``app_directories``
    
  182. template loader works as described above, you can refer to this template within
    
  183. Django as ``polls/index.html``.
    
  184. 
    
  185. .. admonition:: Template namespacing
    
  186. 
    
  187.     Now we *might* be able to get away with putting our templates directly in
    
  188.     ``polls/templates`` (rather than creating another ``polls`` subdirectory),
    
  189.     but it would actually be a bad idea. Django will choose the first template
    
  190.     it finds whose name matches, and if you had a template with the same name
    
  191.     in a *different* application, Django would be unable to distinguish between
    
  192.     them. We need to be able to point Django at the right one, and the best
    
  193.     way to ensure this is by *namespacing* them. That is, by putting those
    
  194.     templates inside *another* directory named for the application itself.
    
  195. 
    
  196. Put the following code in that template:
    
  197. 
    
  198. .. code-block:: html+django
    
  199.     :caption: ``polls/templates/polls/index.html``
    
  200. 
    
  201.     {% if latest_question_list %}
    
  202.         <ul>
    
  203.         {% for question in latest_question_list %}
    
  204.             <li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
    
  205.         {% endfor %}
    
  206.         </ul>
    
  207.     {% else %}
    
  208.         <p>No polls are available.</p>
    
  209.     {% endif %}
    
  210. 
    
  211. .. note::
    
  212. 
    
  213.     To make the tutorial shorter, all template examples use incomplete HTML. In
    
  214.     your own projects you should use `complete HTML documents`__.
    
  215. 
    
  216. __ https://developer.mozilla.org/en-US/docs/Learn/HTML/Introduction_to_HTML/Getting_started#anatomy_of_an_html_document
    
  217. 
    
  218. Now let's update our ``index`` view in ``polls/views.py`` to use the template:
    
  219. 
    
  220. .. code-block:: python
    
  221.     :caption: ``polls/views.py``
    
  222. 
    
  223.     from django.http import HttpResponse
    
  224.     from django.template import loader
    
  225. 
    
  226.     from .models import Question
    
  227. 
    
  228. 
    
  229.     def index(request):
    
  230.         latest_question_list = Question.objects.order_by('-pub_date')[:5]
    
  231.         template = loader.get_template('polls/index.html')
    
  232.         context = {
    
  233.             'latest_question_list': latest_question_list,
    
  234.         }
    
  235.         return HttpResponse(template.render(context, request))
    
  236. 
    
  237. That code loads the template called  ``polls/index.html`` and passes it a
    
  238. context. The context is a dictionary mapping template variable names to Python
    
  239. objects.
    
  240. 
    
  241. Load the page by pointing your browser at "/polls/", and you should see a
    
  242. bulleted-list containing the "What's up" question from :doc:`Tutorial 2
    
  243. </intro/tutorial02>`. The link points to the question's detail page.
    
  244. 
    
  245. A shortcut: :func:`~django.shortcuts.render`
    
  246. --------------------------------------------
    
  247. 
    
  248. It's a very common idiom to load a template, fill a context and return an
    
  249. :class:`~django.http.HttpResponse` object with the result of the rendered
    
  250. template. Django provides a shortcut. Here's the full ``index()`` view,
    
  251. rewritten:
    
  252. 
    
  253. .. code-block:: python
    
  254.     :caption: ``polls/views.py``
    
  255. 
    
  256.     from django.shortcuts import render
    
  257. 
    
  258.     from .models import Question
    
  259. 
    
  260. 
    
  261.     def index(request):
    
  262.         latest_question_list = Question.objects.order_by('-pub_date')[:5]
    
  263.         context = {'latest_question_list': latest_question_list}
    
  264.         return render(request, 'polls/index.html', context)
    
  265. 
    
  266. Note that once we've done this in all these views, we no longer need to import
    
  267. :mod:`~django.template.loader` and :class:`~django.http.HttpResponse` (you'll
    
  268. want to keep ``HttpResponse`` if you still have the stub methods for ``detail``,
    
  269. ``results``, and ``vote``).
    
  270. 
    
  271. The :func:`~django.shortcuts.render` function takes the request object as its
    
  272. first argument, a template name as its second argument and a dictionary as its
    
  273. optional third argument. It returns an :class:`~django.http.HttpResponse`
    
  274. object of the given template rendered with the given context.
    
  275. 
    
  276. Raising a 404 error
    
  277. ===================
    
  278. 
    
  279. Now, let's tackle the question detail view -- the page that displays the question text
    
  280. for a given poll. Here's the view:
    
  281. 
    
  282. .. code-block:: python
    
  283.     :caption: ``polls/views.py``
    
  284. 
    
  285.     from django.http import Http404
    
  286.     from django.shortcuts import render
    
  287. 
    
  288.     from .models import Question
    
  289.     # ...
    
  290.     def detail(request, question_id):
    
  291.         try:
    
  292.             question = Question.objects.get(pk=question_id)
    
  293.         except Question.DoesNotExist:
    
  294.             raise Http404("Question does not exist")
    
  295.         return render(request, 'polls/detail.html', {'question': question})
    
  296. 
    
  297. The new concept here: The view raises the :exc:`~django.http.Http404` exception
    
  298. if a question with the requested ID doesn't exist.
    
  299. 
    
  300. We'll discuss what you could put in that ``polls/detail.html`` template a bit
    
  301. later, but if you'd like to quickly get the above example working, a file
    
  302. containing just:
    
  303. 
    
  304. .. code-block:: html+django
    
  305.     :caption: ``polls/templates/polls/detail.html``
    
  306. 
    
  307.     {{ question }}
    
  308. 
    
  309. will get you started for now.
    
  310. 
    
  311. A shortcut: :func:`~django.shortcuts.get_object_or_404`
    
  312. -------------------------------------------------------
    
  313. 
    
  314. It's a very common idiom to use :meth:`~django.db.models.query.QuerySet.get`
    
  315. and raise :exc:`~django.http.Http404` if the object doesn't exist. Django
    
  316. provides a shortcut. Here's the ``detail()`` view, rewritten:
    
  317. 
    
  318. .. code-block:: python
    
  319.     :caption: ``polls/views.py``
    
  320. 
    
  321.     from django.shortcuts import get_object_or_404, render
    
  322. 
    
  323.     from .models import Question
    
  324.     # ...
    
  325.     def detail(request, question_id):
    
  326.         question = get_object_or_404(Question, pk=question_id)
    
  327.         return render(request, 'polls/detail.html', {'question': question})
    
  328. 
    
  329. The :func:`~django.shortcuts.get_object_or_404` function takes a Django model
    
  330. as its first argument and an arbitrary number of keyword arguments, which it
    
  331. passes to the :meth:`~django.db.models.query.QuerySet.get` function of the
    
  332. model's manager. It raises :exc:`~django.http.Http404` if the object doesn't
    
  333. exist.
    
  334. 
    
  335. .. admonition:: Philosophy
    
  336. 
    
  337.     Why do we use a helper function :func:`~django.shortcuts.get_object_or_404`
    
  338.     instead of automatically catching the
    
  339.     :exc:`~django.core.exceptions.ObjectDoesNotExist` exceptions at a higher
    
  340.     level, or having the model API raise :exc:`~django.http.Http404` instead of
    
  341.     :exc:`~django.core.exceptions.ObjectDoesNotExist`?
    
  342. 
    
  343.     Because that would couple the model layer to the view layer. One of the
    
  344.     foremost design goals of Django is to maintain loose coupling. Some
    
  345.     controlled coupling is introduced in the :mod:`django.shortcuts` module.
    
  346. 
    
  347. There's also a :func:`~django.shortcuts.get_list_or_404` function, which works
    
  348. just as :func:`~django.shortcuts.get_object_or_404` -- except using
    
  349. :meth:`~django.db.models.query.QuerySet.filter` instead of
    
  350. :meth:`~django.db.models.query.QuerySet.get`. It raises
    
  351. :exc:`~django.http.Http404` if the list is empty.
    
  352. 
    
  353. Use the template system
    
  354. =======================
    
  355. 
    
  356. Back to the ``detail()`` view for our poll application. Given the context
    
  357. variable ``question``, here's what the ``polls/detail.html`` template might look
    
  358. like:
    
  359. 
    
  360. .. code-block:: html+django
    
  361.     :caption: ``polls/templates/polls/detail.html``
    
  362. 
    
  363.     <h1>{{ question.question_text }}</h1>
    
  364.     <ul>
    
  365.     {% for choice in question.choice_set.all %}
    
  366.         <li>{{ choice.choice_text }}</li>
    
  367.     {% endfor %}
    
  368.     </ul>
    
  369. 
    
  370. The template system uses dot-lookup syntax to access variable attributes. In
    
  371. the example of ``{{ question.question_text }}``, first Django does a dictionary lookup
    
  372. on the object ``question``. Failing that, it tries an attribute lookup -- which
    
  373. works, in this case. If attribute lookup had failed, it would've tried a
    
  374. list-index lookup.
    
  375. 
    
  376. Method-calling happens in the :ttag:`{% for %}<for>` loop:
    
  377. ``question.choice_set.all`` is interpreted as the Python code
    
  378. ``question.choice_set.all()``, which returns an iterable of ``Choice`` objects and is
    
  379. suitable for use in the :ttag:`{% for %}<for>` tag.
    
  380. 
    
  381. See the :doc:`template guide </topics/templates>` for more about templates.
    
  382. 
    
  383. Removing hardcoded URLs in templates
    
  384. ====================================
    
  385. 
    
  386. Remember, when we wrote the link to a question in the ``polls/index.html``
    
  387. template, the link was partially hardcoded like this:
    
  388. 
    
  389. .. code-block:: html+django
    
  390. 
    
  391.     <li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
    
  392. 
    
  393. The problem with this hardcoded, tightly-coupled approach is that it becomes
    
  394. challenging to change URLs on projects with a lot of templates. However, since
    
  395. you defined the name argument in the :func:`~django.urls.path` functions in
    
  396. the ``polls.urls`` module, you can remove a reliance on specific URL paths
    
  397. defined in your url configurations by using the ``{% url %}`` template tag:
    
  398. 
    
  399. .. code-block:: html+django
    
  400. 
    
  401.     <li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
    
  402. 
    
  403. The way this works is by looking up the URL definition as specified in the
    
  404. ``polls.urls`` module. You can see exactly where the URL name of 'detail' is
    
  405. defined below::
    
  406. 
    
  407.     ...
    
  408.     # the 'name' value as called by the {% url %} template tag
    
  409.     path('<int:question_id>/', views.detail, name='detail'),
    
  410.     ...
    
  411. 
    
  412. If you want to change the URL of the polls detail view to something else,
    
  413. perhaps to something like ``polls/specifics/12/`` instead of doing it in the
    
  414. template (or templates) you would change it in ``polls/urls.py``::
    
  415. 
    
  416.     ...
    
  417.     # added the word 'specifics'
    
  418.     path('specifics/<int:question_id>/', views.detail, name='detail'),
    
  419.     ...
    
  420. 
    
  421. Namespacing URL names
    
  422. =====================
    
  423. 
    
  424. The tutorial project has just one app, ``polls``. In real Django projects,
    
  425. there might be five, ten, twenty apps or more. How does Django differentiate
    
  426. the URL names between them? For example, the ``polls`` app has a ``detail``
    
  427. view, and so might an app on the same project that is for a blog. How does one
    
  428. make it so that Django knows which app view to create for a url when using the
    
  429. ``{% url %}`` template tag?
    
  430. 
    
  431. The answer is to add namespaces to your  URLconf. In the ``polls/urls.py``
    
  432. file, go ahead and add an ``app_name`` to set the application namespace:
    
  433. 
    
  434. .. code-block:: python
    
  435.     :caption: ``polls/urls.py``
    
  436. 
    
  437.     from django.urls import path
    
  438. 
    
  439.     from . import views
    
  440. 
    
  441.     app_name = 'polls'
    
  442.     urlpatterns = [
    
  443.         path('', views.index, name='index'),
    
  444.         path('<int:question_id>/', views.detail, name='detail'),
    
  445.         path('<int:question_id>/results/', views.results, name='results'),
    
  446.         path('<int:question_id>/vote/', views.vote, name='vote'),
    
  447.     ]
    
  448. 
    
  449. Now change your ``polls/index.html`` template from:
    
  450. 
    
  451. .. code-block:: html+django
    
  452.     :caption: ``polls/templates/polls/index.html``
    
  453. 
    
  454.     <li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
    
  455. 
    
  456. to point at the namespaced detail view:
    
  457. 
    
  458. .. code-block:: html+django
    
  459.     :caption: ``polls/templates/polls/index.html``
    
  460. 
    
  461.     <li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
    
  462. 
    
  463. When you're comfortable with writing views, read :doc:`part 4 of this tutorial
    
  464. </intro/tutorial04>` to learn the basics about form processing and generic
    
  465. views.