8. URLs y vistas

Ahora puedes comenzar a crear la interfaz de la aplicación marcador. En este capítulo se crearán dos URLs y sus correspondientes vistas. La primera es una simple vista de una lista de todos los marcadores públicos, la segunda es una lista de todos marcadores de una persona en particular.

8.1. Configurar los URLs

“A clean, elegant URL scheme is an important detail in a high-quality Web application. Django lets you design URLs however you want, with no framework limitations.”

URL dispatcher | Django documentation

So that the views can be used, paths must be associated with them. In Django, this is done explicitly with a URL configration (URLconf). We’ll create a new URLconf just for the marcador app, so that later on the project will have a cleaner structure.

8.1.1. Add URLs to the application

Create a new file named urls.py in the directory mysite/marcador and paste in the following lines:

1
2
3
4
5
6
7
8
from django.conf.urls import url


urlpatterns = [
    url(r'^user/(?P<username>[-\w]+)/$', 'marcador.views.bookmark_user',
        name='marcador_bookmark_user'),
    url(r'^$', 'marcador.views.bookmark_list', name='marcador_bookmark_list'),
]

Every URLconf must define a variable urlpatterns which is a list of URLs. Each URL will be created with the url() function. In our example url() is given three parameters: the path, the names of the views, and an identifier (name), with which you can refer to this URL later. In the documentation you’ll find a list of further parameters.

With Django the paths are defined as Regular Expressions (regexes). Regexes are a powerful tool with which to describe strings of characters by means of syntactic rules. You can find a detailed introduction to this theme at regular-expressions.info. The Online regex tester and debugger can help you to craft regular expressions for new URLs.

La expresión regular de la vista de lista, r'^$', consiste de los caracteres de control ^ (el principio de la cadena) y $ (el final de la cadena). Cuando nada está entre estos dos caracteres, estos describen una cadena vacía. Esto significa que la vista es accesible desde la ruta /, la página principal.

La expresión regular para la vista de marcadores de un usuario en particular en un poco más compleja: r'^user/(?P<username>[-\w]+)/$'. Contiene una parte variable que permite que los nombres de usuario sean filtrados. Así, con /user/alice/``se pueden solicitar los marcadores de Alice y con ``/user/bob/, los marcadores de Bob.

Las expresiones regulares se componen de la siguiente manera: después del carácter de control ^ viene user/, una parte estática que debe coincidir exactamente. Luego sigue un grupo (?P<username>[-\w]+). Esto significa se puede acceder a todas las letras, todos los números, guiones y barras bajas (definidos por [-\w]+) usando la variable username. Django se asegura de que todos los grupos se pasen a la vista como argumentos.

8.1.2. Add URLs to the project

So that the URLconf for our app can be found, it must be referenced in the root URLconf. So paste the emphasized line into the file mysite/mysite/urls.py:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
"""mysite URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/1.8/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  url(r'^$', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  url(r'^$', Home.as_view(), name='home')
Including another URLconf
    1. Add an import:  from blog import urls as blog_urls
    2. Add a URL to urlpatterns:  url(r'^blog/', include(blog_urls))
"""
from django.conf.urls import include, url
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', include(admin.site.urls)),
    url(r'^', include('marcador.urls')),
]

8.1.3. Test the new URLs

Ahora puedes visitar ambos URLs en tu navegador:

Para ambos URLs una excepción ``ViewDoesNotExist``será elevada. Esto significa que los URLs fueron resueltos correctamente, pero las vistas asociadas no existen.

8.2. Añadir las vistas

“A view function, or view for short, is simply a Python function that takes a Web request and returns a Web response.”

Writing views | Django documentation

El siguiente paso es añadir las vistas. En Django una vista es una función que recibe un objeto request y retorna un objeto response. La función contiene la lógica que determina el contenido de la respuesta.

8.2.1. A view to display all bookmarks

La primera vista da una lista de los marcadores públicos. Abre el archivo views.py en el directorio mysite/marcador/ y pega el siguiente código.

1
2
3
4
5
6
7
8
9
from django.shortcuts import get_object_or_404, redirect, render

from .models import Bookmark


def bookmark_list(request):
    bookmarks = Bookmark.public.all()
    context = {'bookmarks': bookmarks}
    return render(request, 'marcador/bookmark_list.html', context)

First of all the database query for all public bookmarks is be generated by means of the ORM, and assigned to the variable bookmarks. This is then in turn stored in the dictionary context. All keys in this dictionary will later be available to the templates as variables. Finally the function render() generates the response object from the request, the path to the template and the dictionary. render() is a shortcut, that carries out several steps in one. You’ll need the shortcuts get_object_or_404() and redirect() for the following views. You can find out what exactly the shortcuts do in the documentation.

8.2.2. A view for each user

La segunda vista muestra todos los marcados pública de una usuario en particular. La nuevas líneas que tienes que pegar están destacadas:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
from django.contrib.auth.models import User
from django.shortcuts import get_object_or_404, redirect, render

from .models import Bookmark


def bookmark_list(request):
    bookmarks = Bookmark.public.all()
    context = {'bookmarks': bookmarks}
    return render(request, 'marcador/bookmark_list.html', context)


def bookmark_user(request, username):
    user = get_object_or_404(User, username=username)
    if request.user == user:
        bookmarks = user.bookmarks.all()
    else:
        bookmarks = Bookmark.public.filter(owner__username=username)
    context = {'bookmarks': bookmarks, 'owner': user}
    return render(request, 'marcador/bookmark_user.html', context)

It has an additional argument username, with which the requested user in the database is sought. This contains the same variable as in the regex of the URLconf. If the user is not found, the HTTP status code 404 will be returned automatically. Whether the user exists and whether he or she is the logged-in user is checked with the help of the relationship, defined by the field Bookmark.owner with the model User, and the bookmarks loaded. If the view for another user is invoked, the public bookmarks are filtered with the help of the argument username. Bookmarks and user are then added to the context dictionary, so that they can be accessed in the template later.

8.2.3. Test the new views

Ahora puedes probar ambos URLs:

Of course both currently produce a TemplateDoesNotExist exception, because the templates do not yet exist. Notice that the second URL will only work if you have an user named “admin”. Otherwise use the username of the superuser you created when you ran the createsuperuser command.