les URL django sans barre oblique à la fin ne redirigent pas

89

J'ai deux applications situées sur deux ordinateurs distincts. Sur l'ordinateur A, dans le urls.pyfichier, j'ai une ligne comme la suivante:

(r'^cast/$', 'mySite.simulate.views.cast')

Et cette URL fonctionnera à la fois pour mySite.com/cast/et mySite.com/cast. Mais sur l'ordinateur, BI a une URL similaire écrite comme:

(r'^login/$', 'mySite.myUser.views.login')

Pour une raison quelconque sur l'ordinateur B, le url mySite.com/login/ fonctionnera mais mySite.com/loginse bloquera et ne reviendra pas mySite.com/login/comme il le fera sur l'ordinateur A. Y a-t-il quelque chose que j'ai manqué? Les deux url.pyfichiers me semblent identiques.

quoi quoi
la source

Réponses:

103

vérifiez vos APPEND_SLASHparamètres dans le fichier settings.py

plus d'informations dans la documentation django

Jiaaro
la source
4
"Lorsqu'elle est définie sur True, si l'URL de la requête ne correspond à aucun des modèles de URLconf et qu'elle ne se termine pas par une barre oblique, une redirection HTTP est émise vers la même URL avec une barre oblique ajoutée. Notez que la redirection peut provoquer toutes les données soumises dans une demande POST à ​​perdre. ". "Le paramètre APPEND_SLASH n'est utilisé que si CommonMiddleware est installé ...". Je préfère la réponse de Michael Gendin pour une solution plus propre.
Wtower
3
Cela ne fonctionne pas si vous utilisez une URL supplémentaire "attraper tout" à la dernière entrée de vos modèles d'url. La réponse de @ speedplane fonctionnera même dans ces situations. Mais, bien sûr, c'est plus simple et devrait être utilisé s'il n'y a pas d'entrées de modèle d'url "catch all".
np8
195

Ou vous pouvez écrire vos URL comme ceci:

(r'^login/?$', 'mySite.myUser.views.login')

Le signe de question après la barre oblique finale le rend facultatif dans l'expression rationnelle. Utilisez-le si, pour certaines raisons, vous ne souhaitez pas utiliser le paramètre APPEND_SLASH.

Michael Gendin
la source
12
Appelez-moi naïf - mais pourquoi cette réponse n'a-t-elle pas reçu un million de votes positifs et une entrée dans la FAQ de Django?
Fergal Moran
42
À peu près sûr que vous ne voulez pas faire cela pour des raisons de référencement - mieux vaut rediriger vers une URL canonique que d'avoir deux URL valides.
Brian Frantz
47
Si vous créez une API RESTful à l'aide de Django, cela peut être une bonne solution lorsque les développeurs POSTENT des données directement sur l'URL du point de terminaison. Lors de l'utilisation APPEND_SLASH, s'ils l'ont envoyé accidentellement sans barre oblique de fin, et que votre urlconf est AVEC une barre oblique de fin, ils recevraient une exception concernant la perte de données lors de la redirection des requêtes POST.
OrPo
5
Le problème avec cette solution est que vous servez la même page sous 2 URL (avec et sans la fin /) - bâclée, mauvaise pour les robots d'exploration, plus difficile à maintenir, plus difficile à migrer vers un nouveau système (car il est si facile à négliger)
Jiaaro
Bonne réponse. Je préférerais interdire la barre oblique (car elle signifiait le début de quelque chose de nouveau, pas la fin de quelque chose (par exemple / etc), mais cela permet le standard (/ view) et le non-standard (/ view /).
David Betz
19

Cela améliore la réponse de @Michael Gendin. Sa réponse sert la même page avec deux URL distinctes. Il vaudrait mieux avoir loginautomatiquement redirigé vers login/, puis servir ce dernier comme page principale:

from django.conf.urls import patterns
from django.views.generic import RedirectView

urlpatterns = patterns('',
    # Redirect login to login/
    (r'^login$', RedirectView.as_view(url = '/login/')),
    # Handle the page with the slash.
    (r'^login/', "views.my_handler"),
)
speedplane
la source
Très utile lorsque vous avez une URL fourre-tout à la fin.
thclark
Comment cela pourrait-il fonctionner avec les expressions régulières? Si l'url d'origine correspond à une expression régulière avec un nom de client par exemple
Nicolò Gasparini
@ NicolòGasparini - les versions plus récentes de Django ont un pattern_nameargument qui est utilisé redirectavec tous les arguments d' url qui correspondent.
Tim Tisdall le
2

J'ai eu le même problème aussi. Ma solution a été placée un (| /) avant la dernière ligne de mon expression régulière.

url(r'^artists/(?P[\d]+)(|/)$', ArtistDetailView.as_view()),

Atahualpa Silva Falcón
la source
1

Ajoutez une barre oblique sans redirection , utilisez-la à la place de CommonMiddleware dans les paramètres, Django 2.1:

MIDDLEWARE = [
    ...
    # 'django.middleware.common.CommonMiddleware',
    'htx.middleware.CommonMiddlewareAppendSlashWithoutRedirect',
    ...
]

Ajoutez à votre répertoire d'applications principal middleware.py :

from django.http import HttpResponsePermanentRedirect, HttpRequest
from django.core.handlers.base import BaseHandler
from django.middleware.common import CommonMiddleware
from django.conf import settings


class HttpSmartRedirectResponse(HttpResponsePermanentRedirect):
    pass


class CommonMiddlewareAppendSlashWithoutRedirect(CommonMiddleware):
    """ This class converts HttpSmartRedirectResponse to the common response
        of Django view, without redirect.
    """
    response_redirect_class = HttpSmartRedirectResponse

    def __init__(self, *args, **kwargs):
        # create django request resolver
        self.handler = BaseHandler()

        # prevent recursive includes
        old = settings.MIDDLEWARE
        name = self.__module__ + '.' + self.__class__.__name__
        settings.MIDDLEWARE = [i for i in settings.MIDDLEWARE if i != name]

        self.handler.load_middleware()

        settings.MIDDLEWARE = old
        super(CommonMiddlewareAppendSlashWithoutRedirect, self).__init__(*args, **kwargs)

    def process_response(self, request, response):
        response = super(CommonMiddlewareAppendSlashWithoutRedirect, self).process_response(request, response)

        if isinstance(response, HttpSmartRedirectResponse):
            if not request.path.endswith('/'):
                request.path = request.path + '/'
            # we don't need query string in path_info because it's in request.GET already
            request.path_info = request.path
            response = self.handler.get_response(request)

        return response
Max Tkachenko
la source
0

J'ai eu le même problème. Dans mon cas, c'était un reste périmé d'une ancienne version de urls.py, d'avant staticfiles:

url(r'^%s(?P<path>.*)$' % settings.MEDIA_URL.lstrip('/'),
    'django.views.static.serve',
    kwargs={'document_root': settings.MEDIA_ROOT}),

MEDIA_URL était vide, donc ce modèle correspondait à tout.

janek37
la source