Django templates url - differentiate between same url names - django

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.

Related

Is it possible to give external URLs names in django

I have just started naming my URL patterns in Django, so that if I want to change the URL pattern, I just have to change it in one place. e.g:
url(r'^$', HomeListView.as_view(), name='home'),
And referencing it in my templates like this:
{% url home %}
Is this possible with external links in case they change or I change the a Facebook page link for example. How would this look?
Thanks
One way to do this could be to write an external_url template tag, and have an ExternalURL model that stores them.
This would give you the advantage of being able to have the urls editable without redeploying changed code.
The disadvantage is that there will be database lookups to see those urls. Also, you would need to {% load external_urls %} in templates you want to use it in.
# models.py pseudo-code
class ExternalURL(models.Model):
name = models.CharField(unique=True)
url = models.URLField()
Your template tag might look something like:
# app/templatetags/external_url.py
#library.register()
def external_url(name):
try:
return ExternalURL.objects.get(name=name).url
except ExternalURL.DoesNotExist:
return ''
Another alternative could be to have a Context Processor that stores them all in the context, allowing you to not have to pass them explicitly into views: would be useful if you had several external urls that were used in many places within your system.
def external_urls(request):
return {
'google': 'http://www.google.com/',
# more here
}
The advantages of this is no database lookup, no requirement to load the template tag, but you will need to add it to your settings.CONTEXT_PROCESSORS. Also, you could inspect request to see if the current user may see all the urls.
If its external links, and possibility of the change then you should define it in settings or separate static url file and pass those variable with request context.
urls.py should be recommended to use only for your app specific urls.
In this SO thread you can see how to approach defining constant

Django-CMS AppHooks with conflicting urls?

I'm trying to use django-cms app hooks in a different way. I have only an app, with different website pages. For each page, i created an AppHook, since i want to have control of all of them with the cms.
To do that, inside the app, i did a package, with urls.py file for each of the page, example:
/urls
/home_urls.py
/portfolio_urls.py
/contacts_urls.py
Here are the definition of some app hooks:
class WebsiteHome(CMSApp):
name = _("cms-home")
urls = ["website.urls.home_urls"]
apphook_pool.register(WebsiteHome)
class WebsiteServices(CMSApp):
name = _("cms-services")
urls = ["website.urls.services_urls"]
apphook_pool.register(WebsiteServices)
Anyway, the problem is: i don't have any control on the regular expressions. Each one, is entering on the first regular expression that it founds, in this case, the urlpattern in the
website.urls.home_urls
Despite, having different apphHooks.
Example:
if i write a slug contacts (that has an apphook to WebsiteContacts), it still goes to the home_urls.py file, associated with the WebsiteHome (app hook).
Did anyone had a similiar problem?
Basically, what I'm trying to say is that it's something wrong with the regular expression. I can't make:
url(r'^$', [...]),
only:
url(r'^', [...]),
If I put the '$', it doesn't enter on any regex. If I take it, it enters always on the
website.urls.home_urls.py
Despite the slugs having different Apphooks, associated with different urls.py files.
Have you tried r'^/$'? I'm using r'^/?$' in some app-hook urls, but I wonder if r'^$' is failing for you because of a '/'?
As you've defined each of those URL files as individual app hooks in CMS then they'll each get attached to a certain page in the CMS e.g.
www.mysite.com/home
www.mysite.com/contacts
www.mysite.com/services
etc
Because those URL files are attached to pages this should prevent conflict between urlpatterns. For example, I've got an URLs file attached to a CMS app called News which looks like this;
urlpatterns = patterns(
'',
url(r'^(?P<slug>[-_\w]+)/$', NewsDetailView.as_view(), name='news_detail'),
url(r'^$', NewsListView.as_view(), name='news_list'),
)
Which is attached to a page at mysite.com/news so if I go to mysite.com/news/myslug I hit that NewsDetailView and if I go to mysite.com/news I hit NewListView.
Using this example, if you had a slug for a contact you'd go to mysite.com/contacts/contact-slug to hit that NewsDetailView.
And just a sidenote on the urlpatterns in case you're not aware, the ^ in the regex signifies the start of a pattern to match, and the $ signifies the end. URL dispatcher docs

What is reverse()?

When I read Django code sometimes, I see in some templates reverse(). I am not quite sure what this is but it is used together with HttpResponseRedirect. How and when is this reverse() supposed to be used?
reverse() | Django documentation
Let's suppose that in your urls.py you have defined this:
url(r'^foo$', some_view, name='url_name'),
In a template you can then refer to this url as:
<!-- django <= 1.4 -->
link which calls some_view
<!-- django >= 1.5 or with {% load url from future %} in your template -->
link which calls some_view
This will be rendered as:
link which calls some_view
Now say you want to do something similar in your views.py - e.g. you are handling some other URL (not /foo/) in some other view (not some_view) and you want to redirect the user to /foo/ (often the case on successful form submission).
You could just do:
return HttpResponseRedirect('/foo/')
But what if you want to change the URL in the future? You'd have to update your urls.py and all references to it in your code. This violates the DRY (Don't Repeat Yourself) principle and the whole idea of editing in one place only - which is something to strive for.
Instead, you can say:
from django.http import HttpResponseRedirect
from django.urls import reverse
return HttpResponseRedirect(reverse('url_name'))
This looks through all URLs defined in your project for the URL defined with the name url_name and returns the actual URL /foo/.
This means that you refer to the URL only by its name attribute - if you want to change the URL itself or the view it refers to you can do this by editing one place only - urls.py.
The existing answers are quite clear. Just in case you do not know why it is called reverse: It takes an input of a url name and gives the actual url, which is reverse to having a url first and then give it a name.
Existing answers did a great job at explaining the what of this reverse() function in Django.
However, I'd hoped that my answer shed a different light at the why: why use reverse() in place of other more straightforward, arguably more pythonic approaches in template-view binding, and what are some legitimate reasons for the popularity of this "redirect via reverse() pattern" in Django routing logic.
One key benefit is the reverse construction of a url, as others have mentioned. Just like how you would use {% url "profile" profile.id %} to generate the url from your app's url configuration file: e.g. path('<int:profile.id>/profile', views.profile, name="profile").
But as the OP have noted, the use of reverse() is also commonly combined with the use of HttpResponseRedirect. But why?
I am not quite sure what this is but it is used together with HttpResponseRedirect. How and when is this reverse() supposed to be used?
Consider the following views.py:
from django.http import HttpResponseRedirect
from django.urls import reverse
def vote(request, question_id):
question = get_object_or_404(Question, pk=question_id)
try:
selected = question.choice_set.get(pk=request.POST['choice'])
except KeyError:
# handle exception
pass
else:
selected.votes += 1
selected.save()
return HttpResponseRedirect(reverse('polls:polls-results',
args=(question.id)
))
And our minimal urls.py:
from django.urls import path
from . import views
app_name = 'polls'
urlpatterns = [
path('<int:question_id>/results/', views.results, name='polls-results'),
path('<int:question_id>/vote/', views.vote, name='polls-vote')
]
In the vote() function, the code in our else block uses reverse along with HttpResponseRedirect in the following pattern:
HttpResponseRedirect(reverse('polls:polls-results',
args=(question.id)
This first and foremost, means we don't have to hardcode the URL (consistent with the DRY principle) but more crucially, reverse() provides an elegant way to construct URL strings by handling values unpacked from the arguments (args=(question.id) is handled by URLConfig). Supposed question has an attribute id which contains the value 5, the URL constructed from the reverse() would then be:
'/polls/5/results/'
In normal template-view binding code, we use HttpResponse() or render() as they typically involve less abstraction: one view function returning one template:
def index(request):
return render(request, 'polls/index.html')
But in many legitimate cases of redirection, we typically care about constructing the URL from a list of parameters. These include cases such as:
HTML form submission through POST request
User login post-validation
Reset password through JSON web tokens
Most of these involve some form of redirection, and a URL constructed through a set of parameters. Hope this adds to the already helpful thread of answers!
This is an old question, but here is something that might help someone.
From the official docs:
Django provides tools for performing URL reversing that match the
different layers where URLs are needed: In templates: Using the url
template tag. In Python code: Using the reverse() function. In higher
level code related to handling of URLs of Django model instances: The
get_absolute_url() method.
Eg. in templates (url tag)
2012 Archive
Eg. in python code (using the reverse function)
return HttpResponseRedirect(reverse('news-year-archive', args=(year,)))
The function supports the dry principle - ensuring that you don't hard code urls throughout your app. A url should be defined in one place, and only one place - your url conf. After that you're really just referencing that info.
Use reverse() to give you the url of a page, given either the path to the view, or the page_name parameter from your url conf. You would use it in cases where it doesn't make sense to do it in the template with {% url 'my-page' %}.
There are lots of possible places you might use this functionality. One place I've found I use it is when redirecting users in a view (often after the successful processing of a form)-
return HttpResponseRedirect(reverse('thanks-we-got-your-form-page'))
You might also use it when writing template tags.
Another time I used reverse() was with model inheritance. I had a ListView on a parent model, but wanted to get from any one of those parent objects to the DetailView of it's associated child object. I attached a get__child_url() function to the parent which identified the existence of a child and returned the url of it's DetailView using reverse().
There is a doc for that
https://docs.djangoproject.com/en/dev/topics/http/urls/#reverse-resolution-of-urls
it can be used to generate an URL for a given view
main advantage is that you do not hard code routes in your code.
The reverse() is used to adhere the django DRY principle i.e if you change the url in future then you can reference that url using reverse(urlname).

How to use namespace urls with django in a reusuable app

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 %}

Can I reference a named URL in an included URLConf using Django's "url" tag?

In my Django project, I used to have a single URLConf, urls.py at the root of the project. This URLConf included some named URLs using Django's url() function. In several templates, I reference these URLs with the url tag, à la {% url named_url %}. This worked fine.
The root urls.py became a bit unwieldy, so I split it off into a URLConf for each app, in app/urls.py. Some URLs still have names. Unfortunately, I get a TemplateSyntaxException when using the url tag in templates now. Specifically, the error message is:
Caught an exception while rendering: Reverse for 'myproj.myapp.new_test' with arguments '()' and keyword arguments '{}' not found.
Is there a way to reference the named URLs in the app-specific URLConfs using the url tag in Django?
You definitely can reference urls in included urlconfs via the url tag - that's in fact what you're supposed to do. However, I've always found the url tag and the reverse() function to be very flaky and error-prone, so these errors do sometimes occur.
My suggestion would be to give all your urls a name, no matter which urlconf they are in. Then you just need to refer to the actual name - you don't need to qualify it with the name of the app or urlconf or anything. See if that works.
Are you referencing each app's urls.py?
from django.conf.urls.defaults import *
urlpatterns = patterns('',
(r'^app1/', include('app1.urls')),
(r'^app2/', include('app2.urls')),
)
from Django Docs
Probably not useful to you but hopefully useful to the next bloke searching for an answer:
I am using Django 1.4 and what solved it for me was not using quotes in the template tag:
{% url 'name_of_view' some_var %}
became this:
{% url name_of_view some_var %}
And that cured it. I didn't see any mention of this in the django docs and the examples in the docs use quotes. So this seems a little buggy but hopefully will keep someone from pulling out their hair.
https://docs.djangoproject.com/en/dev/topics/http/urls/#naming-url-patterns
I can recommend you two things:
Use name in url patterns
Do not do references to the project name inside a app(like you did with "myproj.myapp.new_test". If this was the "right way to do", you should only reference as "myapp.new_test"