Las URL de django sin una barra al final no redireccionan

89

Tengo dos aplicaciones ubicadas en dos computadoras separadas. En la computadora A, en el urls.pyarchivo tengo una línea como la siguiente:

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

Y esa URL funcionará para ambos mySite.com/cast/y mySite.com/cast. Pero en la computadora BI, tenga una URL similar escrita como:

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

Por alguna razón, en la computadora B, el url mySite.com/login/ funcionará pero mySite.com/loginse bloqueará y no volverá mySite.com/login/como lo haría en la computadora A. Ambos url.pyarchivos me parecen idénticos.

que que
fuente

Respuestas:

103

verifique su APPEND_SLASHconfiguración en el archivo settings.py

más información en los documentos de django

Jiaaro
fuente
4
"Cuando se establece en Verdadero, si la URL de la solicitud no coincide con ninguno de los patrones de la URLconf y no termina en una barra, se emite una redirección HTTP a la misma URL con una barra inclinada. Tenga en cuenta que la redirección puede causar todos los datos enviados en una solicitud POST se perderán. ". "La configuración APPEND_SLASH solo se usa si CommonMiddleware está instalado ...". Prefiero la respuesta de Michael Gendin para una solución más limpia.
Wtower
3
Esto no funciona si está utilizando una URL adicional "captura todo" en la última entrada de sus patrones de URL. La respuesta de @ speedplane funcionará incluso en esas situaciones. Pero, por supuesto, esto es más simple y debe usarse si no hay entradas de patrón de URL "capturar todas".
np8
195

O puede escribir sus URL de esta manera:

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

El signo de pregunta después de la barra diagonal al final lo hace opcional en regexp. Úselo si por alguna razón no desea usar la configuración APPEND_SLASH.

Michael Gendin
fuente
12
Llámame ingenuo, pero ¿por qué esta respuesta no tiene un millón de votos a favor y una entrada en las preguntas frecuentes de django?
Fergal Moran
42
Estoy bastante seguro de que no desea hacer esto por razones de SEO; es mejor redirigir a una URL canónica que tener dos URL válidas.
Brian Frantz
47
Si está creando una API RESTful usando Django, esta puede ser una buena solución cuando los desarrolladores envían datos directamente a la URL del punto final. Al usarlo APPEND_SLASH, si lo enviaron accidentalmente sin barra diagonal final, y su urlconf está CON una barra diagonal final, obtendrán una excepción sobre la pérdida de datos al redirigir las solicitudes POST.
OrPo
5
El problema con esta solución es que está publicando la misma página en 2 URL (con y sin el final /): descuidado, malo para los rastreadores, más difícil de mantener, más difícil de migrar a un nuevo sistema (ya que es muy fácil pasarlo por alto)
Jiaaro
Buena respuesta. Preferiría no permitir la barra (ya que significa el comienzo de algo nuevo, no el final de algo (por ejemplo, / etc), pero esto permite el estándar (/ view) y el no estándar (/ view /).
David Betz
19

Esto mejora la respuesta de @Michael Gendin. Su respuesta muestra la página idéntica con dos URL separadas. Sería mejor tener loginla redirección automática login/y luego servir esta última como la página principal:

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"),
)
avión de velocidad
fuente
Muy útil cuando tiene una URL general al final.
thclark
¿Cómo podría funcionar esto con expresiones regulares? Si la URL original coincide con una expresión regular con un nombre de cliente, por ejemplo
Nicolò Gasparini
@ NicolòGasparini: las versiones más nuevas de Django tienen un pattern_nameargumento que se usa redirectjunto con todos los argumentos de URL que coinciden.
Tim Tisdall
2

Yo también tuve el mismo problema. Mi solución fue poner un (| /) antes de la línea final de mi expresión regular.

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

Atahualpa Silva Falcón
fuente
1

Agregue una barra sin redireccionar , úsela en lugar de CommonMiddleware en la configuración, Django 2.1:

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

Agregue a su directorio principal de aplicaciones 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
fuente
0

Tuve el mismo problema. En mi caso, era un sobrante obsoleto de alguna versión anterior en urls.py, de antes de staticfiles:

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

MEDIA_URL estaba vacío, por lo que este patrón coincidía con todo.

janek37
fuente