diff --git a/AUTHORS b/AUTHORS index bd194f7f3f..65d1af7105 100644 --- a/AUTHORS +++ b/AUTHORS @@ -732,6 +732,7 @@ answer newbie questions, and generally made Django that much better: Simon Meers Simon Williams Simon Willison + Sjoerd Job Postmus Slawek Mikula sloonz smurf@smurf.noris.de diff --git a/django/conf/project_template/project_name/urls.py-tpl b/django/conf/project_template/project_name/urls.py-tpl index 30ddffb876..e23d6a92ba 100644 --- a/django/conf/project_template/project_name/urls.py-tpl +++ b/django/conf/project_template/project_name/urls.py-tpl @@ -5,17 +5,17 @@ The `urlpatterns` list routes URLs to views. For more information please see: Examples: Function views 1. Add an import: from my_app import views - 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') + 2. Add a URL to urlpatterns: path('', views.home, name='home') Class-based views 1. Add an import: from other_app.views import Home - 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') Including another URLconf - 1. Import the include() function: from django.conf.urls import url, include - 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ -from django.conf.urls import url from django.contrib import admin +from django.urls import path urlpatterns = [ - url(r'^admin/', admin.site.urls), + path('admin/', admin.site.urls), ] diff --git a/django/conf/urls/__init__.py b/django/conf/urls/__init__.py index b7ad156122..7bda34516b 100644 --- a/django/conf/urls/__init__.py +++ b/django/conf/urls/__init__.py @@ -1,4 +1,4 @@ -from django.urls import RegexURLPattern, RegexURLResolver, include +from django.urls import include, re_path from django.views import defaults __all__ = ['handler400', 'handler403', 'handler404', 'handler500', 'include', 'url'] @@ -10,11 +10,4 @@ handler500 = defaults.server_error def url(regex, view, kwargs=None, name=None): - if isinstance(view, (list, tuple)): - # For include(...) processing. - urlconf_module, app_name, namespace = view - return RegexURLResolver(regex, urlconf_module, kwargs, app_name=app_name, namespace=namespace) - elif callable(view): - return RegexURLPattern(regex, view, kwargs, name) - else: - raise TypeError('view must be a callable or a list/tuple in the case of include().') + return re_path(regex, view, kwargs, name) diff --git a/django/conf/urls/i18n.py b/django/conf/urls/i18n.py index 70d99b4f29..325cc9e60e 100644 --- a/django/conf/urls/i18n.py +++ b/django/conf/urls/i18n.py @@ -1,8 +1,7 @@ import functools from django.conf import settings -from django.conf.urls import url -from django.urls import LocaleRegexURLResolver, get_resolver +from django.urls import LocalePrefixPattern, URLResolver, get_resolver, path from django.views.i18n import set_language @@ -13,7 +12,12 @@ def i18n_patterns(*urls, prefix_default_language=True): """ if not settings.USE_I18N: return list(urls) - return [LocaleRegexURLResolver(list(urls), prefix_default_language=prefix_default_language)] + return [ + URLResolver( + LocalePrefixPattern(prefix_default_language=prefix_default_language), + list(urls), + ) + ] @functools.lru_cache(maxsize=None) @@ -25,11 +29,11 @@ def is_language_prefix_patterns_used(urlconf): ) """ for url_pattern in get_resolver(urlconf).url_patterns: - if isinstance(url_pattern, LocaleRegexURLResolver): - return True, url_pattern.prefix_default_language + if isinstance(url_pattern.pattern, LocalePrefixPattern): + return True, url_pattern.pattern.prefix_default_language return False, False urlpatterns = [ - url(r'^setlang/$', set_language, name='set_language'), + path('setlang/', set_language, name='set_language'), ] diff --git a/django/conf/urls/static.py b/django/conf/urls/static.py index 216602229f..150f4ffd3f 100644 --- a/django/conf/urls/static.py +++ b/django/conf/urls/static.py @@ -1,8 +1,8 @@ import re from django.conf import settings -from django.conf.urls import url from django.core.exceptions import ImproperlyConfigured +from django.urls import re_path from django.views.static import serve @@ -23,5 +23,5 @@ def static(prefix, view=serve, **kwargs): # No-op if not in debug mode or a non-local prefix. return [] return [ - url(r'^%s(?P.*)$' % re.escape(prefix.lstrip('/')), view, kwargs=kwargs), + re_path(r'^%s(?P.*)$' % re.escape(prefix.lstrip('/')), view, kwargs=kwargs), ] diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index 7a4ff947a8..6e4ad180ac 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -567,7 +567,7 @@ class ModelAdmin(BaseModelAdmin): return inline_instances def get_urls(self): - from django.conf.urls import url + from django.urls import path def wrap(view): def wrapper(*args, **kwargs): @@ -578,14 +578,14 @@ class ModelAdmin(BaseModelAdmin): info = self.model._meta.app_label, self.model._meta.model_name urlpatterns = [ - url(r'^$', wrap(self.changelist_view), name='%s_%s_changelist' % info), - url(r'^add/$', wrap(self.add_view), name='%s_%s_add' % info), - url(r'^autocomplete/$', wrap(self.autocomplete_view), name='%s_%s_autocomplete' % info), - url(r'^(.+)/history/$', wrap(self.history_view), name='%s_%s_history' % info), - url(r'^(.+)/delete/$', wrap(self.delete_view), name='%s_%s_delete' % info), - url(r'^(.+)/change/$', wrap(self.change_view), name='%s_%s_change' % info), + path('', wrap(self.changelist_view), name='%s_%s_changelist' % info), + path('add/', wrap(self.add_view), name='%s_%s_add' % info), + path('autocomplete/', wrap(self.autocomplete_view), name='%s_%s_autocomplete' % info), + path('/history/', wrap(self.history_view), name='%s_%s_history' % info), + path('/delete/', wrap(self.delete_view), name='%s_%s_delete' % info), + path('/change/', wrap(self.change_view), name='%s_%s_change' % info), # For backwards compatibility (was the change url before 1.9) - url(r'^(.+)/$', wrap(RedirectView.as_view( + path('/', wrap(RedirectView.as_view( pattern_name='%s:%s_%s_change' % ((self.admin_site.name,) + info) ))), ] @@ -1173,8 +1173,7 @@ class ModelAdmin(BaseModelAdmin): opts = obj._meta to_field = request.POST.get(TO_FIELD_VAR) attr = str(to_field) if to_field else opts.pk.attname - # Retrieve the `object_id` from the resolved pattern arguments. - value = request.resolver_match.args[0] + value = request.resolver_match.kwargs['object_id'] new_value = obj.serializable_value(attr) popup_response_data = json.dumps({ 'action': 'change', diff --git a/django/contrib/admin/sites.py b/django/contrib/admin/sites.py index c0767c15ee..2e37ade62e 100644 --- a/django/contrib/admin/sites.py +++ b/django/contrib/admin/sites.py @@ -196,11 +196,11 @@ class AdminSite: class MyAdminSite(AdminSite): def get_urls(self): - from django.conf.urls import url + from django.urls import path urls = super().get_urls() urls += [ - url(r'^my_view/$', self.admin_view(some_view)) + path('my_view/', self.admin_view(some_view)) ] return urls @@ -230,7 +230,7 @@ class AdminSite: return update_wrapper(inner, view) def get_urls(self): - from django.conf.urls import url, include + from django.urls import include, path, re_path # Since this module gets imported in the application's root package, # it cannot import models from other applications at the module level, # and django.contrib.contenttypes.views imports ContentType. @@ -244,15 +244,21 @@ class AdminSite: # Admin-site-wide views. urlpatterns = [ - url(r'^$', wrap(self.index), name='index'), - url(r'^login/$', self.login, name='login'), - url(r'^logout/$', wrap(self.logout), name='logout'), - url(r'^password_change/$', wrap(self.password_change, cacheable=True), name='password_change'), - url(r'^password_change/done/$', wrap(self.password_change_done, cacheable=True), - name='password_change_done'), - url(r'^jsi18n/$', wrap(self.i18n_javascript, cacheable=True), name='jsi18n'), - url(r'^r/(?P\d+)/(?P.+)/$', wrap(contenttype_views.shortcut), - name='view_on_site'), + path('', wrap(self.index), name='index'), + path('login/', self.login, name='login'), + path('logout/', wrap(self.logout), name='logout'), + path('password_change/', wrap(self.password_change, cacheable=True), name='password_change'), + path( + 'password_change/done/', + wrap(self.password_change_done, cacheable=True), + name='password_change_done', + ), + path('jsi18n/', wrap(self.i18n_javascript, cacheable=True), name='jsi18n'), + path( + 'r///', + wrap(contenttype_views.shortcut), + name='view_on_site', + ), ] # Add in each model's views, and create a list of valid URLS for the @@ -260,7 +266,7 @@ class AdminSite: valid_app_labels = [] for model, model_admin in self._registry.items(): urlpatterns += [ - url(r'^%s/%s/' % (model._meta.app_label, model._meta.model_name), include(model_admin.urls)), + path('%s/%s/' % (model._meta.app_label, model._meta.model_name), include(model_admin.urls)), ] if model._meta.app_label not in valid_app_labels: valid_app_labels.append(model._meta.app_label) @@ -270,7 +276,7 @@ class AdminSite: if valid_app_labels: regex = r'^(?P' + '|'.join(valid_app_labels) + ')/$' urlpatterns += [ - url(regex, wrap(self.app_index), name='app_list'), + re_path(regex, wrap(self.app_index), name='app_list'), ] return urlpatterns diff --git a/django/contrib/admindocs/urls.py b/django/contrib/admindocs/urls.py index bfc9648e83..bc9c3df7cf 100644 --- a/django/contrib/admindocs/urls.py +++ b/django/contrib/admindocs/urls.py @@ -1,32 +1,50 @@ -from django.conf.urls import url from django.contrib.admindocs import views +from django.urls import path, re_path urlpatterns = [ - url(r'^$', + path( + '', views.BaseAdminDocsView.as_view(template_name='admin_doc/index.html'), - name='django-admindocs-docroot'), - url(r'^bookmarklets/$', + name='django-admindocs-docroot', + ), + path( + 'bookmarklets/', views.BookmarkletsView.as_view(), - name='django-admindocs-bookmarklets'), - url(r'^tags/$', + name='django-admindocs-bookmarklets', + ), + path( + 'tags/', views.TemplateTagIndexView.as_view(), - name='django-admindocs-tags'), - url(r'^filters/$', + name='django-admindocs-tags', + ), + path( + 'filters/', views.TemplateFilterIndexView.as_view(), - name='django-admindocs-filters'), - url(r'^views/$', + name='django-admindocs-filters', + ), + path( + 'views/', views.ViewIndexView.as_view(), - name='django-admindocs-views-index'), - url(r'^views/(?P[^/]+)/$', + name='django-admindocs-views-index', + ), + path( + 'views//', views.ViewDetailView.as_view(), - name='django-admindocs-views-detail'), - url(r'^models/$', + name='django-admindocs-views-detail', + ), + path( + 'models/', views.ModelIndexView.as_view(), - name='django-admindocs-models-index'), - url(r'^models/(?P[^\.]+)\.(?P[^/]+)/$', + name='django-admindocs-models-index', + ), + re_path( + r'^models/(?P[^\.]+)\.(?P[^/]+)/$', views.ModelDetailView.as_view(), - name='django-admindocs-models-detail'), - url(r'^templates/(?P