Opened 5 years ago

Closed 5 years ago

Last modified 3 years ago

#22218 closed Cleanup/optimization (fixed)

Deprecate 'prefix' arg to django.conf.urls.patterns

Reported by: Carl Meyer Owned by: Tim Graham
Component: Core (URLs) Version: master
Severity: Normal Keywords:
Cc: loic@…, Simon Charette Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no


In the olden days of Django, it was encouraged to reference views as strings in url patterns:

urlpatterns = patterns('',
    url('^$', 'myapp.views.myview'),

and Django would magically import "myapp.views.myview" internally and turn the string into a real function reference.

In order to reduce repetition when referencing many views from the same module, the patterns function takes a required initial "prefix" arg, which is prepended to all views-as-strings in that set of url patterns:

urlpatterns = patterns('myapp.views',
    url('^$', 'myview'),
    url('^other/$', 'otherview'),

In the modern era, we have updated the tutorial to instead recommend actually importing your views module and referencing your view functions (or classes) directly. This has a number of advantages, all deriving from the fact that we are now using Normal Python in place of Django String Magic: the errors when you mistype a view name are less obscure, IDEs can help with autocompleting view names, etc.

With the advent of the class-based generic views, it is even more likely that users are referencing view callables directly in their urlconf, due to the need to call .as_view().

So these days, the above use of the prefix arg is much more likely to be written (and is better written) as:

from myapp import views

urlpatterns = patterns('',
    url('^$', views.myview),
    url('^other/$', views.otherview),

This leaves the 'prefix' arg in modern Django code as a useless empty string that has to be passed to every invocation of patterns. This is an ugly API wart, and a burden when teaching new users (answering the newbie's question "why do I need this empty string as the first argument to patterns?" requires either a time-wasting detour into the history of Django URLconfs or a hand-wavy "don't worry, just do it").

I suggest that we deprecate this argument. This will require type-checking the first argument to patterns for the duration of the deprecation period, but I don't think that's so bad.

(I would not be opposed to deprecating the entire views-as-strings feature, but the additional gain there is only code we can remove and stop maintaining, not a benefit to the public API, so I decided to keep this proposal minimal. If there's consensus to deprecate the entire feature, I will happily update the summary to reflect that.)

Change History (10)

comment:1 Changed 5 years ago by loic84

Cc: loic@… added
Triage Stage: UnreviewedAccepted
Version: 1.6master

+1 for this cleanup, I always considered prefix an anti-pattern.

comment:2 Changed 5 years ago by Marc Tamlyn

Agreed we should get rid of prefix. I've got some ideas around which actually involve changing URLconfs and resolving/reversing more completely. In particular, if we deprecate prefix on patterns, then at least 50% of the use case of url() rather than RegexURLResolver is unneeded.

*<Going a bit off the real topic of this ticket now, but it's kinda related>*

On the topic of beginners, I've heard a number of complaints about the fact the only built in method of producing urls is regexes. Whilst these are very powerful, learning the (somewhat obscure) syntax of (?P<poll_id>\d+) and remembering it every time you want to write a URL is a non-trivial hurdle (and that's before you start on the differing behaviour of \w on Py2/3). Perhaps we should include a solution similar to

Another irritation of mine is django.conf.urls vs django.core.urlresolvers - everything should live in django.urls.

Also, the syntax for django.shortcuts.reverse is much better than that of django.core.urlresolvers.reverse - the kwargs={} pattern is annoying as (generally) you don't use args in a urlpattern.

Other related issues (which may or may not be resolved) are:

  • The sea of handlerXXX functions (and being able to customise these dependent on the patterns - API views vs real views)
  • The general weirdness of namespacing (currently a powerful solution but 90% of use cases don't need the complexity)
  • The ability to decorate views at the patterns level (rather than each view individually)
  • Deprecate models.permalink
  • Our "absolute urls" are not absolute
  • Reversing by dotted path to view is just a bad idea

I haven't yet decided what the new API should look like, but a significant advantage of putting it in django.urls is all the main public functions can stay exactly as they are for deprecation and proxy to the new version. People using RegexURLResolver directly might have issues, but that can likely be worked around as well. This probably needs a django-dev discussion.

For the sake of clarity, I'm not against fixing this ticket on its own, I think there's some value in it. However I would like (at some point) to "fix" url resolving in general. This whole section of the code base (and its design) are very old, and it might be worth reworking the whole thing, rather than adding type checking on first arguments for a while.

comment:3 Changed 5 years ago by Simon Charette

Cc: Simon Charette added

What about deprecating django.conf.urls.patterns altogether? If we remove support for the prefix argument I see no compelling reason of keeping it around.

Updating the documentation to use a list of urls instead should be straightforward.

from myapp import views

urlpatterns = [
    url('^$', views.myview),
    url('^other/$', views.otherview),

comment:4 Changed 5 years ago by Tim Graham

Owner: changed from nobody to Tim Graham
Status: newassigned

+1 to deprecating patterns entirely. I've started working on this. The other thing that patterns provides besides the prefix parameter is automatically wrapping plain tuples in urlpatterns in url(). I think removing this and forcing usage of url() will be a good cleanup.

comment:5 Changed 5 years ago by Tim Graham

Any thoughts about what we should do about django.conf.urls.i18n.i18n_patterns?

I don't use it, but it looks like deprecating its call to patterns might work.

def i18n_patterns(prefix, *args):
    Adds the language code prefix to every URL pattern within this
    function. This may only be used in the root URLconf, not in an included
    pattern_list = patterns(prefix, *args)
    if not settings.USE_I18N:
        return pattern_list
    return [LocaleRegexURLResolver(pattern_list)]

comment:6 Changed 5 years ago by Tim Graham

Has patch: set

I will answer my own question and say we should deprecate just the prefix argument to i18n_patterns().

PR is ready for final review.

comment:7 Changed 5 years ago by Gwildor Sok

This really cleans up the code a lot, wow. Awesome change!

comment:8 Changed 5 years ago by Tim Graham <timograham@…>

Resolution: fixed
Status: assignedclosed

In d73d0e071c1b4c86d57994a0ab55a74cfe80cdf5:

Fixed #22218 -- Deprecated django.conf.urls.patterns.

Thanks Carl Meyer for the suggestion and Alex Gaynor and Carl for reviews.

comment:9 Changed 5 years ago by Tim Graham <timograham@…>

In e4708385fde278fbbff5428f756632455c2bcaf5:

Merge pull request #2825 from collinanderson/patch-2

Made url syntax consistent in tutorial; refs #22218.

comment:10 Changed 3 years ago by Tim Graham <timograham@…>

In a25d3ce0:

Refs #22218 -- Removed conf.urls.patterns() per deprecation timeline.

Note: See TracTickets for help on using tickets.
Back to Top