What is the purpose of app_name in urls.py in Django? - django

When include()ing urlconf from the Django app to the project's urls.py, some kind of app's name (or namespace) should be specified as:
app_namespace in include((pattern_list, app_namespace), namespace=None) in main urls.py
or
app_name variable in app's urls.py.
Since, I guess, Django 2, the second method is the preferred one Although I copy-pasted first function signature from Django 3 documentation. But that's not the main point.
My current understanding of namespace parameter of include() is that it's what I use when using reverse().
What is the purpose of app_name in app's urls.py or app_namespace in main urls.py?
Are these exactly the same thing?
How is it used by Django?
Existing questions (and answers) I've found here explain HOW I should specify it rather than WHY.

In this answer, I am taking the DRF package and its URL patterns. If you want to try any of the snippets mentioned in this answer, you must install (pip install djangorestframework) and add rest_framework to INSTALLED_APPS list.
The application namespace can be set in two ways, [Ref: Django doc]
in urls.py using app_name varibale.
You can see that DRF has set the app_name in urls.py. Django will use this app_name as the application namespace only if we are included the patterns with module reference.
That is, include(module, namespace=None)
Example:
urlpatterns = [
path('drf-auth/bare/', include('rest_framework.urls')),
]
in include((pattern_list, app_namespace), namespace=None) function using app_namespace parameter.
In this method, you can set an additional app_namespace for the application, if you want.
Most importantly, we are passing a pattern_list instead of module
Example:
from rest_framework.urls import urlpatterns as drf_urlpatterns
urlpatterns = [
path('drf-auth/foo/', include((drf_urlpatterns, 'foo-app-namespace'))),
]
Complete Example
from django.urls import path, include, reverse
from rest_framework.urls import urlpatterns as drf_urlpatterns
urlpatterns = [
path('drf-auth/bare/', include('rest_framework.urls')),
path('drf-auth/foo/', include((drf_urlpatterns, 'foo-app-namespace'))),
path('drf-auth/bar/', include((drf_urlpatterns, 'bar-app-namespace'))),
]
print(reverse('rest_framework:login'))
print(reverse('foo-app-namespace:login'))
print(reverse('bar-app-namespace:login'))
#results
/drf-auth/bare/login/
/drf-auth/foo/login/
/drf-auth/bar/login/
What is the purpose of app_name in app's urls.py or app_namespace in main urls.py?
Both are used to set the application namespace. The app_name can be used as a default application namespace, if defined in the urls.py.
Are these exactly the same thing?
No.
How is it used by Django?
The application namespace and instance namespace are used to retrieve the URL path. In Django, whenever the reverse(...) function get executed, Django looking for an application namespace first, than any other. You can read more about how Django resolve the URL here, Reversing namespaced URLs

app_name in app/urls.py and app_namespace in include((pattern_list, app_namespace), namespace=None) in main urls.py are the same referenced as application namespace which describes the name of the application that is being deployed.
One can either pass the whole app/urls.py or a string reference of app/urls.py with app_name to include()
# blog.urls
from django.urls import path
from . import views
app_name = 'blog'
urlpatterns = [
path('', views.index(), name='index'),
path('<int:pk>/', views.post(), name='detail'),
]
# project urls
from django.urls import include, path
urlpatterns = [
path('', include('blog.urls')),
]
OR
a tuple of url patterns and app_namespace to include()
# project urls
from django.urls import include, path
from blog.urls import urlpatterns as blogpatterns
urlpatterns = [
path('', include((blogpatterns, 'blog'))),
]
app_namespace will be the default application namespace when provided in include(). If app_namespace is not provided, then it will look for app_name in blog/urls.py and that will be the default namespace.
Without the app namespace, urls are added to global namespace which may lead to url conficts.
URL namespaces and included URLconfs | Term application namespace | include()

For years, we've (at Django) been skirting around the (confusing) distinction between an application name(space) and an instance namespace. We've always just recommended using instance namespace, as per the examples in the docs.
What's happened with Django 2.0 (and onwards) is they've made it so you can't use an instance namespace without also (first) using an application name. Instead of fixing code, we should update our examples to the correct usage.
The include needs to go like this:
urlpatterns += [ url('API/', include((router.urls, 'pikachu')) ]
The tendency is to include the second namespace='pikachu' instance namespace parameter as well, but that's not needed — it defaults to None and is set to 'pikachu' in this case automatically.
Generally, users want to be including an app-level URLs module explicitly setting the app_name attribute there, rather than including the router by hand.

From https://docs.djangoproject.com/en/3.0/topics/http/urls/#introduction :
URL namespaces allow you to uniquely reverse named URL patterns even
if different applications use the same URL names. It’s a good practice
for third-party apps to always use namespaced URLs (as we did in the
tutorial). Similarly, it also allows you to reverse URLs if multiple
instances of an application are deployed. In other words, since
multiple instances of a single application will share named URLs,
namespaces provide a way to tell these named URLs apart.

Related

How do I reference a path (URL) in Django? But this path is not in the base app, but in another app

In the base app, which I call it "mywebsite" (the one that contains the settings of the django project), has the urls.py file. But I do not want to reference this file, I want to reference a urls.py in another app, which I call it "account".
For the base file, I would reference as {% url'login' %}, for example. How do I reference to the other one?
Maybe {% account.url 'login' %}?
I tried {% account.url 'login' %} and {% account/url 'login' %}
Never mind, I just added to the base urls.py file: path('account', include("account.urls"))
I see that you already answered your question, but with no further details or explanations. I'll provide the context needed.
You have a project package. This folders contains files that affect all the application (the website, not just an app). Inside we have the following files: settings, wsgi, asgi and urls.
In the urls.py (also called URLconf module) file you control the routing of your application, that is, the mapping between URL path expressions to Python functions (your views).
The Django frameworks knows where to look up for this file because it's stablished in the settings.py file:
ROOT_URLCONF = 'django_project.urls'
This is the process (from the docs)
Django determines the root URLconf module to use. Ordinarily, this is the value of the ROOT_URLCONF setting...
Django loads that Python module and looks for the variable urlpatterns. This should be a sequence of django.urls.path() and/or django.urls.re_path() instances.
Django runs through each URL pattern, in order, and stops at the first one that matches the requested URL, matching against path_info.
Once one of the URL patterns matches, Django imports and calls the given view, which is a Python function (or a class-based view)...
But you also have apps, besides the project package. Each app can also have a URLconf module (app/urls.py).
To Django to know the routing to this apps, you have to include them in the global urls.py file (from the project package).
Finally, the answer:
How do you do it? With the include() function.
As you mentioned in your own answer, the code is:
from django.urls import include, path
urlpatterns = [
path('admin/', admin.site.urls),
path('', include("account.urls")),
]
You're including the accounts paths into the global URLconf module.
So when the user types www.your-domain.com/ he will access the accounts app routes.
If instead you did like below, the user should type: www.your-domain.com/accounts/.
from django.urls import include, path
urlpatterns = [
path('admin/', admin.site.urls),
path('accounts/', include("account.urls")),
]

Setting up simple Django ViewSet APIs from multiple apps

I’m still learning django and assume this may be easy for some. I’m trying to figure out the best way of simply setting up the API URLs (and so that they all display in the api root and can actually be used in the project - in my case at /api/). I’m using django rest framework, and can’t seem to set up more than one API - it works for just one, but the problem occurs when trying to set up another.
So I have an app called pages and accounts (and core - the default where the main urls.py is). I’ve created another urls.py inside the pages app and another inside the accounts app.
accounts/urls.py:
from . import views
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r"accounts", views.AccountsViewSet, basename="accounts")
urlpatterns = router.urls
pages/urls.py:
from . import views
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r"pages", views.PagesViewSet, basename="pages")
urlpatterns = router.urls
And the core urls.py:
from django.contrib import admin
from django.urls import path, include
from rest_framework import routers
from rest_framework.routers import DefaultRouter
router = routers.DefaultRouter()
urlpatterns = [
path("admin/", admin.site.urls),
path("api/", include("pages.urls")), # works but root only shows pages API
# path("api/", include("pages.urls", "accounts.urls")), # fails with error: “django.core.exceptions.ImproperlyConfigured: Specifying a namespace in include() without providing an app_name is not supported. Set the app_name attribute in the included module, or pass a 2-tuple containing the list of patterns and app_name instead.”
# path("api/", include(router.urls)), # no error but root api is empty
]
I would assume, possibly incorrectly, that just router.urls should include all the apis when visiting the root. The root apis currently looks like this when using just include("pages.urls”):
{
"pages": "http://localhost:8000/api/pages/"
}
How can I get it to correctly show all apis? The only way I could do it was by putting router.register(r"accounts", views.AccountsViewSet, basename="accounts”) in the pages urls.py, which is very undesirable, especially as the project grows even further.
Thank you
Have you tried to use:
path("api/pages/", include("pages.urls")),
path("api/accounts/", include("accounts.urls")),
In your urls.py?
Possibly that would mean your routes would be:
{
"pages": "http://localhost:8000/api/pages/pages/"
"accounts": "http://localhost:8000/api/accounts/accounts/"
}
In that case you could try to use
router.register("", views.AccountsViewSet, basename="accounts")
in accounts/urls.py.
And similarly,
router.register("", views.AccountsViewSet, basename="pages")
in pages/urls.py.
That way, you might achieve to have routes like:
{
"pages": "http://localhost:8000/api/pages/"
"accounts": "http://localhost:8000/api/accounts/"
}
if that is what you want.

Path error in django rest framework : Specifying a namespace in include() without providing an app_name is not supported

In my django rest framework app I have Urls.py as follows:
from django.urls import include, path
from .shipper import*
from .views import *
urlpatterns = [path('shipper/',
include(path('shipperitems/', MasterItemsList.as_view()),
path('shippercreateapi/', shipperCreateAPIView.as_view()),)),
]
When I try to run the app it gives me the following error:
django.core.exceptions.ImproperlyConfigured: Specifying a namespace in
include() without providing an app_name is not supported. Set the
app_name attribute in the included module, or pass a 2-tuple
containing t he list of patterns and app_name instead.
what should I do to resolve this ?
You are using the include(...) function in the wrong way. include(...) usually takes the module which contains url-patterns. So change your code as below,
#root urls.py
from django.urls import include, path
urlpatterns = [
path('shipper/', include('shipper.urls'),
]
and
#/shipper/urls.py
urlpatterns = [
path('shipperitems/', MasterItemsList.as_view()),
path('shippercreateapi/', shipperCreateAPIView.as_view()),)),
]
Check the docs for include here. You should pass paramters to include like this
url(r'^reviews/', include('app_name.urls', 'app_name'), namespace='app_name')),
(credits)

Django Using reverse() with multiple URLs that include the same app

EDIT: I'm using rest_framework.reverse and providing the app name not the namespace for the reverse lookup. An example reverse call is:
reverse('api_app:system-detail', kwargs={...}, request=self.context.get('request'))
So I have a django project with a single app, and I have two different url patterns mapped to this one app. I currently have this mostly working, however for a few cases using reverse() results in the wrong namespace. My base urls.py looks like this:
from django.conf.urls import url, include
from django.views.generic.base import RedirectView
urlpatterns = [
url(r'^api/', include('api_project.api_app.urls',
namespace='api')),
url(r'^beta/api/', include('api_project.api_app.urls',
namespace='beta')),
url(r'^$', RedirectView.as_view(url='api', permanent=False),
name='index')
]
EDIT: api_app.urls looks like:
from django.conf.urls import url
from rest_framework.urlpatterns import format_suffix_patterns
from . import views
from rest_framework.schemas import get_schema_view
schema_view = get_schema_view(title="Schema")
app_name = 'api_app'
urlpatterns = [
url(r'^$', views.APIRoot.as_view(), name='api-root'),
url(r'^schema/$', schema_view, name'api-schema'),
....
In most cases reverse works as expected, e.g. using /beta/endpoint has links on the page that look like:
/beta/endpoint/example
However, in a few cases using /beta/endpoint has links like:
/api/endpoint/different-example
My question is less about how reverse determines which namespace to use, but instead is this the appropriate way to use two namespaces for the same app? Is this something I should approach completely differently?
Most of my research so far has not resulted in much success, so I'm thinking this might be fundamentally the wrong approach.
If this approach is okay, then I'm curious why reverse is inconsistent in which namespace it returns.
The documentation on reversing namespaced URLs shows you what happens. In your case, you are instantiating the same app twice, once with the instance namespace api and one with the instance namespace beta.
Your application namespace is api_app, as defined by the app_name variable in your included urls.py file. So it doesn't match any of the two instance namespaces, meaning there is no default application instance.
So in your case, the URL resolver will mostly resolve to case 4: If you don't specify your current_app in the view, it will resolve to the last deployed instance (meaning the last registered), which is "beta".
You should specify your current_app in your view, both when using reverse and on the request object (so that your templates can also resolve correctly).

Can I have two urls.py with different names?

In Django, is it possible to have two different files with url patterns, neither of which is called urls.py ? Or does Django rely on there being only one set of url patterns per Django app, and that it must be called urls.py ?
I'm using Django CMS and I want to split an app across two apphooks and two menus. So I've tried splitting urls.py into pub_urls.py and train_urls.py but I appear to have broken things by doing that, despite the cms_app.py naming the correct urls - eg:
from cms.app_base import CMSApp
from cms.apphook_pool import apphook_pool
from django.utils.translation import ugettext_lazy as _
from resources.menu import TrainingMenu, PublicationMenu
class PublicationApp(CMSApp):
name = _("Publication App") # give your app a name, this is required
urls = ["resources.pub_urls"] # link your app to url configuration(s)
menus = [PublicationMenu]
class TrainingApp(CMSApp):
name = _("Training App") # give your app a name, this is required
urls = ["resources.train_urls"] # link your app to url configuration(s)
menus = [TrainingMenu]
apphook_pool.register(PublicationApp) # register your app
apphook_pool.register(TrainingApp) # register your app
Is something like this possible? Or do I have to split this into two different apps?
There is nothing to stop your urls.py simply acting as a way of including multiple other urls files:
urls.py:
from django.conf.urls.defaults import patterns, include
urlpatterns = urlpatterns + patterns('',
(r'^', include('pub_urls')),
(r'^', include('train_urls')))
pub_urls.py:
from django.conf.urls.defaults import patterns, url
urlpatterns = patterns('',
(r'^$', 'homeview'),
(r'^stuff/$', 'stuffview')
)
etc.
ROOT_URLCONF in your settings file points to the root url file.
Django doesn't care what your urlpatterns file is called. The default base urlconf is by convention called urls.py, but in fact that's just a setting and can be overridden. After that, you need to explicitly include urlconfs by module name, so again it makes no difference what they're called.
I'm not familiar with Django-CMS and I don't know what it's doing in its CMSApp class, but I suspect you're going to have to dig into that to see what's going on.
It is configurable using ROOT_URLCONF setting.
From django docs
ROOT_URLCONF
A string representing the full Python import path to your root URLconf.
For example: "mydjangoapps.urls". Can be overridden on a per-request basis
by setting the attribute urlconf on the incoming HttpRequest object. See How
Django processes a request for details.
You can also write/get a middleware which can set it appropriately depending upon the host or other parameters in the request.