Um esquema de URLs limpo e elegante é um detalhe importante numa aplicação Web de qualidade. O Django permite que você defina as URLs da maneira que quiser, sem limitações impostas pelo framework.
Não são necessários .php, .cgi ou outras coisas sem o menor sentido como 0,2097,1-1-1928,00.
Veja Cool URIs don’t change, pelo criador da World Wide Web Tim Berners-Lee, para ter excelentes argumentos do porquê as URLs devem ser limpas e facilmente utilizáveis.
Para definir URLs para uma aplicação, você cria um módulo Python informalmente chamado de URLconf (URL configuration). Este módulo é Python puro e é um mapeamento simples entre padrões de URL (na forma de expressões regulares simples) para funções Python de callback (suas views).
Este mapeamento pode ser tão extenso ou curto quanto necessário. Ele pode referenciar outros mapeamentos. E, por ser código Python puro, pode ser construído dinamicamente.
Quando um usuário requisita uma página de nosso site em Django, este é o algoritmo que o sistema segue para determinar qual código Python irá executar:
Aqui está um URLconf de exemplo:
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^articles/2003/$', 'news.views.special_case_2003'),
(r'^articles/(\d{4})/$', 'news.views.year_archive'),
(r'^articles/(\d{4})/(\d{2})/$', 'news.views.month_archive'),
(r'^articles/(\d{4})/(\d{2})/(\d+)/$', 'news.views.article_detail'),
)
Notas:
Requisições de exemplo:
O exemplo acima usou grupos de expressões regulares simples e não-nomeados (usando parênteses) para capturar pedaços da URL e passá-los como argumentos posicionais para a view. Em um uso mais avançado, é possível utilizar grupos nomeados de expressões regulares para capturar pedaços de URL e passá-los como argumentos nomeados para a view.
Em expressões regulares Python, a sintaxe para grupos nomeados é (?P<nome>padrão), onde nome é o nome do grupo e padrão é algum padrão a ser correspondido.
Segue o URLconf do exemplo acima, reescrita para utilizar grupos nomeados:
urlpatterns = patterns('',
(r'^articles/2003/$', 'news.views.special_case_2003'),
(r'^articles/(?P<year>\d{4})/$', 'news.views.year_archive'),
(r'^articles/(?P<year>\d{4})/(?P<month>\d{2})/$', 'news.views.month_archive'),
(r'^articles/(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d+)/$', 'news.views.article_detail'),
)
Este exemplo faz exatamente a mesma coisa que o anterior, com uma pequena diferença: Os valores capturados são passados para as funções de view como argumentos nomeados em vez de argumentos posicionais. Por exemplo:
Na prática, isso significa que seus URLconfs são um pouco mais explícitos e menos propensos a bugs de ordem de argumentos -- e você pode reordenar os argumentos nas definições de função de view. Obviamente, estes benefícios são obtidos ao custo da brevidade; alguns desenvolvedores acham a sintaxe de grupos nomeados feia e muito prolixa.
Eis o algoritmo que o parser URLconf segue, no que diz respeito a grupos nomeados contra grupos não-nomeados em uma expressão regular:
Se existe algum argumento nomeado, ele será utilizado, ignorando argumentos não-nomeados. Caso contrário, ele passará todos os argumentos não-nomeados como argumentos posicionais.
Em ambos os casos, serão passados quaisquer argumentos nomeados adicionais como argumentos nomeados. Veja "Passando opções adicionais para funções de view" abaixo.
O URLconf faz a busca de correspondência na URL requisitada, como uma string Python normal. Isso não inclui parâmetros GET ou POST, ou do nome de domínio.
Por exemplo, numa requisição para http://www.example.com/myapp/, o URLconf irá procurar por myapp/.
Em uma requisição para http://www.example.com/myapp/?page=3, o URLconf irá procurar por myapp/.
O URLconf não se importa com o método de requisição. Em outras palavras, todos os métodos de requisição -- POST, GET, HEAD, etc. -- serão roteados para a mesma função da mesma URL.
urlpatterns deve ser uma lista Python no formato retornado pela função django.conf.urls.defaults.patterns(). Sempre use patterns() para criar a variável urlpatterns.
A convenção é usar from django.conf.urls.defaults import * no topo de seu URLconf. Isso dá ao módulo acesso a estes objetos:
Uma função que recebe um prefixo e um número arbitrário de padrões de URL, e retorna uma lista de padrões de URL no formato de que o Django precisa.
O primeiro argumento para patterns() é uma string prefix. Veja O prefixo da view abaixo.
Os argumentos restantes devem ser tuplas neste formato:
(expressão regular, função de callbak Python, [, dicionário opcional [, nome opcional]])
...onde dicionário opcional e nome opcional são opcionais. (Veja Passando opções adicionais para funções de view abaixo.)
Note
Por patterns() ser uma chamada de função, ela aceita um máximo de 255 argumentos (padrões de URL, neste caso). Isso é um limite para todas as chamadas de função Python. Isso raramente é um problema na prática, porque você normalmente estrutura seus padrões de URL modularmente usando seções include(). Entretanto, se mesmo assim você chegar ao limite de 255 argumentos, perceba que patterns() retorna uma lista Python, então você pode dividir a construção da lista.
urlpatterns = patterns('',
...
)
urlpatterns += patterns('',
...
)
As listas Python possuem um tamanho ilimitado, então não há um número máximo para quantos padrões de URL você pode construir. O único limite é que você só pode criar 254 de cada vez (o 255o argumento é o prefixo inicial).
Você pode utilizar a função url() no lugar de uma tupla, como um argumento para patterns(). Isso é conveniente se você quer especificar um nome sem utilizar o dicionário de argumentos opcionais adicionais. Por exemplo:
urlpatterns = patterns('',
url(r'^index/$', index_view, name="main-view"),
...
)
Esta função recebe cinco argumentos, onde a maioria é opcional:
url(regex, view, kwargs=None, name=None, prefix='')
Veja Nomeando padrões de URL para saber da utilidade do parâmetro name.
O parâmetro prefix tem o mesmo significado que o primeiro argumento de patterns() e somente é relevante quando você está passando uma string como um parâmetro para a view.
Uma string representando o caminho completo para a importação Python da view que deve ser chamada se nenhum dos padrões de URL corresponder.
Por padrão, este valor é 'django.views.defaults.page_not_found', que na maioria das vezes deve ser suficiente.
Uma string representando o caminho completo para a importação Python da view que deve ser chamada em caso de erros no servidor. Erros no servidor acontecem quando você tem erros em tempo de execução no código da sua view.
Por padrão, este valor é 'django.views.defaults.server_error', que na maioria das vezes deve ser suficiente.
Uma função que recebe o caminho completo para a importação Python de outro URLconf que deve ser "incluído" neste local. Veja Incluindo outros URLconfs abaixo.
include() also accepts as an argument an iterable that returns URL patterns.
Veja Incluindo outros URLconfs abaixo.
Cada argumento capturado é enviado para a view como uma string Python, independente do tipo de correspondência feita pela expressão regular. Por exemplo, nesta linha do URLconf:
(r'^articles/(?P<year>\d{4})/$', 'news.views.year_archive'),
...o argumento year para news.views.year_archive() será uma string, não um inteiro, embora o \d{4} só vá corresponder a strings que contenham números inteiros.
Um truque conveniente é especificar parâmetros padrão para os argumentos de suas views. Aqui vai um exemplo de URLconf e view:
# URLconf
urlpatterns = patterns('',
(r'^blog/$', 'blog.views.page'),
(r'^blog/page(?P<num>\d+)/$', 'blog.views.page'),
)
# View (em blog/views.py)
def page(request, num="1"):
# Mostra a página apropriada das postagens de blog, de acordo com num.
No exemplo acima, os dois padrões de URL apontam para a mesma view -- blog.views.page --, mas o primeiro padrão não captura nada da URL. Se o primeiro padrão corresponder, a função page() usará seu argumento padrão para num, "1". Se o segundo padrão corresponder, page() usará o valor de num que foi capturado pela expressão regular.
Cada expressão regular em um urlpatterns é compilada na primeira vez em que é accessada. Isso torna o sistema extremamente rápido.
Você pode especificar um prefixo comum em sua chamada patterns() para diminuir a duplicação de código.
Segue um exemplo de URLconf da Visão geral do Django:
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^articles/(\d{4})/$', 'mysite.news.views.year_archive'),
(r'^articles/(\d{4})/(\d{2})/$', 'mysite.news.views.month_archive'),
(r'^articles/(\d{4})/(\d{2})/(\d+)/$', 'mysite.news.views.article_detail'),
)
Neste exemplo, cada view tem um prefixo comum -- 'mysite.news.views'. Em vez de digitar isso para cada entrada em urlpatterns, você pode usar o primeiro argumento da função patterns() para especificar o prefixo que se aplica a cada função de view.
Com isso em mente, o exemplo acima pode ser reescrito de maneira mais concisa assim:
from django.conf.urls.defaults import *
urlpatterns = patterns('news.views',
(r'^articles/(\d{4})/$', 'year_archive'),
(r'^articles/(\d{4})/(\d{2})/$', 'month_archive'),
(r'^articles/(\d{4})/(\d{2})/(\d+)/$', 'article_detail'),
)
Note que você não coloca um ponto final (".") no prefixo. O Django coloca isso automaticamente.
Na prática, você provavelmente acabará misturando views ao ponto em que as views em seu urlpatterns não terão um prefixo comum. Entretanto, você ainda pode tirar vantagem do atalho de prefixo da view para remover código duplicado. Simplesmente concatene múltiplos objetos patterns(), assim:
Antigo:
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^$', 'django.views.generic.date_based.archive_index'),
(r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/$', 'django.views.generic.date_based.archive_month'),
(r'^tag/(?P<tag>\w+)/$', 'weblog.views.tag'),
)
Novo:
from django.conf.urls.defaults import *
urlpatterns = patterns('django.views.generic.date_based',
(r'^$', 'archive_index'),
(r'^(?P<year>\d{4})/(?P<month>[a-z]{3})/$','archive_month'),
)
urlpatterns += patterns('weblog.views',
(r'^tag/(?P<tag>\w+)/$', 'tag'),
)
Em qualquer ponto, seu urlpatterns pode "incluir" outros módulos URLconf. Isso essencialmente "inclui" um conjunto de URLs abaixo de outras.
Por exemplo, eis o URLconf para o próprio Web site do Django. Ele inclui um número de outros URLconfs:
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^weblog/', include('django_website.apps.blog.urls.blog')),
(r'^documentation/', include('django_website.apps.docs.urls.docs')),
(r'^comments/', include('django.contrib.comments.urls.comments')),
)
Note que as expressões regulares neste exemplo não possue um $ (caractere que corresponde ao final de string), mas incluem a barra final. Sempre que o Django encontra include(), ele recorta qualquer parte da URL correspondida até aquele ponto e envia a string restante ao URLconf incluído para processamento adicional.
Another possibility is to include additional URL patterns not by specifying the URLconf Python module defining them as the include argument but by using directly the pattern list as returned by patterns instead. For example:
from django.conf.urls.defaults import *
extra_patterns = patterns('',
url(r'reports/(?P<id>\d+)/$', 'credit.views.report', name='credit-reports'),
url(r'charge/$', 'credit.views.charge', name='credit-charge'),
)
urlpatterns = patterns('',
url(r'^$', 'apps.main.views.homepage', name='site-homepage'),
(r'^help/', include('apps.help.urls')),
(r'^credit/', include(extra_patterns)),
)
This approach can be seen in use when you deploy an instance of the Django Admin application. The Django Admin is deployed as instances of a AdminSite; each AdminSite instance has an attribute urls that returns the url patterns available to that instance. It is this attribute that you include() into your projects urlpatterns when you deploy the admin instance.
Um URLconf incluído recebe quaisquer parâmetros capturados do URLconf pai, então o seguinte exemplo é válido:
# Em settings/urls/main.py
urlpatterns = patterns('',
(r'^(?P<username>\w+)/blog/', include('foo.urls.blog')),
)
# Em foo/urls/blog.py
urlpatterns = patterns('foo.views',
(r'^$', 'blog.index'),
(r'^archive/$', 'blog.archive'),
)
No exemplo acima, a variável "username" capturada é passada para o URLconf, como esperado.
When you need to deploy multiple instances of a single application, it can be helpful to be able to differentiate between instances. This is especially important when using named URL patterns, since multiple instances of a single application will share named URLs. Namespaces provide a way to tell these named URLs apart.
A URL namespace comes in two parts, both of which are strings:
URL Namespaces can be specified in two ways.
Firstly, you can provide the application and instance namespace as arguments to include() when you construct your URL patterns. For example,:
(r'^help/', include('apps.help.urls', namespace='foo', app_name='bar')),
This will include the URLs defined in apps.help.urls into the application namespace bar, with the instance namespace foo.
Secondly, you can include an object that contains embedded namespace data. If you include() a patterns object, that object will be added to the global namespace. However, you can also include() an object that contains a 3-tuple containing:
(<patterns object>, <application namespace>, <instance namespace>)
This will include the nominated URL patterns into the given application and instance namespace. For example, the urls attribute of Django's AdminSite object returns a 3-tuple that contains all the patterns in an admin site, plus the name of the admin instance, and the application namespace admin.
Once you have defined namespaced URLs, you can reverse them. For details on reversing namespaced urls, see the documentation on reversing namespaced URLs.
URLconfs possuem um "hook" que permite passar argumentos adicionais para suas funções de view como um dicionário Python.
Qualquer tupla URLconf pode ter um terceiro elemento opcional, que deve ser um dicionário de argumentos nomeados adicionais a serem passados para a função de view.
Por exemplo:
urlpatterns = patterns('blog.views',
(r'^blog/(?P<year>\d{4})/$', 'year_archive', {'foo': 'bar'}),
)
Neste exemplo, numa requisição para /blog/2005/, o Django chamará a view blog.views.year_archive(), passando estes argumentos nomeados:
year='2005', foo='bar'
Essa técnica é utilizada em views genéricas e em syndication framework para passar metadados e opções para as views.
Lidando com conflitos
É possível ter um padrão de URL que captura argumentos nomeados e também passa argumentos com os mesmos nomes no seu dicionário de argumentos adicionais. Quando isso acontece, os argumentos no dicionário serão usados em vez dos argumentos capturados na URL.
Do mesmo modo, você pode passar opções adicionais para include(). Quando você passa opções adicionais para include(), elas serão passadas para cada linha no URLconf incluído.
Por exemplo, estes dois conjuntos URLconf são funcionalmente idênticos:
Conjunto 1:
# main.py
urlpatterns = patterns('',
(r'^blog/', include('inner'), {'blogid': 3}),
)
# inner.py
urlpatterns = patterns('',
(r'^archive/$', 'mysite.views.archive'),
(r'^about/$', 'mysite.views.about'),
)
Conjunto 2:
# main.py
urlpatterns = patterns('',
(r'^blog/', include('inner')),
)
# inner.py
urlpatterns = patterns('',
(r'^archive/$', 'mysite.views.archive', {'blogid': 3}),
(r'^about/$', 'mysite.views.about', {'blogid': 3}),
)
Note que as opções adicionais serão sempre passadas para cada linha no URLconf incluído, independentemente se a view da linha aceita as opções como válidas. Por este motivo, esta técnica somente é útil se você está seguro de que cada view no URLconf incluído aceita as opções adicionais que você está passando.
Alguns desenvolvedores acham mais natural passar o próprio objeto de função Python do que a string contendo o caminho para seu módulo. Esta alternativa é suportada -- você pode passar qualquer objeto que pode ser chamado como a view.
Por exemplo, dado este URLconf na notação de "string":
urlpatterns = patterns('',
(r'^archive/$', 'mysite.views.archive'),
(r'^about/$', 'mysite.views.about'),
(r'^contact/$', 'mysite.views.contact'),
)
Você pode obter a mesma coisa passando objetos em vez de strings. Apenas assegure-se de importar os objetos:
from mysite.views import archive, about, contact
urlpatterns = patterns('',
(r'^archive/$', archive),
(r'^about/$', about),
(r'^contact/$', contact),
)
O exemplo seguinte é funcionalmente idêntico. Somente um pouco mais compacto porque importa o módulo que contém as views, em vez de importar cada view individualmente:
from mysite import views
urlpatterns = patterns('',
(r'^archive/$', views.archive),
(r'^about/$', views.about),
(r'^contact/$', views.contact),
)
O estilo que você usa fica por sua conta.
Note que se você usa esta técnica -- passando objetos em vez de strings --, o prefixo da view (como explicado em "O prefixo da view" acima) não terá efeito.
É bastante comum usar a mesma função de view em múltiplos padrões de URL em seu URLconf. Por exemplo, estes dois padrões de URL apontam para a view archive:
urlpatterns = patterns('',
(r'^archive/(\d{4})/$', archive),
(r'^archive-summary/(\d{4})/$', archive, {'summary': True}),
)
Isso é complemente válido, mas ocasiona problemas quando você tenta fazer uma correspondência de URL reversa (através do decorator permalink() ou a tag url). Continuando este exemplo, se você quisesse recuperar a URL para a view archive, o mecanismo de correspondência de URL reverso do Django ficaria confuso, pois dois padrões de URL apontam para esta view.
Para resolver este problema, o Django suporta padrões de URL nomeados. Ou seja, você pode dar nomes para um padrão de URL afim de distingui-lo de outros padrões usando a mesma view e parâmetros. Então, você pode usar este nome na correspondência reversa de URL.
Veja o exemplo acima, reescrito para utilizar padrões de URL nomeados:
urlpatterns = patterns('',
url(r'^archive/(\d{4})/$', archive, name="full-archive"),
url(r'^archive-summary/(\d{4})/$', archive, {'summary': True}, "arch-summary"),
)
Com estes nomes no lugar (full-archive and arch-summary), você pode apontar cada padrão individualmente utilizando seu nome:
.. code-block:: html+django
{% url arch-summary 1945 %} {% url full-archive 2007 %}
Apesar de ambos padrões de URL referirem-se à view archive, ao usar o parâmetro name de url() é possível distigui-los em templates.
A string usada para o nome da URL pode conter quaisquer caracteres que você queira. Você não está restrito a nomes válidos em Python.
Note
Quando você nomear seus padrões de URL, certifique-se de que está utilizando nomes que não têm chances óbvias de colidir com nomes escolhidos em outras aplicações. Se você chama seu padrão de URL comment, e outra aplicação faz a mesma coisa, não há garantias de qual URL será inserida no seu template quando você usa este nome.
Colocar um prefixo nos nomes de suas URLs, talvez derivado do nome da aplicação, diminuirá as chances de colisão. Recomendamos algo como myapp-comment em vez de comment.
Namespaced URLs are specified using the : operator. For example, the main index page of the admin application is referenced using admin:index. This indicates a namespace of admin, and a named URL of index.
Namespaces can also be nested. The named URL foo:bar:whiz would look for a pattern named whiz in the namespace bar that is itself defined within the top-level namespace foo.
When given a namespaced URL (e.g. myapp:index) to resolve, Django splits the fully qualified name into parts, and then tries the following lookup:
First, Django looks for a matching application namespace (in this example, myapp). This will yield a list of instances of that application.
If there is a current application defined, Django finds and returns the URL resolver for that instance. The current application can be specified as an attribute on the template context - applications that expect to have multiple deployments should set the current_app attribute on any Context or RequestContext that is used to render a template.
The current application can also be specified manually as an argument to the reverse() function.
If there is no current application. Django looks for a default application instance. The default application instance is the instance that has an instance namespace matching the application namespace (in this example, an instance of the myapp called myapp).
If there is no default application instance, Django will pick the last deployed instance of the application, whatever its instance name may be.
If the provided namespace doesn't match an application namespace in step 1, Django will attempt a direct lookup of the namespace as an instance namespace.
If there are nested namespaces, these steps are repeated for each part of the namespace until only the view name is unresolved. The view name will then be resolved into a URL in the namespace that has been found.
To show this resolution strategy in action, consider an example of two instances of myapp: one called foo, and one called bar. myapp has a main index page with a URL named index. Using this setup, the following lookups are possible:
If there was also a default instance - i.e., an instance named myapp - the following would happen:
Se você precisa usar algo similar a tag de template url no seu código, o Django provê um método (no módulo django.core.urlresolvers):
viewname é o nome da função (podendo ser uma referência para a função, ou o nome dela como uma string, se foi utilizado desta forma em urlpatterns) ou o Nome do padrão de URL. Normalmente, você não precisa se preocupar com o parâmetro urlconf e irá passar somente os argumentos posicionais e nomeados para serem usados na correspondência de URL. Por exemplo:
from django.core.urlresolvers import reverse
def myview(request):
return HttpResponseRedirect(reverse('arch-summary', args=[1945]))
A função reverse() pode reverter uma grande variedade de padrões de expressões regulares nas URLs, mas não todas. A principal restrição é quando o padrão não contém escolhas alternativas, como quando se usa o caracter barra vertical ("|"). Você pode utilizar esses padrões tranquilamente para corresponderem às requisições recebidas e envia-las para os views, mas você não pode reverter tais padrões.
The current_app argument allows you to provide a hint to the resolver indicating the application to which the currently executing view belongs. This current_app argument is used as a hint to resolve application namespaces into URLs on specific application instances, according to the namespaced URL resolution strategy.
Esteja certo de que suas views estão todas corretas
Como parte do trabalho no qual os nomes de URL mapeiam os padrões, a função reverse() tem de importar todos os seus arquivos URLconf e examinar o nome de cada view. Isto envolve importar todos as suas funções view. Se houver qualquer erro enquanto importa qualquer uma de suas funções views, irá fazer o reverse() gerar um erro, mesmo que para tal função não esteja destinado o reverse.
Esteja certo que qualquer view referenciado nos seus arquivos URLconf existam e que possam ser importados corretamente. Não inclua linhas que apontem para views que você ainda não criou, por que estas views não serão importáveis.
A função django.core.urlresolvers.resolve() pode ser usada para resolver caminhos de URL que correspondem a um funções view. Ela possui a seguinte assinatura:
path é o caminho da URL que você deseja resolver. Como com reverse() acima, você não precisa se preocupar com o parâmetro urlconf. A função retorna uma tuple tripla (função view, argumentos, argumentos nomeados).
If the URL does not resolve, the function raises an Http404 exception.s
Por exemplo, ela pode ser usada para testar se um view geraria um erro Http404 antes de redirecioná-lo:
from urlparse import urlparse
from django.core.urlresolvers import resolve
from django.http import HttpResponseRedirect, Http404
def myview(request):
next = request.META.get('HTTP_REFERER', None) or '/'
response = HttpResponseRedirect(next)
# modifica a requisição e a resposta quando necessário, e.g. mudar
# localidade e setar o cookie correspondente a localidade
view, args, kwargs = resolve(urlparse(next)[2])
kwargs['request'] = request
try:
view(*args, **kwargs)
except Http404:
return HttpResponseRedirect('/')
return response
O decorator django.db.models.permalink`() é útil para escrever métodos curtos que retornam o caminho inteiro da URL. Por exemplo, o método get_absolute_url() do modelo. Consulte django.db.models.permalink() para mais informações.
Normally, you should always use reverse() or permalink() to define URLs within your application. However, if your application constructs part of the URL hierarchy itself, you may occasionally need to generate URLs. In that case, you need to be able to find the base URL of the Django project within its web server (normally, reverse() takes care of this for you). In that case, you can call get_script_prefix(), which will return the script prefix portion of the URL for your Django project. If your Django project is at the root of its webserver, this is always "/", but it can be changed, for instance by using django.root (see How to use Django with Apache and mod_python).
Dec 26, 2011