ce qui est reverse () dans Django

220

Quand je lis parfois du code django, je vois dans certains modèles reverse(). Je ne sais pas trop ce que c'est, mais il est utilisé avec HttpResponseRedirect. Comment et quand est-il reverse()censé être utilisé?

Ce serait bien si quelqu'un donnait une réponse avec quelques exemples ...

Lakesh
la source
27
Étant donné un modèle d'URL, Django utilise url () pour choisir la bonne vue et générer une page. C'est url--> view name. Mais parfois, comme lors de la redirection, vous devez aller dans le sens inverse et donner à Django le nom d'une vue, et Django génère l'url appropriée. En d' autres termes, view name --> url. Autrement dit, reverse()(c'est l'inverse de la fonction url). Il peut sembler plus transparent de l'appeler, generateUrlFromViewNamemais c'est trop long et probablement pas assez général: docs.djangoproject.com/en/dev/topics/http/urls/…
eric
4
@neuronet Excellente explication, merci. Ce nom m'a semblé (et semble) particulièrement non intuitif, que je considère comme un grave péché. Qui ne déteste pas l'obscurcissement inutile?
mike rodent
Il s'agit d'un exemple typique de dénomination qui met l'accent sur un aspect d'une entité (par exemple, une fonction) qui était avant tout dans l'esprit du programmeur à l'époque, compte tenu de son contexte, mais qui n'est pas l'option la plus utile dans le contexte général de tout autre développeur. . Nous tombons souvent dans ce piège en tant que programmeurs - la dénomination est si importante pour la découvrabilité, cela vaut la peine de s'arrêter et de réfléchir aux différents contextes et de choisir le plus approprié.
Cornel Masson

Réponses:

349

reverse()| Documentation Django


Supposons que urls.pyvous ayez défini ceci dans votre :

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

Dans un modèle, vous pouvez alors faire référence à cette URL comme:

<!-- 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>

Cela sera rendu comme:

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

Maintenant, dites que vous voulez faire quelque chose de similaire dans votre views.py- par exemple, vous gérez une autre URL (pas /foo/) dans une autre vue (pas some_view) et vous voulez rediriger l'utilisateur vers /foo/(souvent le cas lors de la soumission réussie du formulaire).

Vous pourriez simplement faire:

return HttpResponseRedirect('/foo/')

Mais que se passe-t-il si vous souhaitez modifier l'URL à l'avenir? Vous devez mettre à jour votre urls.py et toutes les références à celui-ci dans votre code. Cela viole DRY (Don't Repeat Yourself) , l'idée de n'éditer qu'un seul endroit, ce qui est quelque chose à rechercher.

Au lieu de cela, vous pouvez dire:

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

Cela recherche dans toutes les URL définies dans votre projet l'URL définie avec le nom url_nameet renvoie l'URL réelle /foo/.

Cela signifie que vous ne faites référence à l'URL que par son nameattribut - si vous souhaitez modifier l'URL elle-même ou la vue à laquelle elle se réfère, vous pouvez le faire en modifiant un seul emplacement - urls.py.

faux
la source
3
Pour info, {{ url 'url_name' }}devrait être {% url url_name %}dans Django 1.4 ou une version antérieure. Cela changera dans la prochaine version de Django (1.5) et devrait alors l'être {% url 'url_name' %}. Les documents de l' URL templatetag donnent de bonnes informations si vous faites défiler un peu vers la section "compatibilité ascendante"
j_syk
1
j_syk merci - je fais @load url from future @ depuis la sortie de la 1.3 et j'ai oublié que ce n'est pas encore la valeur par défaut. Je mettrai à jour ma réponse pour qu'elle ne fasse pas trébucher les inexpérimentés.
scytale
2
corrigé - Je pense qu'il est considéré comme totalement acceptable pour vous de modifier vous-même les fautes de frappe stupides dans les réponses des autres, donc si vous en voyez plus, sautez :-)
scytale
3
L'une des réponses les plus subtiles que l'on puisse trouver sur ce site.
Manas Chaturvedi
1
">>> mais que se passe-t-il si vous souhaitez modifier l'URL à l'avenir", ces types de subtilités qui sont utiles sur .0001% du temps et la solution est livrée comme une fonctionnalité utile, et les gens l'utilisent comme s'ils étaient ' meilleures pratiques »et laisser le gâchis. TBH si quand on change les URL à l'avenir, vous faites juste une recherche globale de remplacement. Même cette solution (utilisez url_name) est sujette au problème de «et si vous voulez changer l'url_name à l'avenir? Je code à Django depuis plus de 5 ans et je n'ai toujours pas répondu au besoin url_reverse. La meilleure façon de faire face à ce genre de bizarreries est de refuser de les utiliser.
nehem
10

C'est une vieille question, mais voici quelque chose qui pourrait aider quelqu'un.

De la documentation officielle:

Django fournit des outils pour effectuer une inversion d'URL qui correspondent aux différentes couches où les URL sont nécessaires: Dans les modèles: Utilisation de la balise de modèle d'URL. En code Python: Utilisation de la fonction reverse (). Dans le code de niveau supérieur lié à la gestion des URL des instances de modèle Django: La méthode get_absolute_url ().

Par exemple. dans les modèles (balise url)

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

Par exemple. en code python (en utilisant la reversefonction)

return HttpResponseRedirect(reverse('news-year-archive', args=(year,)))
Kishy Nivas
la source
1
besoin de description complète patron
giveJob
OP a spécifiquement mentionné qu'il avait lu les documents, il avait besoin d'explications, pas seulement de copier / coller à partir des documents.
RusI
8

Les réponses existantes ont fait un excellent travail pour expliquer le pourquoi de cette reverse()fonction dans Django.

Cependant, j'espérais que ma réponse jetterait un éclairage différent sur le pourquoi : pourquoi utiliser reverse()à la place d'autres approches plus simples, sans doute plus pythoniques dans la liaison de vue de modèle, et quelles sont les raisons légitimes de la popularité de cette "redirection via reverse() pattern "dans la logique de routage Django.

Un avantage clé est la construction inverse d'une URL, comme d'autres l'ont mentionné. Tout comme la façon dont vous utiliseriez {% url "profile" profile.id %}pour générer l'URL à partir du fichier de configuration d'URL de votre application: par exemple path('<int:profile.id>/profile', views.profile, name="profile").

Mais comme l'OP l'a noté, l'utilisation de reverse()est également couramment combinée à l'utilisation de HttpResponseRedirect. Mais pourquoi?

Je ne sais pas trop ce que c'est, mais il est utilisé avec HttpResponseRedirect. Comment et quand ce reverse () est-il censé être utilisé?

Tenez compte des éléments suivants 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)
        ))

Et notre minimum 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')
]

Dans la vote()fonction, le code de notre elsebloc utilise reverseavec HttpResponseRedirectle modèle suivant:

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

Cela signifie avant tout que nous n'avons pas à coder en dur l'URL (conformément au principe DRY) mais plus important encore, reverse()fournit un moyen élégant de construire des chaînes d'URL en gérant les valeurs décompressées des arguments ( args=(question.id)est géré par URLConfig). Supposé questionavoir un attribut idqui contient la valeur 5, l'URL construite à partir de reverse()serait alors:

'/polls/5/results/'

Dans le code de liaison de vue de modèle normal, nous utilisons HttpResponse()ou render()car ils impliquent généralement moins d'abstraction: une fonction de vue renvoyant un modèle:

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

Mais dans de nombreux cas légitimes de redirection, nous nous soucions généralement de construire l'URL à partir d'une liste de paramètres. Il s'agit notamment de cas tels que:

  • Soumission de formulaire HTML par POSTdemande
  • Connexion utilisateur après validation
  • Réinitialiser le mot de passe via les jetons Web JSON

La plupart d'entre eux impliquent une certaine forme de redirection et une URL construite à travers un ensemble de paramètres. J'espère que cela s'ajoute au fil de réponses déjà utile!

fantôme
la source
4

La fonction prend en charge le principe sec - garantissant que vous ne codez pas les URL en dur dans votre application. Une URL doit être définie en un seul endroit, et un seul endroit - votre conf. URL. Après cela, vous ne faites que référencer ces informations.

Utilisez reverse()pour vous donner l'url d'une page, étant donné soit le chemin d'accès à la vue, soit le paramètre page_name de votre conf d'url. Vous l'utiliseriez dans les cas où cela n'a pas de sens de le faire dans le modèle avec {% url 'my-page' %}.

Il existe de nombreux endroits où vous pouvez utiliser cette fonctionnalité. Un endroit que j'ai trouvé que je l'utilise est lors de la redirection des utilisateurs dans une vue (souvent après le traitement réussi d'un formulaire) -

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

Vous pouvez également l'utiliser lors de l'écriture de balises de modèle.

Une autre fois, j'ai utilisé reverse()avec l'héritage de modèle. J'avais un ListView sur un modèle parent, mais je voulais passer de l'un de ces objets parents au DetailView de son objet enfant associé. J'ai attaché une get__child_url()fonction au parent qui a identifié l'existence d'un enfant et a renvoyé l'URL de son DetailView à l'aide reverse().

Ashish Kumar Sahoo
la source
2

Les réponses existantes sont assez claires. Juste au cas où vous ne savez pas pourquoi il est appelé reverse: il prend une entrée d'un nom d'URL et donne l'URL réelle, ce qui est inverse à avoir une URL d'abord, puis lui donner un nom.

yyFred
la source
1
Je viens d'apprendre Django à partir d'un tutoriel (Django Girls). C'est une courbe d'apprentissage abrupte. Je pense que le nom de cette fonction est affreux: "réserver" sans aucune qualification suggère TRÈS FORTEMENT de réserver une liste ou une chaîne, ce qui n'a évidemment rien à voir avec cela.
mike rodent
@mikerodent Je suis entièrement d'accord avec vous. En outre, aucune de ces réponses n'explique pourquoi la fonction est appelée inversée. C'est un mauvais nom imo.
Soham Dongargaonkar
1

Le reverse () est utilisé pour adhérer au principe django DRY, c'est-à-dire que si vous changez l'url à l'avenir, vous pouvez référencer cette url en utilisant reverse (urlname).

AEROCODE
la source