urls django sem uma barra final não redirecionam


89

Tenho dois aplicativos localizados em dois computadores separados. No computador A, no urls.pyarquivo, tenho uma linha como a seguinte:

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

E esse url funcionará para ambos mySite.com/cast/e mySite.com/cast. Mas no computador BI tem um url semelhante escrito como:

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

Por algum motivo, no computador B, o url mySite.com/login/ funcionará, mas mySite.com/logintravará e não retornará mySite.com/login/como no computador A. Há algo que perdi? Ambos os url.pyarquivos parecem idênticos a mim.

Respostas:


104

verifique sua APPEND_SLASHconfiguração no arquivo settings.py

mais informações nos documentos do django


4
"Quando definido como True, se o URL de solicitação não corresponder a nenhum dos padrões no URLconf e não terminar em uma barra, um redirecionamento de HTTP é emitido para o mesmo URL com uma barra anexada. Observe que o redirecionamento pode causar quaisquer dados enviados em uma solicitação POST serão perdidos. ". "A configuração APPEND_SLASH é usada apenas se CommonMiddleware estiver instalado ...". Prefiro a resposta de Michael Gendin para uma solução mais limpa.
— Torre de Vigia de

3
Isso não funcionará se você estiver usando um url "catch all" adicional na última entrada de seus urlpatterns. A resposta de @ speedplane funcionará mesmo nessas situações. Mas, é claro, isso é mais simples e deve ser usado se não houver entradas de urlpattern "catch all".
— np8 de

196

Ou você pode escrever seus urls assim:

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

O sinal de pergunta após a barra final o torna opcional em regexp. Use-o se, por alguns motivos, você não quiser usar a configuração APPEND_SLASH.


12
Pode me chamar de ingênuo - mas por que essa resposta não teve um milhão de votos positivos e uma entrada no faq do django?
— Fergal Moran

42
Tenho certeza de que você não deseja fazer isso por motivos de SEO - melhor redirecionar para uma URL canônica do que ter duas URLs válidas.
— Brian Frantz

47
Se você estiver criando uma API RESTful usando Django, esta pode ser uma boa solução quando os desenvolvedores POSTAR dados diretamente para a URL do endpoint. Ao usar APPEND_SLASH, se eles o enviarem acidentalmente sem uma barra final, e seu urlconf estiver COM uma barra final, eles obteriam uma exceção sobre perda de dados ao redirecionar solicitações POST.
— OrPo

5
O problema com esta solução é que você está exibindo a mesma página em 2 urls (com e sem o trailing /) - desleixado, ruim para rastreadores, mais difícil de manter, mais difícil de migrar para um novo sistema (já que é tão fácil de ignorar)
— Jiaaro

1
Um pouco fora do assunto (Django / Python), mas como alguém com anos de experiência em SEO, posso dizer que se você deseja otimizar para mecanismos de pesquisa, NÃO deseja 2 versões da mesma URL. site.com/users é um URL diferente de site.com/users/ Isso não é o que você deseja para o SEO. Você precisa de apenas 1 versão de um url e conteúdo! Escolha apenas uma versão e certifique-se de redirecionar a outra corretamente.
— Dani

19

Isso melhora a resposta de @Michael Gendin. Sua resposta exibe a página idêntica com dois URLs separados. Seria melhor loginredirecionar automaticamente para login/e, em seguida, servir o último como a 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"),
)

Muito útil quando você tem uma URL pega-tudo no final.
— thclark

Como isso funcionaria com regexs? Se o url original corresponder a um regex com um nome de cliente, por exemplo
— Nicolò Gasparini

@ NicolòGasparini - versões mais recentes do Django possuem um pattern_namearg que é usado redirectjunto com todos os args de url que são correspondidos.
— Tim Tisdall

2

Eu também tive o mesmo problema. Minha solução foi colocada um (| /) antes da linha final da minha expressão regular.

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


1

Anexe a barra sem redirecionar , use-a em vez de CommonMiddleware nas configurações, Django 2.1:

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

Adicione ao diretório principal do aplicativo 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

0

Eu tive o mesmo problema. No meu caso, era uma sobra desatualizada de alguma versão antiga em urls.py, anterior a arquivos estáticos:

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

MEDIA_URL estava vazio, então esse padrão correspondeu a tudo.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.