¿Qué es reverse () en Django?

218

Cuando leo el código django a veces, veo en algunas plantillas reverse(). No estoy muy seguro de qué se trata, pero se usa junto con HttpResponseRedirect. ¿Cómo y cuándo se reverse()supone que debe usarse?

Sería bueno que alguien respondiera con algunos ejemplos ...

Lakesh
fuente
26
Dado un patrón de URL, Django usa url () para elegir la vista correcta y generar una página. Es decir, url--> view name. Pero a veces, como cuando se redirige, debe ir en la dirección inversa y darle a Django el nombre de una vista, y Django genera la URL adecuada. En otras palabras, view name --> url. Es decir, reverse()(es el reverso de la función url). Puede parecer más transparente simplemente llamarlo, generateUrlFromViewNamepero eso es demasiado largo y probablemente no lo suficientemente general: docs.djangoproject.com/en/dev/topics/http/urls/…
eric
44
@neuronet Gran explicación, gracias. Este nombre me pareció (y parece) particularmente no intuitivo, lo cual considero un pecado grave. ¿Quién no odia la ofuscación innecesaria?
Mike roedor
Este es un ejemplo típico de nomenclatura que enfatiza un aspecto de una entidad (por ejemplo, una función) que estaba en la mente del programador en ese momento, dado su contexto, pero no es la opción más útil en el contexto amplio de ningún otro desarrollador. . A menudo caemos en esta trampa como programadores: nombrar es tan importante para la capacidad de descubrimiento, vale la pena detenerse y pensar en los diferentes contextos y elegir el más apropiado.
Cornel Masson

Respuestas:

346

reverse()El | Documentación de Django


Supongamos que en tu urls.pyhas definido esto:

url(r'^foo$', some_view, name='url_name'),

En una plantilla, puede referirse a esta url como:

<!-- django <= 1.4 -->
<a href="{% url url_name %}">link which calls some_view</a>

<!-- django >= 1.5 or with {% load url from future %} in your template -->
<a href="{% url 'url_name' %}">link which calls some_view</a>

Esto se representará como:

<a href="/foo/">link which calls some_view</a>

Ahora diga que quiere hacer algo similar en su views.py- por ejemplo, está manejando alguna otra url (no /foo/) en otra vista (no some_view) y desea redirigir al usuario a /foo/(a menudo el caso en el envío exitoso del formulario).

Podrías simplemente hacer:

return HttpResponseRedirect('/foo/')

Pero, ¿qué pasa si quieres cambiar la URL en el futuro? Tendría que actualizar su urls.py y todas las referencias a él en su código. Esto viola DRY (Don't Repeat Yourself) , toda la idea de editar solo un lugar, que es algo por lo que debe esforzarse.

En cambio, puedes decir:

from django.urls import reverse
return HttpResponseRedirect(reverse('url_name'))

Esto busca en todas las URL definidas en su proyecto la URL definida con el nombre url_namey devuelve la URL real /foo/.

Esto significa que se refiere a la url solo por su nameatributo; si desea cambiar la url en sí o la vista a la que se refiere, puede hacerlo editando solo un lugar - urls.py.

escita
fuente
2
Para su información, {{ url 'url_name' }}debe estar {% url url_name %}en Django 1.4 o anterior. Esto cambiará en la próxima versión de Django (1.5) y luego debería estarlo {% url 'url_name' %}. Los documentos de la etiqueta de plantilla de URL proporcionan buena información si se desplaza hacia abajo un poco hacia la sección "compatibilidad con
versiones posteriores
1
j_syk gracias: he estado haciendo @load url from future @ desde que salió 1.3 y olvidé que aún no es el predeterminado. Actualizaré mi respuesta para que no tropiece con los inexpertos.
scytale
2
solucionado - Creo que se considera totalmente aceptable que edite errores tipográficos tontos en las respuestas de otras personas, así que si ve más, simplemente ingrese :-)
scytale
3
Una de las respuestas más sutiles que se pueden encontrar en este sitio.
Manas Chaturvedi
1
">>> pero qué pasa si desea cambiar la URL en el futuro", este tipo de sutilezas que son útiles en .0001% del tiempo y la solución se envía como una característica útil, y las personas la usan como si fueran ' mejores prácticas 'y dejar el desorden. TBH si cuando uno cambia las URL en el futuro solo hace una búsqueda global de reemplazo. Incluso esta solución (use url_name) es propensa al problema de '¿qué pasa si desea cambiar url_name en el futuro?' He estado codificando en Django por más de 5 años y aún no he respondido a la necesidad url_reverse. La mejor manera de lidiar con este tipo de rarezas es negarse a usarlas.
nehem
10

Esta es una vieja pregunta, pero aquí hay algo que podría ayudar a alguien.

De los documentos oficiales:

Django proporciona herramientas para realizar reversiones de URL que coinciden con las diferentes capas donde se necesitan URL: En plantillas: Uso de la etiqueta de plantilla de URL. En código Python: Usando la función reverse (). En código de nivel superior relacionado con el manejo de URL de instancias de modelo Django: El método get_absolute_url ().

P.ej. en plantillas (etiqueta de URL)

<a href="{% url 'news-year-archive' 2012 %}">2012 Archive</a>

P.ej. en código python (usando la reversefunción)

return HttpResponseRedirect(reverse('news-year-archive', args=(year,)))
Kishy Nivas
fuente
1
necesita descripción completa jefe
giveJob
OP mencionó específicamente que leyó los documentos, necesitaba una explicación, no solo copiar / pegar de los documentos.
RusI
8

Las respuestas existentes hicieron un gran trabajo al explicar el qué de esta reverse()función en Django.

Sin embargo, esperaba que mi respuesta arrojara una luz diferente sobre el por qué : por qué usar reverse()en lugar de otros enfoques más directos, posiblemente más pitónicos en el enlace de vista de plantilla, y cuáles son algunas razones legítimas para la popularidad de esta "redirección a través de reverse() patrón "en la lógica de enrutamiento de Django.

Un beneficio clave es la construcción inversa de una url, como han mencionado otros. Justo como usaría {% url "profile" profile.id %}para generar la url desde el archivo de configuración de url de su aplicación: por ejemplo path('<int:profile.id>/profile', views.profile, name="profile").

Pero como lo ha señalado el OP, el uso de reverse()también se combina comúnmente con el uso de HttpResponseRedirect. ¿Pero por qué?

No estoy muy seguro de qué se trata, pero se usa junto con HttpResponseRedirect. ¿Cómo y cuándo se supone que se usa este reverse ()?

Considere lo siguiente views.py:

from django.http import HttpResponseRedirect
from django.urls import reverse

def vote(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    try:
        selected = question.choice_set.get(pk=request.POST['choice'])
    except KeyError:
        # handle exception
        pass
    else:
        selected.votes += 1
        selected.save()
        return HttpResponseRedirect(reverse('polls:polls-results',
                                    args=(question.id)
        ))

Y nuestro mínimo urls.py:

from django.urls import path
from . import views

app_name = 'polls'
urlpatterns = [
    path('<int:question_id>/results/', views.results, name='polls-results'),
    path('<int:question_id>/vote/', views.vote, name='polls-vote')
]

En la vote()función, el código en nuestro elsebloque se usa reversejunto con HttpResponseRedirectel siguiente patrón:

HttpResponseRedirect(reverse('polls:polls-results',
                                        args=(question.id)

En primer lugar, esto significa que no tenemos que codificar la URL (de acuerdo con el principio DRY) pero, lo que es más importante, reverse()proporciona una forma elegante de construir cadenas de URL al manejar valores desempaquetados de los argumentos ( args=(question.id)es manejado por URLConfig). Se supone que questiontiene un atributo idque contiene el valor 5, la URL construida a partir de reverse()entonces sería:

'/polls/5/results/'

En el código de enlace de vista de plantilla normal, usamos HttpResponse()o render()como generalmente implican menos abstracción: una función de vista que devuelve una plantilla:

def index(request):
    return render(request, 'polls/index.html') 

Pero en muchos casos legítimos de redireccionamiento, generalmente nos importa construir la URL a partir de una lista de parámetros. Estos incluyen casos como:

  • Envío de formulario HTML mediante POSTsolicitud
  • Inicio de sesión de usuario posterior a la validación
  • Restablecer contraseña a través de tokens web JSON

La mayoría de estos implican alguna forma de redireccionamiento y una URL construida a través de un conjunto de parámetros. ¡Espero que esto se agregue al útil hilo de respuestas!

onlyphantom
fuente
4

La función admite el principio seco: garantizar que no codifique las URL en toda su aplicación. Una url debe definirse en un solo lugar y solo en un lugar: su url conf. Después de eso, solo estás haciendo referencia a esa información.

Utilícelo reverse()para darle la url de una página, dada la ruta a la vista o el parámetro page_name de su url conf. Lo usaría en casos en los que no tiene sentido hacerlo en la plantilla con {% url 'my-page' %}.

Hay muchos lugares posibles donde podría usar esta funcionalidad. Un lugar que he encontrado que lo uso es al redirigir a los usuarios en una vista (a menudo después del procesamiento exitoso de un formulario):

return HttpResponseRedirect(reverse('thanks-we-got-your-form-page'))

También puede usarlo al escribir etiquetas de plantilla.

Otra vez que utilicé reverse()fue con el modelo de herencia. Tenía un ListView en un modelo principal, pero quería pasar de cualquiera de esos objetos principales al DetailView de su objeto secundario asociado. Adjunté una get__child_url()función al padre que identificaba la existencia de un hijo y devolvía la url de su DetailView usando reverse().

Ashish Kumar Sahoo
fuente
2

Las respuestas existentes son bastante claras. En caso de que no sepa por qué se llama reverse: toma la entrada de un nombre de url y da la url real, que es inversa a tener una url primero y luego darle un nombre.

yyFred
fuente
1
Solo estoy aprendiendo Django de un tutorial (Django Girls). Es una curva de aprendizaje empinada. Creo que el nombre de esta función es terrible: "reservar" sin ninguna calificación MUY FUERTE sugiere reservar una lista o cadena, que obviamente no tiene nada que ver con eso.
Mike roedor
@mikerodent Estoy completamente de acuerdo contigo. Además, ninguna de estas respuestas explica por qué la función se llama inversa. Es un mal nombre imo.
Soham Dongargaonkar
1

El reverse () se utiliza para adherir el principio DRYGO de django, es decir, si cambia la url en el futuro, puede hacer referencia a esa url usando reverse (urlname).

AEROCODE
fuente