Code

Opened 5 years ago

Last modified 3 months ago

#11642 new New feature

Specify default namespace in urls.py

Reported by: mrmachine Owned by: abki
Component: Core (URLs) Version: 1.1
Severity: Normal Keywords: url namespace default
Cc: amirouche.boubekki@… Triage Stage: Accepted
Has patch: no Needs documentation: yes
Needs tests: yes Patch needs improvement: yes
Easy pickings: no UI/UX: no

Description

The new URL namespace feature is great, but could be made a little easier to use by allowing a default namespace to be defined as a variable in the urls module being included (e.g. namespace). This could be looked up by Django automatically if no namespace is specified when the urls module is included, in the same way that urlpatterns is looked up.

In myapp/urls.py:

namespace = 'myapp'

urlpatterns('myapp',
    url('^$', 'index', name='index'),
)

In myproject/urls.py:

urlpatterns('',
    url(r'^myapp/$', include('myapp.urls')),
)

In your templates:

{% url myapp:index %}

The benefit here is that users will not need to specify a namespace for every app in their urls module to avoid clashes with commonly named urls (e.g. "index"), unless they are deploying multiple instances of the same app, which I would have thought is the less common case.

At the moment I believe the only way to specify a default namespace (like the admin app does) is to define a three item tuple somewhere in myapp which contains urlpatterns from myapp.urls plus app and instance namespaces. But this can't be referenced by a simple string like the urls module can, it must be imported directly before being used. It also seems a little messy and counter-intuitive asking users to import and include myapp.urls.namespace or myapp.urls_namespace or something similar, instead of just importing or referencing the urls module.

Attachments (1)

11642-default-namespace-r11413.diff (1.5 KB) - added by mrmachine 5 years ago.
Get default app_name from urlconf module.

Download all attachments as: .zip

Change History (11)

comment:1 Changed 5 years ago by russellm

  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset
  • Triage Stage changed from Unreviewed to Accepted

There's the start of an idea here, but it's not fully formed. For starters, there isn't a single concept of a 'namespace' - there's the application namespace and the instance namespace. What you refer to as the "default" namespace is the application namespace.

Essentially, it sounds like you're proposing that include('myapp.urls', namespace='foo', app_name='bar') can be made easier for the case where an application knows it can provide a namespace. I agree with this. However, it isn't quite as simple as you describe. By definition, you can't define the instance namespace in the way you describe. However, you could define the application namespace in this way.

There are some issues that need to be resolved here - for instance:

  • What happens when you define an application namespace, but not an instance namespace? This would happen if you ever did include('myapp.urls') when myapp.urls defines an app_name internally.
  • What takes precedence? An app_name defined in the include, or in the app itself?

I'll mark this as accepted in principle - but there is some design work that still needs to be done. If you can resolve what happens in the edge cases, this shouldn't be too hard to implement (patches welcome).

comment:2 Changed 5 years ago by mtredinnick

I'm slightly against the concept of forcing or even encouraging namespace use just to avoid clashes in poorly named URL patterns. We already recommend making URL pattern names unique. They are top-level names. Just like your Python module names have to be unique amongst all modules on the Python path, etc. Coming up with unique names isn't even that hard.

So the primary motivation behind this ticket is a bit suspect.

comment:3 Changed 5 years ago by russellm

@malcolm - I agree completely that this isn't the solution for making 'index' a unique name. I'm almost starting to regret using the term "namespace", since it seems to be encouraging the use of namespaces to tell app1.index from app2.index, which (as you well know) wasn't the intention.

My motivation for accepting this ticket was to provide an analog for the way dynamic url deployments (e.g., what admin uses) specify default application namespaces.

Consider contrib.admin. When you deploy admin using include(admin.site.urls), the admin app provides a triple that gives you the instance name and application namespace to use. In that way, the application is designed to be deployed multiple times, as it's impossible to deploy the app without a namespace.

As a counterpoint, consider contrib.comments. This uses a traditional urls module for deployment, so it means if you want to use an application level namespace, you need to include('comments.urls', namespace='foo', app_name='comments'). There is no analog for getting the application name as a default during deployment.

My ideal picture is that if an application developer knows a priori that their application is a candidate for multiple deployment, then they could specify an application namespace. include('myapp.urls') would deploy an instance of the app with the default (i.e. == app) namespace - just like admin does. include('myapp.urls', namespace='foo') would deploy an instance of the app with a specific name.

It's not a major problem - just an inconsistency that I think is worth looking at. If we can't find a way to provide this capability elegantly and unambiguously, then I'll be the first to throw the idea overboard.

comment:4 Changed 5 years ago by mrmachine

I'm not sure why there is resistance to using namespaces to avoid URL name clashes between different apps, as well as allowing multiple deployments of the same app. Both are useful. An app developer should only need to worry about named URLs being unique within their own app because this is all they have control over, and Django provides a way for users to resolve app name conflicts if they occur.

Looking at the admin app as a real world example, it's probably far more common that people only deploy one instance, yet it provides default app and instance names, and has a URL named simply "index". All apps should be able to follow this example, without having to configure and import a dynamic urlconf like the admin app does.

I've written up a patch that behaves according to Russell's ideal picture above. The app developer can specify a default app name by assigning a string to the app_name variable in the app's urlconf module. This is something of a "magic" variable tied to the urlconf (much like urlpatterns), but after looking at a few alternatives, I think it is still the best option as it is under the control of the app developer, and doesn't require anything different or special from the user when importing the urlconf module the same as any other app. Then include('myapp.urls') will use myapp.urls.app_name (if it is set) as the default app name. The user can always override the app/instance names with the namespace and app_name arguments to include().

The docs regarding URL namespaces say that "the default application instance is the instance that has an instance namespace matching the application namespace", so I've also modified include() to use app_name (specified or default) as the default instance name.

The current include() function will raise an ImproperlyConfigured exception if a tuple is passed in as arg *and* namespace is also specified. Although strangely, no exception is raised if app_name is specified, and this is silently discarded. There appears to be an assumption that only dynamic modules that provide a namespace will ever pass a tuple to include() (of which I'm only aware of one, the admin app), and these should be configured to use a different namespace elsewhere. But this isn't reflected in the docs about URL namespacing which lists a tuple as second option to specify namespaces.

I think that any values passed to include() as namespace or app_name should always override the defaults (obtained from the tuple or urlconf module), and when using a dynamic module that provides a namespace, users shouldn't be trying to override it anyway. I've removed this exception in my patch and made include() behave in the same way whether a tuple or string or urlconf module is passed in. For a tuple, the defaults are taken from the 2nd and 3rd items. For a string or urlconf module, the defaults are taken from the app_name attribute of the urlconf module. For both, app_name is the default namespace.

Changed 5 years ago by mrmachine

Get default app_name from urlconf module.

comment:5 Changed 3 years ago by julien

  • Severity set to Normal
  • Type set to New feature

comment:6 Changed 3 years ago by twoolie

  • Easy pickings unset
  • UI/UX unset

why did this stagnate? EDIT: did not unset UI/UX or Easy pickings on purpose.

Last edited 3 years ago by twoolie (previous) (diff)

comment:7 Changed 20 months ago by aaugustin

  • Component changed from Core (Other) to Core (URLs)

comment:8 Changed 16 months ago by abki

  • Cc amirouche.boubekki@… added
  • Needs documentation set
  • Needs tests set
  • Owner changed from nobody to abki
  • Patch needs improvement set
  • Status changed from new to assigned

deleted

Last edited 16 months ago by abki (previous) (diff)

comment:9 Changed 16 months ago by abki

  • Status changed from assigned to new

comment:10 Changed 3 months ago by aaugustin

This may be superseded by #21927.

Add Comment

Modify Ticket

Change Properties
<Author field>
Action
as new
The owner will be changed from abki to anonymous. Next status will be 'assigned'
as The resolution will be set. Next status will be 'closed'
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.