Django URL namespaces - the template has to know its namespace? - django

I've been tinkering with (my first) Django project that started out with Django 1.6 and has just recently moved to Django 1.8. I've also been working through Django Patterns & Best Practices, learning how I should have structured it all along :-)
My project has several sub-apps and a typical main urls.py with lines like:
(r'', include('noc.apps.frontpage.urls')),
Within each app, I've prefixed all the URL names with the app name, e.g. frontpage_edit_page and used {% url %} throughout the templates to refer between views.
Then I read about URL namespaces, and thought I could de-uglify my URL names. As I first interpreted it, if I added a namespace to each include() in the main urls.py, everything would function as before, because the URL names referred to would all be resolved by the 'local' app. But it doesn't seem to work that way.
With this in the main urls.py:
(r'', include('noc.apps.frontpage.urls', namespace='frontpage', app_name='frontpage')),
This in the app urls.py:
url(r'^frontpage/edit/(?P<slug>[A-Za-z0-9]+)$', views.edit_page, name='front_page_edit_page'),
and {% url 'front_page_edit_page' slug=page.slug %} in a template inside that app, I get a NoReverseMatch exception with 0 URLs tried.
All the examples I can find are talking about prefixing URLs with the namespace - frontpage:front_page_edit_page but 1) how is this an improvement on the previous app prefix on the URL name? and 2) how could you ever have two instances of the same app, which is supposed to be a benefit... So I'm assuming that this is for linking between apps, not within apps.
So what is it that I am missing? Do I need to embed the app_name or namespace in my view function too? It's true that if I do prefix all my URL names with the namespace, even within the app, I get a rendered page, but it seems to defeat the point.

The way you're meant to do it is indeed to add the namespace to the URL tag;
{% url 'frontpage:edit_page' slug='SLUG' %}
But it would be better practice to structure your main project URls file like this;
urlpatterns = patterns(
'',
url(r'^admin/', include(admin.site.urls)), # NOQA
url(r'frontpage', include('noc.apps.frontpage.urls', namespace='frontpage', app_name='frontpage')),
That way you can specify the path for each app in the main URLs file, and avoid repetition;
urlpatterns = patterns(
'noc.apps.frontpage.views',
url(r'^edit/(?P<slug>[A-Za-z0-9]+)$', 'edit_page', name='edit_page'),
With this you can introduce a RESTful URL structure to all your apps so you'll end up with things like;
urlpatterns = patterns(
'',
url(r'^admin/', include(admin.site.urls)), # NOQA
url(r'frontpage/', include('noc.apps.frontpage.urls', namespace='frontpage', app_name='frontpage')),
url(r'contact/', include('noc.apps.contact.urls', namespace='contact', app_name='contact')),
url(r'myapp/', include('noc.apps.myapp.urls', namespace='myapp', app_name='myapp')),
All your apps can follow a similar structure then;
urlpatterns = patterns(
'noc.apps.contact.views',
url(r'^$', 'index', name='index'),
url(r'^add/$', 'add', name='add'),
url(r'^edit/(?P<slug>[A-Za-z0-9]+)$', 'edit', name='edit'),
urlpatterns = patterns(
'noc.apps.myapp.views',
url(r'^$', 'index', name='index'),
url(r'^add/$', 'add', name='add'),
url(r'^edit/(?P<slug>[A-Za-z0-9]+)$', 'edit', name='edit'),
Multiple instances of frontpage could be achieved using the top level namespace;
urlpatterns = patterns(
'',
url(r'^admin/', include(admin.site.urls)), # NOQA
url(r'frontpage/', include('noc.apps.frontpage.urls', namespace='frontpage1', app_name='frontpage')),
url(r'frontpage/', include('noc.apps.frontpage.urls', namespace='frontpage2', app_name='frontpage')),
That way you should be able to target the top level instance namespace, followed by the app namespace like this;
{% url 'frontpage1:frontpage:edit_page' slug='SLUG' %}
{% url 'frontpage2:frontpage:edit_page' slug='SLUG' %}
But if you would like to keep your template links more generic I believe you can leave out the top level namespace and django will resolve to the current app which you have to add to the request object. This is detailed at the end of Reversing Namespaced URLs

For anyone to reference.
This worked for me, Django 2.2.2
# project urls.py
# DjpollsConfig is my app config
from djpolls.apps import DjpollsConfig
djpolls_app_name = DjpollsConfig.name
urlpatterns = [
path(djpolls_app_name, include('djpolls.urls', namespace='djpolls_app_name'))
]
# app urls.py
from django_proj.urls import djpolls_app_name
app_name = djpolls_app_name
# app template
{% url 'djpolls_app_name:detail' question.id %}
Hope it helps!

Try this
(r'', include('noc.apps.frontpage.urls', namespace = 'abc')),
and then in your templates:
{% url 'abc:frontpage:front_page_edit_page' slug=page.slug %}

Related

Multiple auth login pages in Django

I have multiple apps in one django project
/user
/manager
/business`
Each needs a separate set of login and registration. How do I use django.contrib.auth to satisfy this?
I have urlpatterns in main are:
urlpatterns = [
path('admin/', admin.site.urls),
path('user/', include('user.urls')),
path('user/', include('django.contrib.auth.urls')),
path('manager/', include('manager.urls')),
path('manager/', include('django.contrib.auth.urls')),
path('business/', include('business.urls')),
path('business/', include('django.contrib.auth.urls')),
Urlpatterns in the apps are like those:
urlpatterns = [
path('index', views.index, name='index'),
path('register', views.register, name='register'),
]
and I have different views for login and register, also have different templates in each app: /templates/register/register.html and /templates/register/login.html
However, login and register views seem to be shared between apps. Is there a way to separate them with ease?
First of all I would suggest you make use namespaces in your urls so you don't get any conflicts with the names in your urls. So add app_name to the urls.py of your apps, e.g.
#user urls.py
app_name='user'
urlpatterns = [
...
Then something like {% url 'user:register' %} will point to register view of your user app, {% url 'manager:register' %} will point to the register view of your manager app and so on.
If you have defined individual login views for each app the same goes for these views too, providing you have imported the correct views in you apps urls.py.
As you are also including the django.contrib.auth.urls in each of your apps there is of course a second 'login/' path coming from the default path in the auth.urls. However, as far as I know the resolution of urls is done top down by django, so if you keep the order of urls as in your post your custom 'login/' will be hit first and used. So no problem with that.
Where I see a problem is with your templates. If I understand it correctly all are located in a templates/register/ folder in your apps. The django template loader does not differentiate between a register.html template in your user app and a register.html template in your manager app. So it is likely that you do not get the correct template. What I would suggest is moving the templates to an app specific subfolder, e.g. /templates/user/register. Then you can fetch the correct templates in your views (e.g. 'user/register.html').
I understand that you have individual app specific views. Of course you can always subclass the views django provides and adapt them to your needs. For instance by overriding the form_valid() method of the LoginView. This SO post provides an example.
If on the other hand you want to use the default django login view just with a custom template you can pass the template as a kwarg to your view. E.g.:
# user urls.py
from django.urls import path
from django.contrib.auth.views import LoginView
app_name='user'
urlpatterns = [
path('login/', LoginView.as_view(template_name='user/login.html'), name='login'),
# more patterns
]
If you want to redirect to some custom site after successful login you can add an input called 'next' to your login form containing the url to where to redirect, e.g.
<!-- user/login.html -->
<form action="{% url 'user:login' %}" method="POST">
{% csrf_token %}
{{ form }}
<input type="hidden" name="next" value="/user/index/">
<button type="submit">Login</button>
</form>
In your apps you will propably be using the user_passes_test decorator or something similar to check whether the user is allowed to access the view in that app.

How to convert Template tags from normal django to the one django-hosts use

I have just added django-hosts to setup subdomains for my site which works perfectly. Next step is just to convert all the normal django URL's in my template to the one django-hosts like.
I know how to link pages , but once I need to add variables to my URL's i'm not sure how to construct the code for it.
Normal django URL that works
{% url 'golemstats:nodeinfo' node.Node_id node.Node|slugify %}
How do I convert that to a URL that django-hosts like? I've tried the following:
{% host_url 'nodeinfo' host 'golemstats' 'node.Node_id' 'node.Node|slugify' %}
hosts.py
from django.conf import settings
from django_hosts import patterns, host
host_patterns = patterns('',
host(r'www', settings.ROOT_URLCONF, name='www'),
host(r'golem', 'golemstats.urls', name='golemstats'),
)
golemstats.urls
from django.urls import path
from . import views
app_name = 'golemstats'
urlpatterns = [
path('', views.index, name='index'),
path('node', views.searchNode, name='searchNode'),
path('node/<nodeid>/<node>', views.nodeinfo, name="nodeinfo"),
path('version-notifier', views.notifierIndex, name="notifier"),
path('ports', views.portScanner, name="portscanner"),
path('scoreboard', views.scoreboard, name="scoreboard"),
path('tools', views.tools, name="tools"),
path('troubleshooting', views.troubleshooting, name="troubleshooting"),
path('network', views.networkOverview, name="networkOverview"),
]
Fixed. have to put arguments after host_url
{% host_url 'nodeinfo' node.Node_id node.Node|slugify host 'golemstats' %}

Building URL from name

I've got a url of 'accounts:produce_setup' (i.e. namespace/app is 'accounts' and name of url is 'product_setup'). I'd like to build the full url associated with this view and pass it as context into a template.
How would I go about doing this? Would I use build_absolute_uri()?
Thanks!
you should include your app in project_name/urls.py and bind your application to special URL pattern like this:
from account.urls import urlpatterns as account_urlpatterns
urlpatterns = [
url(r'^account/', include(account_urlpatterns, namespace='account')),
url(r'^admin/', admin.site.urls),
]
and after this in your account/urls.py you can implement your urlpatterns and set your special name for each url like this:
from django.conf.urls import url
from .views import produce_setup_view
urlpatterns = [
url(r'^produce_setup/$', produce_setup_view, name='produce_setup')),
]
at the end now you can use them in your template and views or any python file in your django project like this:
.py in django project:
from django.urls import reverse
url_string = reverse('account:produce_setup')
print(url_string)
>>> '/account/produce_setup/'
in template:
Good Luck :)
Actually, you don't need to. You can simply use {% url "accounts:product_setup" %} in template. For more details check here. And if you want to build the url is views(maybe for other reasons) you can use reverse.

django 1.10 one app page with a link redirect to another app page

I'm new to django server and trying to build a simple website for user registration. Here is the problem, I create my own app with index.html as my homepage. I also used another user registration app from:
https://github.com/pennersr/django-allauth/tree/master/allauth
I was trying to add the app to my homepage with a 'sign up' link. Basically, the account part, and ideally, the link can direct to: http://127.0.0.1:8000/accounts/login/
However, when I run the server, it gives me error:
Reverse for 'base' with arguments '()' and keyword arguments '{}' not found. 0 pattern(s) tried: []
server result:
Both apps work fine individually, but when I try to add the link to my homepage, the error occurs.
The related code in index.html file in my first app:
<li>Log In</li>
The full path for index.html in my project is:
project/app1/templates/app1/index.html
The full path for base.html in my project is:
project/allauth/templates/base.html
I know I probably need to add a url line in my first app's urls.py file, and a view to show it, but how can I do it? Can anyone help me with this, much appreciate.
<li>Log In</li>
this line uses URL reversing, 'allauth:base' is the URL patterns, allauth prefix is the namespace, base is the named URL. You must define the namespace and named URL in the urls.py first.
Define your namespace in project's urls.py file like this:
from django.conf.urls import include, url
urlpatterns = [
url(r'^author-polls/', include('polls.urls', namespace='author-polls')),
url(r'^publisher-polls/', include('polls.urls', namespace='publisher-polls')),
]
Define your named URL in app's urls.py file like this:
from django.conf.urls import url
from . import views
app_name = 'polls'
urlpatterns = [
url(r'^$', views.IndexView.as_view(), name='index'),
url(r'^(?P<pk>\d+)/$', views.DetailView.as_view(), name='detail'),
...
]
all the help you need is in this document: Naming URL patterns

Django flaky url (randomly omits WSGIScriptAlias)

My Django site is producing URLs that intermittently omit my WSGIScriptAlias. If I simply print out {% url 'index' %} in my index.html (see my urls.py settings below) I randomly (around 50% of the time) get either:
MySiteAlias/MySite
which is correct, or
MySite/
which is incorrect.
myapp/urls.py:
from django.conf.urls import url,include
urlpatterns = [
url(r'^MySite/', include('mysite.urls')),
]
mysite/urls.py:
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.index, name='index'),
]
and views.index basically does return render(request, 'mysite/index.html). Any ideas on how to fix this?
I'd imagine you might have two urls with the same name, this is where namespaces will help. If you provide a namespace for your mysite.urls then there is no confusion on where you should go to
url(r'^MySite/', include('mysite.urls', namespace='mysite')),
{% url 'mysite:index' %}