How to use namespace urls with django in a reusuable app - django

I have a django app, a forum app, that has templates with it. In those templates, there are urls that point to parts of the app. For instance the thread_list template has links to each thread like so:
{% for thread in threads %}
{{thread.title}}
{% endfor %}
The thing is, I don't really like calling my urls "forum_thread". I prefer just "thread" and using the namespace feature of django. "forum_thread" may be used somewhere else in the project (namespace collision).So it will look like this:
{% for thread in threads %}
{{thread.title}}
{% endfor %}
but this doesn't feel like the correct way to do this. The docs are kind of unclear here.
I want this app to be reusable and easy to configure. But I also want to use the best standards. I don't want to have the to make the user specify their own namespace name, and then have them edit every single url in each template.
How should I do urls in this app?

From what I can gather you should be able use {% url forum:thread thread %} as you've described. Namespaces always seem to be defined with two variables, namespace and app_name.
If you then do the following in urls.py:
url(r'^/forum/', include('forum.urls', namespace='forum', app_name='forum')),
url(r'^/foo/', include('forum.urls', namespace='foo', app_name='forum')),
url(r'^/bar/', include('forum.urls', namespace='bar', app_name='forum')),
In my understanding, this defines 3 instances of the app 'forum', 'foo', 'bar', and the default (which has namespace==app_name).
When you reverse forum:thread, it uses the current context to determine which one to use- if you are in namespace 'foo' it will use that, otherwise it will fall back on the default.
If anyone is able to clarify how Django decides what the 'current' namespace/app is that would be very helpful. I currently categorise it as 'black magic'.
Some clarification on the actual difference between namespace and app_name would also be helpful- it's possible that I have this totally reversed. The current docs are highly ambiguous.
Note: I have this working for initial requests, but I'm currently unable to make this work for AJAX requests- those always use the default instance for some reason.

This might be a simple syntax error. I was following the Django Tutorial, and I changed mysite/urls.py improperly. The original syntax:
url(r'^polls/', include('polls.urls')),
The desired change:
url(r'^polls/', include('polls.urls', namespace="polls")),
What I did:
url(r'^polls/', include('polls.urls'), namespace="polls"),
Correcting the syntax resolved the issue.

based on my understanding of this question:
in Django 2.1.7
You can app name in app's urls.py file
# app's urls.py
from django.urls import path
from . import views
app_name = 'forum'
urlpatterns = [
path('thread/', views.mark_done, name='thread')
]
in main urls.py
# urls.py
....
urlpatterns = [
path('forum/', include('forum.urls')),
]
then you can employ {% url 'forum:thread' %} in your template
If you wanna use it in a for loop
I think we should
create a view return all threads as context
then add a path to that view
...
path('thread/<int:pk>', views.mark_done, name='thread')
the url in template will like:
{% for thread in threads %}
{{thread.title}}
{% endfor %}

Related

Django templates url - differentiate between same url names

in my application I've got a view called download_document which works fine using {% url 'download_document' some_id %} .
Installing a 3rd party app into the virtual environment cause trouble because this app has also the download_document view (also accepting one ID parameter) with the same urlname.
If I'd like to use both with this name, can I somehow make the difference between them? For instance using both cases in the same template file?
Python: 2.7
Django: 1.11.16
Debian .
Update #1:
my app is called infk . I tried already using
{% url 'infk:download_document' document.id %}
, but in this case I got a 500 with: u'infk' is not a registered namespace.
I also tried adding namespace to the top urls.py file:
#original
url(r'^infk/', include('infk.urls')),
#modified
url(r'^infk/', include('infk.urls', namespace="infk")),
but in this case i got back a different 500: Reverse for 'infk' not found. 'infk' is not a valid view function or pattern name.
To work the second one I'd need to change at least all reverse methods from ** reverse('infk'...) to reverse('infk:infk'...) which I don't really want.
So if I don't add namespace, is there any way to call my download_document function? (By default it calls the 3rd party app's download_document view .
Long post ahead, so deep breath...
So if I don't add namespace, is there any way to call my download_document function? (By default it calls the 3rd party app's download_document view .
That depends on what you're trying to do. If you always want your download_document function to be called throughout your entire website (even on the pages provided by the 3rd-party-app), you can just move infk to the end of your urlpatterns in main_app/urls. That would look something like this:
main_app/urls.py
================================================================================
from django.conf.urls import include, url
urlpatterns = [
url(r'^3rd-party-app/', include('3rd-party-app.urls')),
url(r'^infk/', include('infk.urls')),
]
Otherwise, if you're trying to get your download_document in specific instances without namespaces, it's not particularly easily. You could create a custom tag that always produces the url for your download_document function, but that's a lot of extra work, and if you run into another name conflict, you'd have to do it again and again, and that's barely a step above hard-coding the url in the first place. Plus, if you're planning to deploy infk as a third party app, you'll probably want to use a namespace to help other people avoid the same problem you're having.
Explanation
According to the docs at https://docs.djangoproject.com/en/1.11/topics/http/urls/#naming-url-patterns:
If you call your URL pattern comment and another application does the same thing, the URL that reverse() finds depends on whichever pattern is last in your project’s urlpatterns list.
So if you change the order of your included urls in your main_app/urls.py file such that infk is after the 3rd-party app, you should get the infk download_document. However, this means that you'll ALWAYS get the infk download_document, which may break some of the functionality in your 3rd-party app.
Examples
Django 1.x
Source: https://docs.djangoproject.com/en/1.11/topics/http/urls/#reversing-namespaced-urls
Your modified url(r'^infk/', include('infk.urls', namespace="infk")) has created an instance namespace, which might make sense if you are using multiple instances of infk throughout your app. If you aren't (or you're planning on deploying infk as a third-party app itself) you probably don't need that. Either way, I'd recommend you set the application namespace.
main_app/urls.py
================================================================================
from django.conf.urls import include, url
urlpatterns = [
# These are both instance namespaces. Note that although the same urls
# are being included from infk.urls for both, the final route will be
# different.
url(r'^infk-instance1/', include('infk.urls', namespace='infk-instance1')),
url(r'^infk-instance2/', include('infk.urls', namespace='infk-instance2')),
# The 3rd-party app your url and reverse functions are colliding with.
url(r'^3rd-party-app/', include('3rd-party-app.urls')),
]
infk/urls.py
================================================================================
from django.conf.urls import url
from . import views
# This is the application namespace. I recommend setting this regardless of
# whether you use instance namespaces.
app_name = 'infk'
urlpatterns = [
url(r'^(?P<pk>\d+)/$', views.download_document(), name='download_document'),
]
Instance Namespaces
Instance namespaces would be called like this:
infk/foo_template.html
================================================================================
...
<!-- In this case, {% url 'infk-instance1:download_document' document.id %}
will return /infk-instance1/<document.id>-->
Download Document
...
ingk/foo_template.html
================================================================================
...
<!-- In this case, {% url 'infk-instance2:download_document' document.id %}
will return /infk-instance2/<document.id>-->
Download Document
...
You should still be able to get the 3rd-party download_document url, if you need it. Note that in order for this to work, 3rd-party-app.urls must be included AFTER infk.urls.
ingk/foo_template.html
================================================================================
...
<!-- In this case, {% url 'download_document' document.id %}
will return /3rd-party-app/<document.id>-->
Download Document
...
Application Namespaces
The behavior of application namespaces depends on whether there are instance namespaces defined for the application or not, and whether the view called defines a current_app. If you use the above example where both application and instance namespaces are defined, then using an application namespace in your {% url %} tag may return inconsistent results. (See https://docs.djangoproject.com/en/1.11/topics/http/urls/#reversing-namespaced-urls for more information)
For ease of explanation, I'm going to assume you are not using multiple instances of the infk app. Your main_app/urls.py might then look like:
main_app/urls.py
================================================================================
from django.conf.urls import include, url
urlpatterns = [
# No instance namespaces
url(r'^infk/', include('infk.urls')),
url(r'^3rd-party-app/', include('3rd-party-app.urls')),
]
Application namespaces are called like this:
infk/foo_template.html
================================================================================
...
<!-- In this case, {% url 'infk:download_document' document.id %}
will return /infk/<document.id>-->
Download Document
...
And, assuming that 3rd-party app is installed AFTER infk, your 3rd-party app's download_document can still be called like:
ingk/foo_template.html
================================================================================
...
<!-- In this case, {% url 'download_document' document.id %}
will return /3rd-party-app/<document.id>-->
Download Document
...
Django >=2.x
Source: https://docs.djangoproject.com/en/dev/topics/http/urls/#term-application-namespace
The resolver works essentially the same as described above; however, instead of using url() in your urls.py files, use path() or re_path().
There is a way to avoid url name conflicts. All you have to do is to add their respective namespaces before the url name. As per the docs, all you need is the following:
{% url 'myapp:view-name' %}
By using the namespaced URL resolution strategy.

Accessing a named view from a template in django

Suppose I have two Apps AppA and AppB
|_AppB
| |_urls.py # url(r'^create/', views.user_profile, name="name_user_profile"),
|
|_AppA
|_urls.py
|_templates
|_file.html
Now in file.html I have something like this
{% block content %}
Success !!!
goto <a href={% url `name_user_profile` % }> Manage Profile </a>
{% endblock %}
However name_user_profile cant be recognised ? Is there anything i have to do to get it recognised ?
Update:
This is what I have done so far
In my main app urls.py (the one with settings.py). I added the following
url(r'^profile/', include("UserProfile.urls" , namespace="UserProfile" , app_name="UserProfile")),
Now in my UserProfile app where the view is located I have this
urlpatterns = [
url(r'^$', views.user_profile, name="name_user_profile"),
]
and the template which which needs to call that view is located in the 3rd app
called accounts which has this
goto <a href={% url 'UserProfile:name_user_profile' % }> Manage Profile </a>
But that does not seem to work
Since your apps reference different urls.py files, in order to use named URLs you will need to access each one with its own namespace.
Check out the example in the docs here. There is a urls.py file that uses include('polls.urls'). Then, in the actual Polls app, there are a couple of named URLs, index and detail. Since they're inside an app those urls can be accessed by using their namespace - {% url 'polls:index' %} and {% urls 'polls:detail' %} respectively.
If you are working before Django 1.9, you have to specifically supply the namespace argument after the include statement. Starting in 1.9 Django automatically uses the app name for the namespace when you include a urls.py file from an app.

In Django, how do I get the name of the pattern that was followed for a particular URL within a template?

I have been doing lots of Googling on this topic and have gone through plenty of Django docs, but I can't seem to find a decent answer to what seems like ought to be a very simple question with a simple solution. So hopefully a Django vet out there can help.
Lets say I have the following urlconf:
urlpatterns = patterns('',
....
url(r'^directory/users/$', UserView.as_view(), name='users'),
url(r'^directory/users/(?P<user_id>[0-9]+)/$',UserView.as_view(), name='users'),
....
)
What I want to be able to do is test from within a template which pattern was followed, something like this:
{% if name_of_last_followed_url_pattern == 'users' %}
....
{% endif %}
Now one would think that Django would stash this away somewhere and be able to spit it back out to me. But I cannot find anything that corresponds to "name_of_last_followed_url_pattern" anywhere in the docs or in my searches. Any ideas how I might access this (or if not provided, why not)?
You need the context processors that adds the request to the template django.core.context_processors.request added to the TEMPLATE_CONTEXT_PROCESSORS. Then in the template you can do
{% if request.resolver_match.url_name == 'users' %}
....
{% endif %}
resolver_match has other attributes like namespaces, app_name. You can see here:
https://docs.djangoproject.com/en/1.6/ref/urlresolvers/#django.core.urlresolvers.ResolverMatch

Django - Why url namespaces?

I've been learning Django for sometime now. I came across url reverse, understood most of it but couldn't understand namespaces:
1. How is it useful ?
2. How to use it ?
It is poorly documented and I've not found any decent article on it. :(
Also could someone explain how to reverse included urls??
i found a good solution here
Anyone knows good Django URL namespaces tutorial?
Disclaimer : This is written by David Eyk on above mentioned link
Agreed, the docs for this are rather confusing. Here's my reading of it (NB: all code is untested!):
In apps.help.urls:
urlpatterns = patterns(
'',
url(r'^$', 'apps.help.views.index', name='index'),
)
In your main urls.py:
urlpatterns = patterns(
'',
url(r'^help/', include('apps.help.urls', namespace='help', app_name='help')),
url(r'^ineedhelp/', include('apps.help.urls', namespace='otherhelp', app_name='help')),
)
In your template:
{% url help:index %}
should produce the url /help/.
{% url otherhelp:index %}
should produce the url /ineedhelp/.
{% with current_app as 'otherhelp' %}
{% url help:index %}
{% endwith %}
should likewise produce the url /ineedhelp/.
Similarly, reverse('help:index') should produce /help/.
reverse('otherhelp:index') should produce /ineedhelp/.
reverse('help:index', current_app='otherhelp') should likewise produce /ineedhelp/.
Like I said, this is based on my reading of the docs and my existing familiarity with how things tend to work in Django-land. I haven't taken the time to test this.

{% url %} gives me NoReverseMatch error while reverse() returns the url just fine. Why?

I don't know if this SO question is of the same problem that I am about to describe, but it does share the same symptoms. Unfortunately, it still remains unresolved as I am writing.
So here is my problem. I am trying to add James Bennett's django-registration app to my django project. I have pretty much finished configuring it to my needs - custom templates and urls. Just when I thought everything was good to go. I got NoReverseMatch error from using {% url 'testing' item_id=123 %} (I also tried using the view name, myapp.views.test, instead but no luck) in one of the custom templates required by django-registration. Interestingly, I tried reverse('testing', kwargs={'item_id':123}) in the shell and the url was returned just fine. I thought {% url %} uses reverse() in the back-end but why did I get different outcomes?
urls.py: (the URLconf of my site)
urlpatterns = patterns('myapp.views',
url(r'^test/(?P<item_id>\d+)/$', 'test', name='testing'),
)
activation_email.txt: (the said template. Note it's intentionally in .txt extension as required by django-registration and that shouldn't be the cause of the problem.)
{% comment %}Used to generate the body of the activation email.{% endcomment %}
Welcome to {{ site }}! Please activate your account by clicking on the following link:
{% url 'testing' item_id=123 %}
Note the activation link/code will be expired in {{ expiration_days }} days.
I don't know if it matters but just thought I should mention activation_email.txt is stored in the templates directory of myapp though it is used by django-registration.
Also, I am using django 1.4
I have a feeling that the problem has something to do with the url namespaces, a topic that I have never understood, but it's just a naive guess. (IMO, the django documentation is great in explaining everything about django, except when it comes to url namespaces)
I'm no expert here, but in a Django project I'm working on at the moment I use the name of the url without quotes. I just added quotes around a similar line in one of my templates and it produced the same error as your error.
Try:
{% url testing item_id=123 %}