xpanel, lxde, linux, fbpanel opensuse, linux, instalacion gtk3, gnome, ambienti grafici PS3, mandos, Play Station, Linux edubuntu, ubuntu, linux fedora, linux, discapacitados visuales fuduntu, fedora, ubuntu, linux, distribuciones inkscape, grafica, linux, editor tux, tuz, iconos, mascota, linux artistx, distro, linux, artistas, graficos

Guía de Django, el web framework para perfeccionistas (7a parte).

Vistas y URLconfs avanzadas.

Trucos URLconf.

No hay nada “especial” en URLconfs – como todo en Django, sólo código Python.

Racionalizar las importaciones de funciones

Considerar la siguiente URLconf, basada en un ejemplo visto anteriormente:

from django.conf.urls.defaults import *
from mysite.views import hello, current_datetime, hours_ahead

urlpatterns = patterns(”,

(r’^hello/$’, hello),
(r’^time/$’, current_datetime),
(r’^time/plus/(d{1,2})/$’, hours_ahead),

)

django_slide_4

Cada entrada en la URLconf incluye su vista asociada, pasada directamente como un objeto de función. Esto significa que es necesario importar la vista al prinicipio del módulo.

Pero, cuando una aplicación de Django crece en complejidad, su URLconf también crece, y gestionar esas importaciones puede ser tedioso. (Para cada nueva vista, usted tendría que recordar importarla, y la declaración de importación tiende a ser demasiado larga si se utiliza este enfoque.) Es posible evitar esto con la importación de propio módulo views. Este ejemplo de URLconf es equivalente al anterior:

from django.conf.urls.defaults import *
from mysite import views

urlpatterns = patterns(”,

(r’^hello/$’, views.hello),
(r’^time/$’, views.current_datetime),
(r’^time/plus/(d{1,2})/$’, views.hours_ahead),

)

Django ofrece otra manera de especificar la vista de un patrón particular en el URLconf: se puede pasar una cadena que contenga el nombre del módulo y el nombre de la vista en lugar del objeto de función en sí mismo. Continuando con el ejemplo en curso:

from django.conf.urls.defaults import *

urlpatterns = patterns(”,

(r’^hello/$’, ‘mysite.views.hello’),
(r’^time/$’, ‘mysite.views.current_datetime’),
(r’^time/plus/(d{1,2})/$’, ‘mysite.views.hours_ahead’),

)

Notar las comillas que delimitan los nombres de las vistas.

 

Usando esta técnica, ya no es necesario importar las vistas; Django automáticamente importa la función de vista apropiada la primera vez que se necesita, según la cadena que describe el nombre y la ruta de la función de vista.

 

Una abreviatura más que usted puede usar cuando utiliza esta técnica es factorizar un “prefijo de vista” común. En nuestro ejemplo de URLconf, cada una de las cadenas de vista comienza con ‘mysite.views’. Podemos factorizar este prefijo común y pasarlo como el primer argumento a los patrones, así:

from django.conf.urls.defaults import *
urlpatterns = patterns(‘mysite.views’,

(r’^hello/$’, ‘hello’),
(r’^time/$’, ‘current_datetime’),
(r’^time/plus/(d{1,2})/$’, ‘hours_ahead’),

)

Con estos dos enfoques en mente, ¿cuál es mejor? Realmente depende de su estilo personal de programación y de sus necesidades.

 

Las ventajas de la cadena son las siguientes:

  • Es más compacto, porque no es necesario importar las funciones de vista.
  • El resultado es URLconfs más legibles y manejables si las funciones de su vista se reparten entre diferentes módulos de Python.

Las ventajas del enfoque de objeto de función son las siguientes:

  • Se permite un fácil “ajuste” de las funciones de vista.
  • Es más acorde con las tradiciones de Python, como pasar funciones como objetos.

Ambos enfoques son válidos, e incluso se pueden mezclar en el mismo URLconf. La elección es suya.

DjangoArchitecture-JeffCroftUsar varios Prefijos de Vista.

En la práctica, si utiliza la técnica de cadena, probablemente acabe mezclando vistas que no tienen un prefijo común. Sin embargo, usted aún puede usar la abreviatura del prefijo de vistas para eliminar la duplicación. Sólo tiene que añadir varios objetos patterns() juntos, así:

from django.conf.urls.defaults import *

urlpatterns = patterns(‘mysite.views’,

(r’^hello/$’, ‘hello’),
(r’^time/$’, ‘current_datetime’),
(r’^time/plus/(d{1,2})/$’, ‘hours_ahead’),

)

urlpatterns += patterns(‘weblog.views’,

(r’^tag/(w+)/$’, ‘tag’),

)

 

URLs especiales en modo Debug.

Hablando de la construcción de urlpatterns de forma dinámica, es posible que desee tomar ventaja de esta técnica alterando el comportamiento de su URLconf en el modo de depuración de Django. Para ello, basta con comprobar el valor de la propiedad DEBUG en tiempo de ejecución, así:

from django.conf import settings
from django.conf.urls.defaults import *
from mysite import views

urlpatterns = patterns(”,

(r’^$’, views.homepage),
(r’^(d{4})/([a-z]{3})/$’, views.archive_month),

)

if settings.DEBUG:

urlpatterns += patterns(”,

(r’^debuginfo/$’, views.debug),

)

En este ejemplo, la URL /debuginfo/ estará disponible sólo si la propiedad DEBUG es True.

 

Uso de Grupos con nombre.

En todos nuestros ejemplos de URLconf hasta ahora, hemos usado, grupos de expresiones regulares sin nombre, es decir, ponemos entre paréntesis las partes de la URL que queremos capturar y, Django pasa ese texto capturado a la función de vista como un argumento posicional. En un uso más avanzado, es posible utilizar grupos con nombre de expresiones regulares para capturar los bits de la URL y pasarlos como argumentos de palabra clave a una vista.

 

En las expresiones regulares Python, la sintaxis para grupos con nombre de expresiones regulares es (?Ppattern), donde name es el nombre del grupo y pattern es un patrón de concordancia.

He aquí un ejemplo de URLconf que usa grupos sin nombre:

from django.conf.urls.defaults import *
from mysite import views

urlpatterns = patterns(”,

(r’^articles/(d{4})/$’, views.year_archive),
(r’^articles/(d{4})/(d{2})/$’, views.month_archive),

)

Aquí, la misma URLConf reescrita, usando grupos con nombre:

from django.conf.urls.defaults import *
from mysite import views

urlpatterns = patterns(”,

(r’^articles/(?Pd{4})/$’, views.year_archive),
(r’^articles/(?Pd{4})/(?Pd{2})/$’, views.month_archive),

)

Esto logra exactamente lo mismo que el ejemplo anterior, con una sutil diferencia: los valores capturados se pasan a las funciones de vista como argumentos de palabras clave en lugar de argumentos posicionales.

 

Por ejemplo, con grupos sin nombre, una petición a /articles/2006/03/ daría lugar a una llamada a la función equivalente a esto:

month_archive(request, ’2006′, ’03′)

 

Con grupos con nombre, la misma petición resultaría en esta llamada a función:

month_archive(request, year=’2006′, month=’03′)

 

En la práctica, usar grupos con nombre hace a su URLconfs un poco más explícita y menos propensa a los errores del orden de los argumentos y puede volver a ordenar los argumentos de la función sus definiciones de vistas. Siguiendo el ejemplo anterior, si quisieramos cambiar la URL para incluir el mes antes del año, y estamos utilizando grupos sin nombre, tendríamos que recordar cambiar el orden de los argumentos en la vista month_archive. Si estuviéramos utilizando grupos con nombre, cambiar el orden de los parámetros capturados en la URL no tendría ningún efecto en la vista.

 

Por supuesto, los beneficios de los grupos con nombre llegan a costa de la brevedad; algunos desarrolladores encuentran la sintaxis de un grupo con nombre fea y demasiado detallada. Sin embargo, otra de las ventajas de los grupos con nombre es la legibilidad, especialmente para aquellos que no están íntimamente familiarizados con expresiones regulares o su aplicación de Django particular. Es más fácil ver lo que sucede, de un vistazo, en una URLconf que utiliza grupos con nombre.

django logo

Comprender el algoritmo de agrupación/concordancia.

Una advertencia con el uso de grupos con nombre en una URLconf es que un único patrón de URLconf no puede contener tanto grupos con nombre como sin nombre. Si usted hace esto, Django no dará ningún error, pero usted probablemente encontrará que sus URLs no son coincidentes tal y como usted espera. En concreto, aquí está el algoritmo que sigue el analizador URLconf:

 

  • Si hay algunos argumentos con nombre, utilizará esos, haciendo caso omiso de los argumentos con nombre.
  • De lo contrario, pasará todos los argumentos sin nombre como argumentos posicionales.
  • En ambos casos, pasará opciones adicionales como argumentos de palabra clave.
  • Pasar opciones extra a funciones de vista.

A veces usted se encontrará con la escritura de funciones de vista que son bastante similares, con sólo unas pequeñas diferencias. Por ejemplo, supongamos que tiene dos vistas cuyos contenidos son idénticos, excepto por las plantillas que utilizan:

# urls.py
from django.conf.urls.defaults import *
from mysite import views

urlpatterns = patterns(”,

(r’^foo/$’, views.foo_view),
(r’^bar/$’, views.bar_view),

)

# views.py
from django.shortcuts import render_to_response
from mysite.models import MyModel

def foo_view(request):

m_list = MyModel.objects.filter(is_new=True)
return render_to_response(‘template1.html’, {‘m_list’: m_list})

def bar_view(request):

m_list = MyModel.objects.filter(is_new=True)
return render_to_response(‘template2.html’, {‘m_list’: m_list})

Estamos repitiendo el código, y eso es poco elegante. En principio, usted puede pensar eliminar la redundancia utilizando el mismo punto de vista, tanto para las direcciones URL, poniendo entre paréntesis la URL a capturar, y comprobar la URL dentro de la vista para determinar la plantilla, así:

# urls.py
from django.conf.urls.defaults import *
from mysite import views

urlpatterns = patterns(”,

(r’^(foo)/$’, views.foobar_view),
(r’^(bar)/$’, views.foobar_view),

)

# views.py
from django.shortcuts import render_to_response
from mysite.models import MyModel

def foobar_view(request, url):

m_list = MyModel.objects.filter(is_new=True)
if url == ‘foo’:

template_name = ‘template1.html’

elif url == ‘bar’:

template_name = ‘template2.html’

return render_to_response(template_name, {‘m_list’: m_list})

El problema con esta solución, sin embargo, es que acopla las URL a su código. Si usted decide cambiar el nombre /foo/ a /fooey/, tendrá que acordarse de cambiar el código de la vista.

 

La solución elegante implica un parámetro URLconf opcional. Cada patrón en una URLconf puede incluir un tercer elemento: un diccionario de los argumentos de palabra clave para pasar a la función de vista.

django4

Con esto en mente, podemos volver a escribir nuestro ejemplo actual de esta manera:

# urls.py
from django.conf.urls.defaults import *
from mysite import views
urlpatterns = patterns(”,

(r’^foo/$’, views.foobar_view, {‘template_name’: ‘template1.html’}),
(r’^bar/$’, views.foobar_view, {‘template_name’: ‘template2.html’}),

)

# views.py
from django.shortcuts import render_to_response
from mysite.models import MyModel

def foobar_view(request, template_name):

m_list = MyModel.objects.filter(is_new=True)
return render_to_response(template_name, {‘m_list’: m_list})

Esta técnica de opciones URLconf extra es una buena forma de enviar información adicional a las funciones de la vista con una sobrecarga mínimo.

 

Las siguientes secciones contienen un par de ideas sobre cómo utilizar la técnica de opciones de URLconf extra en sus propios proyectos.

 

Fingiendo valores URLConf capturados.

Digamos que tiene un conjunto de vistas que coinciden con un patrón, junto con otra URL que no se ajusta a ese patrón, pero cuya lógica de la vista es la misma. En este caso, puede “falsear” la captación de valores URL mediante el uso de opciones URLconf extra para manejar esa URL adicional con la misma vista.

 

Por ejemplo, usted podría tener una aplicación que muestre algunos datos de un día en particular, con direcciones de Internet tales como las siguientes:

/mydata/jan/01/
/mydata/jan/02/
/mydata/jan/03/
# …
/mydata/dec/30/
/mydata/dec/31/

Esto es bastante simple – usted puede capturarlos con una URLconf como esta (con sintaxis de grupos con nombre):

urlpatterns = patterns(”,

(r’^mydata/(?Pw{3})/(?Pdd)/$’, views.my_view),

)

Y la función de vista sería así:

def my_view(request, month, day):

# ….

Ese método es sencillo – no es nada que no haya visto antes. El truco viene cuando se quiere añadir otra URL que utiliza my_view pero cuya URL no incluye un mes y/o día.

Por ejemplo, usted podría querer agregar otra URL, /mydata/birthday/, que sería equivalente a /mydata/jan/06/ :

urlpatterns = patterns(”,

(r’^mydata/birthday/$’, views.my_view, {‘month’: ‘jan’, ‘day’: ’06′}),
(r’^mydata/(?Pw{3})/(?Pdd)/$’, views.my_view),

)

Usted no tiene que cambiar su función de vista en absoluto.

 

Hacer una vista genérica.

Es buena práctica de programación “factorizar” puntos comunes en el código. Por ejemplo, con estas dos funciones de Python:

def say_hello(person_name):

print ‘Hello, %s’ % person_name

def say_goodbye(person_name):

print ‘Goodbye, %s’ % person_name

podemos factorizarlas usando un parámetro:

def greet(person_name, greeting):

print ‘%s, %s’ % (greeting, person_name)

Podemos aplicar esta misma filosofía a las vista Django usando parámetros adicionales URLconf. Con esto en mente, usted puede comenzar a hacer abstracciones de alto nivel de sus vistas. En lugar de pensar: “Esta vista muestra una lista de objetos Event,” y “Esta vista muestra una lista de objetos BlogEntry”, dese cuenta que ambos son casos específicos de vista que muestra una lista de objetos, donde el tipo de objeto es variable.

Tome este código, por ejemplo:

# urls.py
from django.conf.urls.defaults import *
from mysite import views

urlpatterns = patterns(”,

(r’^events/$’, views.event_list),
(r’^blog/entries/$’, views.entry_list),

)

# views.py
from django.shortcuts import render_to_response
from mysite.models import Event, BlogEntry

def event_list(request):

obj_list = Event.objects.all()
return render_to_response(‘mysite/event_list.html’, {‘event_list’: obj_list})

def entry_list(request):

obj_list = BlogEntry.objects.all()
return render_to_response(‘mysite/blogentry_list.html’,{‘entry_list’: obj_list})

Las dos vistas hacen esencialmente lo mismo: mostrar una lista de objetos. Factoricemos el tipo de objeto que estamos mostrando:

# urls.py
from django.conf.urls.defaults import *
from mysite import models, views

urlpatterns = patterns(”,

(r’^events/$’, views.object_list, {‘model’: models.Event}),
(r’^blog/entries/$’, views.object_list, {‘model’: models.BlogEntry}),

)

# views.py
from django.shortcuts import render_to_response

def object_list(request, model):

obj_list = model.objects.all()
template_name = ‘mysite/%s_list.html’ % model.__name__.lower()
return render_to_response(template_name, {‘object_list’: obj_list})

Con estos pequeños cambios, de repente tenemos una vista reutilizable, independiente del modelo. A partir de ahora, cada vez que necesitemos una vista que muestre un conjunto de objetos, podemos simplemente volver a utilizar esta vista object_list.  Un par de apuntes de lo realizado:

 

Pasamos las clases del modelo directamente, como el parámetro model. El diccionario de opciones URLConf extra puede pasar cualquier tipo de objeto – no sólo cadenas.

  • Usamos model.__name__.lower () para determinar el nombre de la plantilla. Cada clase Python tiene un atributo __name__ que devuelve el nombre de la clase. Esta función es útil en momentos como éste, cuando no sabemos el tipo de clase hasta el tiempo de ejecución.
  • Pasamos el nombre de variable genérico object_list a la plantilla. Fácilmente podríamos cambiar este nombre a blogentry_list o event_list.

Dado que los sitios web de bases de datos tienen varios patrones comunes, Django viene con un conjunto de “vistas genéricas” que utilizan esta técnica para ahorrar tiempo.

django3

Dar opciones de configuración de vista.

Si está desarrollando una aplicación Django, lo más probable es que los usuarios quieran algún grado de configuración. En este caso, es una buena idea añadir ganchos a sus vistas para las opciones de configuración que crea que la gente puede cambiar. Puede utilizar parámetros URLConf adicionales para este propósito.

Un parte común de una aplicación para configurar es el nombre de la plantilla:

def my_view(request, template_name):

var = do_something()
return render_to_response(template_name, {‘var’: var})

 

Comprender la precedencia de los valores capturados frente a las opciones extra.

Cuando hay un conflicto, los parámetros URLconf extra tienen prioridad sobre los parámetros de captura.

 

En otras palabras, si su URLconf captura una variable de un grupo con nombre y un parámetro URLconf extra incluye una variable con el mismo nombre, el valor del parámetro URLconf extra será utilizado. Por ejemplo, considere esta URLconf:

from django.conf.urls.defaults import *
from mysite import views

urlpatterns = patterns(”,

(r’^mydata/(?Pd+)/$’, views.my_view, {‘id’: 3}),

)

Aquí, tanto la expresión regular como el diccionario extra incluyen un id. 


Cualquier petición (por ejemplo, /mydata/2/ o /mydata/432432/) será tratada como si id es 3, con independencia del valor capturado en la URL. Notamos esto sólo para ayudarle a evitar caer en el error.

 

Usar argumentos de vista por defecto.

Otro truco conveniente es especificar los parámetros por defecto para los argumentos de una vista. Esto le dice a la vista que valor usar porn defecto para un parámetro si no se especifica ninguno. He aquí un ejemplo:

# urls.py
from django.conf.urls.defaults import *
from mysite import views

urlpatterns = patterns(”,

(r’^blog/$’, views.page),
(r’^blog/page(?Pd+)/$’, views.page),

)

# views.py
def page(request, num=’1′):

# Output the appropriate page of blog entries, according to num.
# …

Aquí, ambas URLpatterns apuntan a la misma vista views.page, pero el primer patrón no captura cualquier cosa desde la URL. Si el primer patrón concuerda, la función page() usará su argumento predeterminado para num, “1″. Si el segundo patrón concuerda, page() utilizará cualquier valor num capturado por la expresión regular.

Es común el uso de esta técnica en relación con las opciones de configuración, como se explicó anteriormente. En el siguiente ejemplo se hace una ligera mejora con el ejemplo de la sección “Dar opciones de configuración de vista”, proporcionando un valor predeterminado para template_name:

def my_view(request, template_name=’mysite/my_view.html’):

var = do_something()
return render_to_response(template_name, {‘var’: var})

django_mainVistas de casos especiales.

A veces usted tiene un patrón en su URLconf que maneja un gran conjunto de direcciones URL, pero usted necesitará un caso especial de ellas. En este caso, aprovechar la manera lineal en que trabaja una URLconf y colocar el caso particular en primer lugar.

 

Por ejemplo, usted puede pensar en páginas de “agregar un objeto” en el sitio de administración de Django, representado por un URLpattern así:

urlpatterns = patterns(”,

# …
(‘^([^/]+)/([^/]+)/add/$’, views.add_stage),
# …

)

Esto coincide con las URL como /myblog/entries/add/ y /auth/groups/add/. Sin embargo, la página “Añadir” para un objeto de usuario (/auth/user/add/) es un caso especial – que no muestra todos los campos del formulario, muestra dos campos de contraseña, etc. Podríamos resolver este problema mediante el caso especial en la vista, de este modo:

def add_stage(request, app_label, model_name):

if app_label == ‘auth’ and model_name == ‘user’:

# do special-case code

else:

# do normal code

pero eso es poco elegante por la razón que hemos visto varias veces en este capítulo: pone lógica de URL en la vista. Como una solución más elegante, podemos aprovechar el hecho de que las URLconfs se procesan en orden de arriba a abajo:

urlpatterns = patterns(”,

# …
(‘^auth/user/add/$’, views.user_add_stage),
(‘^([^/]+)/([^/]+)/add/$’, views.add_stage),
# …

)

Ahora, una petición a /auth/user/add/ será manejada por la vista user_add_stage. A pesar de que la URL coincide con el segundo patrón, coincide con el de arriba primero.

django-arch_structure

Capturar texto en las URLs.

Cada argumento capturado es enviado a la vista como una simple cadena Unicode de Python, independientemente del orden de concordancia que la expresión regular hace. Por ejemplo, en esta línea URLconf, el argumento year para views.year_archive() será una cadena, no un entero, incluso aunque d{4} sólo concuerde con cadenas de enteros:

(r’^articles/(?Pd{4})/$’, views.year_archive),

Esto es importante tenerlo en cuenta cuando se está escribiendo el código de la vista. Muchas funciones Python sólo aceptann objetos de cierto tipo. Un error común es tratar de crear un objeto datetime.date con valores de cadena en lugar de valores enteros valores:

>>> import datetime
>>> datetime.date(’1993′, ’7′, ’9′)
Traceback (most recent call last):

TypeError: an integer is required
>>> datetime.date(1993, 7, 9)
datetime.date(1993, 7, 9)

Trasladado a una URLConf y vista, el error se parecería a esto:

# urls.py
from django.conf.urls.defaults import *
from mysite import views

urlpatterns = patterns(”,

(r’^articles/(d{4})/(d{2})/(d{2})/$’, views.day_archive),

)

# views.py
import datetime

def day_archive(request, year, month, day):

# The following statement raises a TypeError!
date = datetime.date(year, month, day)

En lugar de esto, day_archive() podría escribirse correctamente así:

def day_archive(request, year, month, day):

date = datetime.date(int(year), int(month), int(day))

Tenga en cuenta que int () eleva un ValueError al pasar una cadena que no está compuesto exclusivamente de dígitos, pero estamos evitando el error en este caso porque la expresión regular en nuestra URLconf asegura que sólo se pase cadenas que contienen dígitos a la función de vista.

 

Determinar contra qué busca la URLConf.

Cuando una petición llega, Django intenta hacer coincidir los patrones de la URLConf contra la URL solicitada, como una cadena de Python. Esto no incluye los parámetros GET o POST, o el nombre de dominio. Tampoco incluye la barra principal, ya que cada dirección tiene una barra principal. Por ejemplo, en una solicitud de http://www.example.com/myapp/, Django tratará de satisfacer myapp/. En una solicitud de http://www.example.com/myapp/?page=3, Django tratará de satisfacer myapp/.

 

El método de petición (por ejemplo, POST, GET) no se tiene en cuenta a la hora de tratar con el URLconf. En otras palabras, todos los métodos de petición serán enviados a la misma función para la misma URL. Es la responsabilidad de una función de vista llevar a cabo la ramificación basada en el método de la solicitud.

 

Abstracciones de alto nivel de las funciones de vista.

Hablando de ramificación basada en el método de la petición, vamos a echar un vistazo a cómo podríamos construir una buena manera de hacerlo. Considerar este diseño URLconf/Vista:

# urls.py
from django.conf.urls.defaults import *
from mysite import views
urlpatterns = patterns(”,

# …
(r’^somepage/$’, views.some_page),
# …

)

# views.py
from django.http import Http404, HttpResponseRedirect
from django.shortcuts import render_to_response
def some_page(request):

if request.method == ‘POST’:

do_something_for_post()
return HttpResponseRedirect(‘/someurl/’)

elif request.method == ‘GET’:

do_something_for_get()
return render_to_response(‘page.html’)

else:

raise Http404()

En este ejemplo, el manejo de la vista some_page() de las peticiones POST y GET es muy diferente.

 

Lo único que tienen en común es una URL compartida URL: /somepage/. Como tal, es una falta de elegancia hacer frente tanto a POST y GET en la misma función de vista. Sería bueno si pudiéramos tener dos funciones de vista separadas – una que gestione las peticiones GET y otra que gestione las POST – y garantizar que cada una se invoque sólo cuando sea necesario.

 

Podemos hacer eso escribiendo una función de vista que delege a otras vistas, ya sea antes o después de ejecutar alguna lógica personalizada. He aquí un ejemplo de cómo esta técnica podría ayudar a simplificar nuestra vista some_page():

DjangoApp_8

# views.py
from django.http import Http404, HttpResponseRedirect
from django.shortcuts import render_to_response

def method_splitter(request, GET=None, POST=None):

if request.method == ‘GET’ and GET is not None:

return GET(request)

elif request.method == ‘POST’ and POST is not None:

return POST(request)

raise Http404

def some_page_get(request):

assert request.method == ‘GET’
do_something_for_get()
return render_to_response(‘page.html’)

def some_page_post(request):

assert request.method == ‘POST’
do_something_for_post()
return HttpResponseRedirect(‘/someurl/’)

# urls.py
from django.conf.urls.defaults import *
from mysite import views

urlpatterns = patterns(”,

# …
(r’^somepage/$’, views.method_splitter, {‘GET’: views.some_page_get, ‘POST’: views.some_page_post}),
# …

)

Veamos que hace esto:

  • Escribimos una nueva vista, method_splitter(), que delega a otras vistas según request.method. Busca dos argumentos de palabra clave, GET y POST, que deben ser funciones de vista. Si request.method es ‘GET’, llama a la vista GET. Si request.method es “POST”, llama a la vista POST. Si request.method es otra cosa (HEAD, etc), o si GET o POST no fueron suministrados a la función, se eleva un Http404.
  • En el URLconf, apuntamos /somepage/ al method_splitter() y le pasarmos argumentos extra – las funciones para el uso de GET y POST, respectivamente.
  • Por último, dividimos la vista some_page() vista en dos funciones de vista: some_page_get() y some_page_post (). Esto es mucho mejor que colocar toda la lógica en una sola vista.

Ahora tenemos una bonita función de vista genérica que encapsula la lógica de delegar una vista mediante request.method. Nada de method_splitter() está ligado a nuestra aplicación específica, por supuesto, asi que podemos reutilizarla en otros proyectos.

Pero hay una forma de mejorar el method_splitter(). Ahora, se asume que las vistas GET y POST no toman otros argumentos que request. ¿Qué pasa si se quiere utilizar method_splitter() con vistas que, por ejemplo, capturen texto de URLs o tomen argumentos de palabra clave opcionales?

 

Para ello, podemos usar una característica agradable de Python: argumentos variables con asteriscos. Vamos a mostrar el ejemplo primero y luego explicarlo:

def method_splitter(request, *args, **kwargs):

get_view = kwargs.pop(‘GET’, None)
post_view = kwargs.pop(‘POST’, None)
if request.method == ‘GET’ and get_view is not None:

return get_view(request, *args, **kwargs)

elif request.method == ‘POST’ and post_view is not None:

return post_view(request, *args, **kwargs)

raise Http404

Aquí, refactorizamos method_splitter() para eliminar los argumentos de palabra clave GET y POST en favor de *args y **kwargs. Esta es una característica Python que permite a una función aceptar un número arbitrario dinámico de argumentos cuyos nombres no se conocen hasta el tiempo de ejecución. Si coloca un asterisco delante de un parámetro en una definición de la función, cualquier argumento posicional a esa función se guarda en una sola tupla. Si coloca dos asteriscos delante de un parámetro en una definición de la función, cualquier argumento de palabras clave para esa función se guardará en un diccionario único.

Por ejemplo, tenga en cuenta esta función:

def foo(*args, **kwargs):

print “Positional arguments are:”
print args
print “Keyword arguments are:”
print kwargs

Así es como funcionaría:

>>> foo(1, 2, 3)
Positional arguments are:
(1, 2, 3)
Keyword arguments are:
{}
>>> foo(1, 2, name=’Adrian’, framework=’Django’)
Positional arguments are:
(1, 2)
Keyword arguments are:
{‘framework’: ‘Django’, ‘name’: ‘Adrian’}

Llevando esto a method_splitter(), se puede ver que estamos usando *args y **kwargs para aceptar argumentos a la función y distribuirlos a la vista apropiada. Pero antes de hacer eso, hacemos dos llamadas a kwargs.pop() para obtener los argumentos GET y POST, si están disponibles. (Estamos usando pop() con un valor por defecto None para evitar KeyError si alguno no está definido.)

django_main

Envolviendo funciones de vista.

Nuestro truco de vista final se basa en una avanzada técnica de Python. Supongamos que se encuentre un montón de código repetitivo a través de diversas vistas, como en este ejemplo:

def my_view1(request):

if not request.user.is_authenticated():

return HttpResponseRedirect(‘/accounts/login/’)

# …
return render_to_response(‘template1.html’)

def my_view2(request):

if not request.user.is_authenticated():

return HttpResponseRedirect(‘/accounts/login/’)

# …
return render_to_response(‘template2.html’)

def my_view3(request):

if not request.user.is_authenticated():

return HttpResponseRedirect(‘/accounts/login/’)

# …
return render_to_response(‘template3.html’)

Aquí, cada vista comienza comprobando que request.user está autenticado, es decir, que el usuario se ha validado correctamente en el sitio web y sino lo redirige a /accounts/login/.

Sería bueno si pudiéramos eliminar ese trozo de código repetitivo de cada una de estas vistas y sólo marcarlos como que requieren autenticación. Podemos hacer esto haciendo un envoltorio de la vista. Tome un momento para estudiar esto:

def requires_login(view):

def new_view(request, *args, **kwargs):

if not request.user.is_authenticated():

return HttpResponseRedirect(‘/accounts/login/’)

return view(request, *args, **kwargs)

return new_view

Esta función, requires_login, toma una función de vista (view) y devuelve una nueva función de vista (new_view). La nueva función, new_view, se define dentro de requires_login y se encarga la lógica de controlar request.user.is_authenticated() y delegar a la vista original (view).

Ahora, podemos eliminar las comprobaciones if not request.user.is_authenticated() de nuestras vistas y simplemente envolverlas con requires_login en nuestra URLconf:

from django.conf.urls.defaults import *
from mysite.views import requires_login, my_view1, my_view2, my_view3

urlpatterns = patterns(”,

(r’^view1/$’, requires_login(my_view1)),
(r’^view2/$’, requires_login(my_view2)),
(r’^view3/$’, requires_login(my_view3)),

)

Esto tiene el mismo efecto que antes, pero con menos código redundante. Ahora hemos creado una buena función genérica – requires_login() que nos puede envolver cualquier vista para requerir un inicio de sesión.

djangopony1

Incluir otras URLconfs.

Si desea que su código sea usado en varios sitios basados en Django, debe considerar la organización de sus URLconfs de tal manera que se puedan “incluir”.

De esta forma, su URLconf puede “incluir” otros módulos URLconf. Por ejemplo, esta URLconf incluye otros URLconfs:

from django.conf.urls.defaults import *

urlpatterns = patterns(”,

(r’^weblog/’, include(‘mysite.blog.urls’)),
(r’^photos/’, include(‘mysite.photos.urls’)),
(r’^about/$’, ‘mysite.views.about’),

)

Hay un aspecto importante: las expresiones regulares en este ejemplo que apuntan a un include() no acaban en $, pero si una barra final. Cuando Django se encuentra con include(), corta parte de la URL concordante hasta ese punto y envía la cadena restante a la URLconf incluida para su posterior procesamiento.

Siguiendo este ejemplo, aquí está el URLconf mysite.blog.urls:

from django.conf.urls.defaults import *

urlpatterns = patterns(”,

(r’^(dddd)/$’, ‘mysite.blog.views.year_detail’),
(r’^(dddd)/(dd)/$’, ‘mysite.blog.views.month_detail’),

)

Con estas dos URLconfs, aquí se muestra como se gestionarían algunas solicitudes:

  • /weblog/2007/: En la primera URLconf, el patrón r’^weblog/’ coincide. Debido a que es un include(), Django divide todo el texto coincidente, que es ‘weblog/’ en este caso. La parte restante de la dirección es 2007/, que coincide con la primera línea en el URLconf mysite.blog.urls.
  • /weblog//2007/ (con dos barras inclinadas): En la primera URLconf, el patrón r’^weblog/’ coincide. Debido a que es un include(), Django divide todo el texto coincidente, que es ‘weblog/’ en este caso. La parte restante de la dirección es /2007/, que no coincide con ninguna de las líneas en el URLconf mysite.blog.urls.
  • /about/: Coincide con la vista mysite.views.about la primera URLconf, lo que demuestra que se pueden mezclar patrones include() con patrones no include.
Cómo trabajan con include() los parámetros capturados

Un URLconf incluido recibe cualquier parámetro capturado del URLconf padre, por ejemplo:

# root urls.py
from django.conf.urls.defaults import *

urlpatterns = patterns(”,

(r’^(?Pw+)/blog/’, include(‘foo.urls.blog’)),

)

# foo/urls/blog.py
from django.conf.urls.defaults import *

urlpatterns = patterns(”,

(r’^$’, ‘foo.views.blog_index’),
(r’^archive/$’, ‘foo.views.blog_archive’),

)

En este ejemplo, la variable username capturada se pasa al URLconf incluido y, por lo tanto, a cada función de vista dentro de ese URLconf.

Tenga en cuenta que los parámetros capturados siempre serán pasados a todas las líneas del URLconf incluido, independientemente de si la vista de la línea en realidad acepta esos parámetros como válidos. Por esta razón, esta técnica es útil sólo si estás seguro de que todas las vistas de la URLconf incluida aceptan los parámetros que está pasando.

 

Cómo trabajan con include() las opciones URLconf extras.

Del mismo modo, se pueden pasar opciones adicionales URLconf a include(), tal como se pueden pasar opciones URLconf adicionales para una vista normal – como un diccionario. Al hacer esto, a cada línea de la URLconf incluida se le pasan las opciones extra.

Por ejemplo, los siguientes conjuntos URLconf son funcionalmente idénticos.

Conjunto uno:

# urls.py
from django.conf.urls.defaults import *

urlpatterns = patterns('',

(r’^blog/’, include(‘inner’), {‘blogid’: 3}),

)

# inner.py
from django.conf.urls.defaults import *

urlpatterns = patterns(”,

(r’^archive/$’, ‘mysite.views.archive’),
(r’^about/$’, ‘mysite.views.about’),
(r’^rss/$’, ‘mysite.views.rss’),

)

Conjunto dos:

# urls.py
from django.conf.urls.defaults import *

urlpatterns = patterns(”,

(r’^blog/’, include(‘inner’)),

)

# inner.py
from django.conf.urls.defaults import *

urlpatterns = patterns(”,

(r’^archive/$’, ‘mysite.views.archive’, {‘blogid’: 3}),
(r’^about/$’, ‘mysite.views.about’, {‘blogid’: 3}),
(r’^rss/$’, ‘mysite.views.rss’, {‘blogid’: 3}),

)

Como es el caso de los parámetros capturados, las opciones adicionales siempre se pasarán a todas las líneas del URLconf incluido, independientemente de si la vista de la línea acepta esas opciones como válidas. Por esta razón, esta técnica es útil sólo si tiene la certeza de que todas las vistas incluidas en la URLconf aceptan las opciones extra que está pasando.

</BLOCKQUOTE

hqdefault

 

Ultima versión estable actualizada de Django:

Descarga

 

Si te ha gustado el artículo inscribete al feed clicando en la imagen más abajo para tenerte siempre actualizado sobre los nuevos contenidos del blog:

Espero que esta publicación te haya gustado. Si tienes alguna duda, consulta o quieras complementar este post, no dudes en escribir en la zona de comentarios. También puedes visitar Facebook, Twitter, Google +, Linkedin, Instagram, Pinterest y Feedly donde encontrarás información complementaria a este blog. COMPARTE EN!

0 comentarios:

Publicar un comentario

No insertes enlaces clicables, de lo contrario se eliminará el comentario. Si quieres ser advertido via email de los nuevos comentarios marca la casilla "Avisarme". Si te ayudé con la publicación o con las respuestas a los comentarios, compártelo en Facebook,Twitter o Instagram. Gracias.

Archivo