Django named routes like Laravel - python

I'm starting to learn about Django Framework. I have this urls in my urls.py
from django.conf.urls import patterns, include, url
urlpatterns = patterns('',
# Examples:
url(r'^hello/$','article.views.hello'),
)
My questions are: Can I give this routes a name, like i would in Laravel? How can i reference those named routes from template?
Thanks!

Yes, you can:
url(r'^hello/$','article.views.hello', name="hello"),
You would reference it in a template as:
{% url 'hello' %}
For more information, including how to give arguments to a named URL, see here.

Related

Django apps resolving to the wrong namespace

In my project I have three apps, "abc", "xyz" and "common." Common isn't a real app inasmuch as it just stores templates, models and views that are inherited and extended by both apps.
Project-level urls.py looks like so, and properly redirects requests to the respective app:
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^abc/', include('abc.urls')),
url(r'^xyz/', include('xyz.urls')),
]
Both apps' url.py files look like so; the ONLY difference is replace every instance of ABC with XYZ:
from django.conf.urls import url
from views import ABCAlertList as AlertList
from views import ABCEventList as EventList
from views import ABCEventDetail as EventDetail
from views import ABCEventReplay as EventReplay
from views import ABCUploadView as UploadView
urlpatterns = [
url(r'^$', AlertList.as_view(), name='view_alerts'),
url(r'^entities/(?P<uid>\w+)/$', EventList.as_view(), name='view_uid'),
url(r'^entities/(?P<uid>\w+)/replay/$', EventReplay.as_view(), name='view_replay'),
url(r'^entities/(?P<uid>\w+)/event/(?P<eid>\w+)/$', EventDetail.as_view(), name='view_event'),
url(r'^upload/$', UploadView.as_view(), name='upload_file'),
]
Again, all the views are common between both apps so there is nothing app-specific to either of them. Both apps make use of the same line in the same common template:
<a href="{% url 'view_uid' alert.uid %}">
Now, the problem:
App ABC works fine on the top-level page. But the urls it's rendering to go past that point point to the wrong app.
For example, I'll be in
http://localhost:8888/abc/
and the urls on that page render as
http://localhost:8888/xyz/entities/262b3bce18e71c5459a41e1e6d52a946ab47e88f/
What gives? It looks like Django is reading the wrong app's urls.py.
Django can't tell the difference between the URLs under abc/ and xyz/ just by the view name and arguments. Since reversing will go through the patterns in reverse order, the patterns under xyz/ will always match first, so all links generated using reverse() or the {% url %} tag will point to the xyz app.
You need to give each pattern a unique name, or use a URL namespace. In Django 1.9+ you should set the app_name attribute:
app_name = 'abc'
urlpatterns = [
url(r'^$', AlertList.as_view(), name='view_alerts'),
url(r'^entities/(?P<uid>\w+)/$', EventList.as_view(), name='view_uid'),
url(r'^entities/(?P<uid>\w+)/replay/$', EventReplay.as_view(), name='view_replay'),
url(r'^entities/(?P<uid>\w+)/event/(?P<eid>\w+)/$', EventDetail.as_view(), name='view_event'),
url(r'^upload/$', UploadView.as_view(), name='upload_file'),
]
In Django 1.8 you need to pass the namespace parameter to include():
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^abc/', include('abc.urls', namespace='abc')),
url(r'^xyz/', include('xyz.urls', namespace='xyz')),
]
You can then reverse the url by passing the proper namespace:
<a href="{% url 'abc:view_uid' alert.uid %}">
If you need to use the same templates or functions for both apps, you need to set the application namespace of both apps to be the same, but use a different instance namespace.
In 1.9+, this means using the same app_name attribute, but passing a different namespace argument:
# myapp/urls.py
app_name = 'common_app_name'
urlpatterns = [
# app urls
]
# myproject/urls.py
urlpatterns = [
url(r'^abc/', include('abc.urls', namespace='abc')),
url(r'^xyz/', include('xyz.urls', namespace='xyz')),
]
In templates, you need to use the application namespace to reverse urls. The current instance namespace is automatically taken into account. In calls to reverse() you need to pass the current namespace:
reverse('common_app_name:view_alerts', current_app=request.resolver_match.namespace)
In Django 1.8 you don't have the app_name attribute, you need to pass it as a parameter to include():
urlpatterns = [
url(r'^abc/', include('abc.urls', namespace='abc', app_name='common_app_name')),
url(r'^xyz/', include('xyz.urls', namespace='xyz', app_name='common_app_name')),
]
Django 1.8 also won't automatically use the current instance namespace in calls to the {% url %} tag. You need to set the request.current_app attribute for that:
def my_view(request):
request.current_app = request.resolver_match.namespace
...

Django Haystack Reverse URL Failing

I am currently trying to configure the most barebones possible setup for Django Haystack. In my HTML, I have a form that resolves a url using a named url pattern. Here is the HTML code.
<form id="search-ticket-form" class="navbar-form navbar-left dropdown" method="get"
action="{% url "search_ticket" %}" role="search">
Django returns an error every time saying "Reverse for 'search_ticket' with arguments '()' and keyword arguments '{}' not found. 0 pattern(s) tried: []"
Here is the configuration in my urls.py:
urlpatterns = patterns('',
url(r'^$', contact.views.home, name='homepage'),
#url(r'^blog/', include('zinnia.urls', namespace='zinnia')),
url(r'^profile/', include('user_profile.urls')),
url(r'^registration/', include('registration.urls')),
url(r'^comments/', include('django_comments.urls')),
url(r'^contact/', include('contact.urls')),
url(r'^tickets/', include('tickets.urls')),
url(r'^admin/', include(admin.site.urls)),
url(r'^search/', include('haystack.urls')),
) + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
Here is the configuration in my tickets/urls.py:
urlpatterns = patterns('',
url(r'submit_ticket/$', submit_ticket, name='submit_ticket'),
url(r'search_ticket/$', include('haystack.urls'), name='search_ticket')
)
The setup certainly looks fine. When I substitute include('haystack.urls') for a function based view named 'abc', the url resolves just fine. This makes me think that something is wrong with my Django Haystack setup, but the error certainly is misleading. Here is what my single Haystack view looks like:
class TicketIndex(indexes.BasicSearchIndex, indexes.Indexable):
def get_model(self):
return Ticket
I modeled this setup after the barebones example in Haystack's github repo (https://github.com/toastdriven/django-haystack/blob/master/example_project/bare_bones_app/search_indexes.py).
Any thoughts on what is going on here?
Can you actually name an included URL tree? It usually includes multiple URL patterns. Looking at haystack.urls you may want to try {% url "haystack_search"%}.

Django: Named Url's/ Same regex, different Names

I have a urls.py file which contains multiple urls with same named paramaters and regex but different names. When I call a view function from the template using {% url 'name' param %} it calls the function which comes first in the urls.py file in disregard to the name. Here is the content of urls.py:
urlpatterns = patterns('accountsearch.views',
url(r'^$', 'account_search', name='account_search'),
url(r'(?P<account_uuid>[a-zA-Z0-9\-]+)/(?P<user_uuid>[a-zA-Z0-9\-]+)/$','reset_password',name='reset_password'),
url(r'(?P<account_uuid>[a-zA-Z0-9\-]+)/(?P<user_uuid>[a-zA-Z0-9\-]+)/$','reset_securityquestions',name='reset_securityquestions'),
I am trying to call reset_securityquestions from the template using:
"{% url 'reset_securityquestions' account.uuid user.uuid %}">
but it calls reset_password instead.
If I change the order of urls in urls.py to this:
urlpatterns = patterns('accountsearch.views',
url(r'^$', 'account_search', name='account_search'),
url(r'(?P<account_uuid>[a-zA-Z0-9\-]+)/(?P<user_uuid>[a-zA-Z0-9\-]+)/$', 'reset_securityquestions',
name='reset_securityquestions'),
url(r'(?P<account_uuid>[a-zA-Z0-9\-]+)/(?P<user_uuid>[a-zA-Z0-9\-]+)/$', 'reset_password', name='reset_password'),
and call reset_password using:
{% url 'reset_password' account.uuid user.uuid %}
it calls the reset_security_questions function. Where am I going wrong?
Django always take firsts matching url pattern , so rewrite the urls as :
urlpatterns = patterns('accountsearch.views',
url(r'^$', 'account_search', name='account_search'),
url(r'^reset-password/(?P<account_uuid>[a-zA-Z0-9\-]+)/(?P<user_uuid>[a-zA-Z0-9\-]+)/$','reset_password',name='reset_password'),
url(r'^security-question/(?P<account_uuid>[a-zA-Z0-9\-]+)/(?P<user_uuid>[a-zA-Z0-9\-]+)/$','reset_securityquestions',name='reset_securityquestions'),
I don't understand how you are expecting Django to know which URL to use if they both have exactly the same url pattern. When the request comes in, all Django has to go on is the URL itself, and it will always try and match those in order. With your switched patterns, you'll find that the reset_password view now doesn't work.
You need to give Django some way of distinguishing between them. Usually some literal text, either as a prefix or a suffix, would be used, for example:
r'reset/(?P<account_uuid>[a-zA-Z0-9\-]+)/(?P<user_uuid>[a-zA-Z0-9\-]+)/$'
r'security/(?P<account_uuid>[a-zA-Z0-9\-]+)/(?P<user_uuid>[a-zA-Z0-9\-]+)/$'

django redirect inside of urls.py

I plan on having several different apps inside my django project, each for a new technology I want to play around with. As I work on each on, I want to have the root URL path redirect to the project I'm working on.
Directory Structure:
backyard/
my_project/
views.py
backyard/
urls.py
backyard/backyard/urls.py:
from django.conf.urls import patterns, include, url
from django.shortcuts import redirect
urlpatterns = patterns('',
url(r'^$', redirect('my_app/')),
url(r'^my_project/$', 'my_project.views.homepage'),
)
backyard/my_project/views.py
def homepage(request):
return render_to_response('my_project/index.html', {'data':data})
When I access the page at http:machine-name:8000/ I get an error saying The included urlconf backyard.urls doesn't have any patterns in it I most definitely have URLs in my urls.py, what is the issue?
A URLconf needs to map regexes to views -- the redirect function doesn't return a view; rather, it returns an HttpResponse. You can look at this question for an example of how to define a redirect directly in the URLconf.

Understanding django.shortcuts.redirect

I have a couple problems understanding how redirect or rather reverse really work.
In the main urls.py I have:
from django.conf.urls import patterns, include, url
from django.views.generic.simple import redirect_to
urlpatterns = patterns('',
url(r'^$', redirect_to, {'url': '/monitor/'}),
url(r'^monitor/', include('monitor.urls')),
)
and in monitors.urls I have:
from django.conf.urls import patterns, include, url
urlpatterns = patterns('monitor.views',
(r'^$', 'index'),
(r'^abc/(?P<id>.*$)', 'abc'),
)
When you call /monitor I want to redirect it to /monitor/abc so I did:
def index(request):
return redirect("abc")
def abc(render, id=None):
return render_to_response("monitor/list.htmld", {})
But I got an NoReverseMatch exception. But when I do:
def index(request):
return redirect("abc/")
then it suddenly works.
I cannot fully understand why. Why did reverse fail with abc but not with abc/? And how does reverse know that the redirect should include monitor/ as well? What if I had in the main urls.py another app called xyz which also has a abc view?
Why did reverse fail with 'abc' but not with 'abc/'?
Because it interpreted it as a view name (and you indeed have a view named 'abc', see your monitor.urls file). This means Django will call reverse to compute the URL. The value abc/ is interpreted as an actual URL which means Django won't call reverse to determine the URL.
This also explains why reverse failed: the view with name abc also requires an argument called id. Otherwise Django won't be able to lookup the URL as there is no view called abc without parameters.
Based on the documentation you should be able to reverse the URL using:
redirect("abc", id=...)
where ... is the value of the id parameter.
And how does reverse know that the redirect should include monitor/ as well?
That is because it knows what URLs are available and 1) it knows where the view called abc is defined and 2) it knows that monitors.urls is included with monitor/ in front.
What if I had in the main urls.py another app called "xyz" which also has a "abc" view?
In that case you have to use namespaces.

Categories