Una primera aplicación en Django

por lunes, septiembre 28, 2015 0 No tags Permalink 0

El primer concepto a tener en cuenta al crear una aplicación web en Django es el de vista (view). Una vista es una página web de la aplicación con una función específica y que emplea un determinado diseño.

En Django cada vista es representada por una función o método en Python. Dicha vista se selecciona a partir de la URL que se demande.

Primera página en Django

En primer lugar debemos acceder al archivo views.py donde escribiremos el siguiente código Python:

from django.http import HttpResponse

def index(request):
    return HttpResponse("Hello, world.")

De este modo hemos definido la página en Django. Ahora falta asociarla con una URL. Para ello creamos, dentro del directorio de la aplicación, un archivo urls.py que se incluirá el siguiente código:

from django.conf.urls import url

from . import views

urlpatterns = [
    url(r'^$', views.index, name='index'),
]

Y a continuación incluimos la raíz del módulo de la aplicación dentro de las URLs del sitio. Para ello editamos el archivo urls.py que se creó en su momento en la raíz del sitio (no confundir la raíz del sitio con la raíz de la aplicación). Dicho archivo incluirá el siguiente código:

from django.conf.urls import include, url
from django.contrib import admin

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

En la vista local http://127.0.0.1:8000/polls/ veremos ahora el texto “Hello, world.”.

Argumentos de la función url()

La función urls() tiene 4 argumentos:

  • regex
  • view
  • kwargs
  • name

Los dos primeros son obligatorios y los dos últimos opcionales.

El primer argumento regex es una abreviatura de regular expression, que son patrones sintácticos aplicados en este caso a las URL. En el caso de la URL  http://www.example.com/myapp/?page=3 Django buscará myapp/.

Una vez que Django encuentra una coincidencia con la expresión regular regex, entonces llama a la función especificada en el argumento view mediante el objeto HttpRequest.

El argumento  kwargs envía palabras clave (keywords) que son pasadas en un diccionario a la función especificada en view.

El último argumento name permite nombrar sin ambigüedad las URLs de modo que pueden establecerse cambios en el esquema de URLs del sitio manejando sólo un archivo.

Añadiendo más páginas o vistas

Para añadir nuevas vistas editamos el archivo views.py dentro de la aplicación:

def detail(request, question_id):
    return HttpResponse("You're looking at question %s." % question_id)

def results(request, question_id):
    response = "You're looking at the results of question %s."
    return HttpResponse(response % question_id)

def vote(request, question_id):
    return HttpResponse("You're voting on question %s." % question_id)

Las funciones detail, results y vote son las que definen las nuevas vistas, que a su vez deben incluirse en el archivo de URLs urls.py mediante llamadas de la función url():

from django.conf.urls import url

from . import views

urlpatterns = [
    # ex: /polls/
    url(r'^$', views.index, name='index'),
    # ex: /polls/5/
    url(r'^(?P[0-9]+)/$', views.detail, name='detail'),
    # ex: /polls/5/results/
    url(r'^(?P[0-9]+)/results/$', views.results, name='results'),
    # ex: /polls/5/vote/
    url(r'^(?P[0-9]+)/vote/$', views.vote, name='vote'),
]

Si llamamos a la siguiente URL en el navegador http://127.0.0.1:8000/polls/34/ Django buscará en urlpatterns la expresión regular que primero concuerde con dicha URL. En este caso será la llamada al método detail() que precisa del argumento question_id, el cual se pasa a través de la URL.

Cuando alguien solicita la página /polls/34/, Django carga el módulo mysite.urls, según lo establecido en el parámetro ROOT_URLCONF de la configuración. Allí encuentra la variable urlpatterns y en ella ‘^polls/’ como expresión regular asociada a include(‘polls.urls’) que hace que se procese a su vez el módulo polls.urls con la parte restante de la URL (“34/”), que concuerda con r’^(?P<question_id>[0-9]+)/$’, resultando en una llamada a la vista detail():

detail(request=<HttpRequest object>, question_id='34')

Vistas que realizan funciones

Una vista puede hacer cualquier cosa que deseemos usando Python. Puede extraer o escribir datos de una base de datos, generar un archivo PDF o ZIP o presentar información usando una plantilla.

En el post acerca de los primeros pasos en Django empleamos la base de datos que trae el sistema por defecto. A continuación modificaremos el código de la vista index en el archivo views.py para mostrar contenidos de esa base de datos, concretamente los 5 últimos resultados de la encuesta:

from django.http import HttpResponse

from .models import Question

def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    output = ', '.join([p.question_text for p in latest_question_list])
    return HttpResponse(output)

Por el momento no se ha añadido ningún tipo de diseño a la página. Mediante el sistema de plantillas de Django es posible separar el diseño del código Python. Para ello, un primer paso es crear un directorio de plantillas llamado templates en el directorio de la aplicación. Django buscará en dicho directorio por la plantilla que corresponda a una determinada vista. Dentro de este directorio crear otro que se llame polls y dentro de este un archivo llamado index.html de modo que la plantilla se encontrará en polls/templates/polls/index.html. Dentro de Django nos referiremos a esta plantilla como polls/index.html.

Dentro del archivo index.html incluiremos el siguiente código.

{% if latest_question_list %}
    <ul>
    {% for question in latest_question_list %}
        <li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
    {% endfor %}
    </ul>
{% else %}
    <p>No polls are available.</p>
{% endif %}

Ahora actualizamos la vista index en polls/views.py para usar la plantilla:

from django.http import HttpResponse
from django.template import RequestContext, loader

from .models import Question

def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    template = loader.get_template('polls/index.html')
    context = RequestContext(request, {
        'latest_question_list': latest_question_list,
    })
    return HttpResponse(template.render(context))

Este código carga la plantilla y le pasa un contexto, el cual es una diccionario que establece la relación entre los nombres de las variables en la plantilla con los objetos Python.

Si cargamos de nuevo la página, veremos que ahora aparece la estructura de lista que hemos indicado en la plantilla.

Aún puede simplificarse más mediante la función render() de Python, que evita importar loader y RequestContext; incluso no sería necesario importar HttpResponse si no se utilizase por otras funciones. Empleando render() la vista index() puede reescribirse del siguiente modo:

from django.shortcuts import render

from .models import Question

def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    context = {'latest_question_list': latest_question_list}
    return render(request, 'polls/index.html', context)

La función render() toma el objeto request como primer argumento, seguido del nombre de la plantilla como segundo argumento y un diccionario como tercer argumento, devolviendo un objeto HttpResponse de la plantilla especificada con el contexto indicado.

Así mismo, puede simplificarse la forma de escribir las URLs en el archivo index.html:

{% if latest_question_list %}
 <ul>
 {% for question in latest_question_list %}
 <li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
 {% endfor %}
 </ul>
{% else %}
 <p>No polls are available.</p>
{% endif %}

Como puede verse, se ha sustituido  href=“/polls/{{ question.id }}/”  por  href=”{% url ‘polls:detail’ question.id %}” empleando el argumento name para referirnos a la URL, de acuerdo a como está especificado en el archivo url.py.

Escribimos polls:detail para referirnos a la función detail que se encuentra en polls/urls.py distinta de la que podría encontrase en admin/urls.py. Ambas están especificadas en mysite/urls.py :

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

Incluir la página de error 404

La página muestra una serie de enlaces que nos llevan a la vista detail correspondiente. En caso de que por cualquier razón dicha vista no existiese, debería aparecer una nueva plantilla indicando que se ha producido un error 404 de página no encontrada. Para ello, debe modificarse la función asociada a la vista detail añadiendo el siguiente código:

from django.shortcuts import render
from django.http import HttpResponse
from django.http import Http404
from .models import Question

# ...

def detail(request, question_id):
    try:
        question = Question.objects.get(pk=question_id)
    except Question.DoesNotExist:
        raise Http404("Question does not exist")
    return render(request, 'polls/detail.html', {'question': question})

donde se ha cargado Http404 y se llama a la plantilla detail.html.

Existe otra forma de llamar a la plantilla 404 mediante el método get_object_or_404

from django.shortcuts import get_object_or_404, render

from .models import Question
# ...
def detail(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, 'polls/detail.html', {'question': question})

La función get_object_or_404() tiene como primer argumento el modelo de Django, seguido de un número arbitrario de argumentos o palabras clave, que son enviados a dicho modelo, dando lugar a una página 404 si el modelo no existe.

En cuanto a la plantilla detail.html puede ser un archivo con el siguiente contenido:

<h1>{{ question.question_text }}</h1>
<ul>{% for choice in question.choice_set.all %}
 <li>{{ choice.choice_text }}</li>
</ul>
{% endfor %}

La etiqueta h1 muestra el elemento question_text del objeto question. Django buscará si se trata de un diccionario, un atributo o una lista, en ese orden. En este caso se trata de un atributo, como podemos ver en la clase Question del archivo models.py.

En el bucle for llama a todos los elementos de choice que contienen el índice dado por question y presenta el valor de choice_text para cada uno de ellos.

 

Comments are closed.