#25409: Allow url and groups of urls to be easily tagged and selected
-------------------------------------+-------------------------------------
     Reporter:  atul-bhouraskar      |                    Owner:  atul-
                                     |  bhouraskar
         Type:  New feature          |                   Status:  assigned
    Component:  Core (URLs)          |                  Version:  master
     Severity:  Normal               |               Resolution:
     Keywords:                       |             Triage Stage:
                                     |  Unreviewed
    Has patch:  1                    |      Needs documentation:  0
  Needs tests:  0                    |  Patch needs improvement:  0
Easy pickings:  0                    |                    UI/UX:  0
-------------------------------------+-------------------------------------
Changes (by atul-bhouraskar):

 * status:  new => assigned
 * needs_better_patch:   => 0
 * needs_tests:   => 0
 * owner:  nobody => atul-bhouraskar
 * needs_docs:   => 0


Old description:

> == The Problem ==
> Often (usually in middleware) processing has to be applied to a certain
> URLs only eg CORS.
>
> The usual way to specify this would be to create an additional set of
> regex patterns identifying these urls - eg.
> {{{
> CORS_URLS_REGEX = r'^/api/.*$'
> }}}
> The middleware then typically matches the incoming request URL to the
> regex and determines whether it is to be selected for processing.
>
> This approach has several limitations including:
> * It violates DRY as the regexes in the settings have to be synced with
> the actual URL patterns
> * Matching multiple patterns may require the app writer to essentially
> reinvent URL patterns - poorly.
>
> == The Proposal ==
> Add an optional {{{tags}}} keyword argument to {{{django.conf.urls.url}}}
> allowing a URL to be optionally tagged with one or more tags which can
> then be retrieved via {{{HttpRequest.resolver_match.tags}}} in the
> middleware / view etc.
> {{{
> urlpatterns = [
>     url(r'^$', views.home, name='home'),
>     url(r'^articles/$', views.show_articles, name='show-articles',
> tags=['require_GET']),
>     url(r'^private/$', include(private_patterns),
> tags=['login_required']),
>     url(r'^api/', include(api_patterns), tags=[
>         'cors_allowed', 'nologin_forbidden', 'ajax_required'
>     ]),
> ]
> }}}
>
> In the example above, the {{{home}}} url has no tags, the {{{show-
> articles}}} url is tagged {{{require_GET}}}, the urls under
> {{{private/}}} are all tagged {{{login_required}}} while the the urls
> under  {{{api/}}} have multiple tags.
>
> This allows app middleware to selectively process urls very easily:
> {{{
> class LoginRequiredMiddleware(object):
>     def process_view(self, request, view_func, view_args, view_kwargs):
>         if 'login_required' in request.resolver_match.tags and \
>                 not request.user.is_authenticated():
>             return redirect('login')
>
> class RequireGETMiddleware(object):
>     def process_view(self, request, view_func, view_args, view_kwargs):
>         if 'require_GET' in request.resolver_match.tags and
> request.method != 'GET':
>             return HttpResponseNotAllowed(['GET'])
>
> class AjaxRequiredMiddleware(object):
>     def process_view(self, request, view_func, view_args, view_kwargs):
>         if 'ajax_required' in request.resolver_match.tags and not
> request.is_ajax():
>             return HttpResponseForbidden()
>
> class CorsMiddleware(object):
>     def process_view(self, request, view_func, view_args, view_kwargs):
>         if 'cors_allowed' in request.resolver_match.tags:
>             # continue CORS processing
> }}}
>
> I am attaching a patch that implements this tagging feature to
> urlpatterns. It has tests (docs to be added). The change is fully
> backwards compatible as specifying tags is completely optional, all
> existing tests pass with the patch applied to master.
>
> Comments welcome!

New description:

 == The Problem ==
 Often (usually in middleware) processing has to be applied to a certain
 URLs only eg CORS.

 The usual way to specify this would be to create an additional set of
 regex patterns identifying these urls - eg.
 {{{
 CORS_URLS_REGEX = r'^/api/.*$'
 }}}
 The middleware then typically matches the incoming request URL to the
 regex and determines whether it is to be selected for processing.

 This approach has several limitations including:
 * It violates DRY as the regexes in the settings have to be synced with
 the actual URL patterns
 * Matching multiple patterns may require the app writer to essentially
 reinvent URL patterns - poorly.

 == The Proposal ==
 Add an optional {{{tags}}} keyword argument to {{{django.conf.urls.url}}}
 allowing a URL to be optionally tagged with one or more tags which can
 then be retrieved via {{{HttpRequest.resolver_match.tags}}} in the
 middleware / view etc.
 {{{
 urlpatterns = [
     url(r'^$', views.home, name='home'),
     url(r'^articles/$', views.show_articles, name='show-articles',
 tags=['require_GET']),
     url(r'^private/$', include(private_patterns),
 tags=['login_required']),
     url(r'^api/', include(api_patterns), tags=[
         'cors_allowed', 'nologin_forbidden', 'ajax_required'
     ]),
 ]
 }}}

 In the example above, the {{{home}}} url has no tags, the {{{show-
 articles}}} url is tagged {{{require_GET}}}, the urls under {{{private/}}}
 are all tagged {{{login_required}}} while the the urls under  {{{api/}}}
 have multiple tags.

 This allows app middleware to selectively process urls very easily:
 {{{
 class LoginRequiredMiddleware(object):
     def process_view(self, request, view_func, view_args, view_kwargs):
         if 'login_required' in request.resolver_match.tags and \
                 not request.user.is_authenticated():
             return redirect('login')

 class RequireGETMiddleware(object):
     def process_view(self, request, view_func, view_args, view_kwargs):
         if 'require_GET' in request.resolver_match.tags and request.method
 != 'GET':
             return HttpResponseNotAllowed(['GET'])

 class AjaxRequiredMiddleware(object):
     def process_view(self, request, view_func, view_args, view_kwargs):
         if 'ajax_required' in request.resolver_match.tags and not
 request.is_ajax():
             return HttpResponseForbidden()

 class CorsMiddleware(object):
     def process_view(self, request, view_func, view_args, view_kwargs):
         if 'cors_allowed' in request.resolver_match.tags:
             # continue CORS processing
 }}}

 I am attaching a patch that implements this tagging feature to
 urlpatterns. It has tests (docs to be added). The change is fully
 backwards compatible as specifying tags is completely optional, all
 existing tests pass with the patch applied to master.

 I'm working on my branch at https://github.com/atul-
 bhouraskar/django/tree/ticket_25409

 Comments welcome!

--

--
Ticket URL: <https://code.djangoproject.com/ticket/25409#comment:1>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-updates+unsubscr...@googlegroups.com.
To post to this group, send email to django-updates@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/073.24c78775119e54b901b823d0d3616037%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to