Opened 13 years ago

Closed 13 years ago

#15645 closed (duplicate)

HTTP methods in urls.py

Reported by: Matt Harasymczuk Owned by: nobody
Component: HTTP handling Version:
Severity: Keywords: http urls
Cc: Triage Stage: Design decision needed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

It would be nice to have possibility to distinguish a HTTP method in urls.py.
IMHO it would be clearer and more extensible in future
for example:

urlpatterns = patterns('',
  url ('POST', r'/user/(?P<username>\d+)$', 'myapp.views.user.view1'),
  url ('GET', r'/user/(?P<username>\d+)$', 'myapp.views.user.view2'),
  url ('DELETE', r'/user/(?P<username>\d+)$', 'myapp.views.user.delete'),
)

Change History (6)

comment:1 by Julien Phalip, 13 years ago

Triage Stage: UnreviewedDesign decision needed

Why not, although the suggested API would not be backwards compatible. Also, if the method didn't match then a 404 would systematically be returned, which is quite restrained. Marking DDN for now.

comment:2 by Matt Harasymczuk, 13 years ago

it could be backward compatible.
HTTP methods could be extracted if only there is an url() function, not simple list ()

and url will check for its first param, if it is HTTP method GET, PUT, DELETE, POST, etc, then do action with regex and view
it not, do the old way

However I think it is not necessary complicated.

I suggest a http function:

urlpatterns = patterns('',
  http ('POST', r'/user/(?P<username>\d+)$', 'myapp.views.user.view1'),
  http ('GET', r'/user/(?P<username>\d+)$', 'myapp.views.user.view2'),
  http ('DELETE', r'/user/(?P<username>\d+)$', 'myapp.views.user.delete'),
)

and url do the same as it is now

Last edited 13 years ago by Matt Harasymczuk (previous) (diff)

comment:3 by Matt Harasymczuk, 13 years ago

I know that this maybe encumber an urlpatterns but consider this:

from django.conf.urls.defaults import http
from django.conf.urls.defaults import url
from django.conf.urls.defaults import patterns
from django.conf.urls.defaults import include
from django.contrib import admin

urlpatterns = patterns('myapp',
  http.ajax (r'/user/(?P<username>\d+)$', 'views.view1'),
  http.post (r'/user/(?P<username>\d+)$', 'views.view2'),
  http.get (r'/user/(?P<username>\d+)$', 'views.view2'),
  http.delete (r'/user/(?P<username>\d+)$', 'views.delete'),

  # an url function will do the old way
  url (r'^', include(admin.site.urls)),
)

or equivalent

from django.conf.urls.defaults import http
from django.conf.urls.defaults import url
from django.conf.urls.defaults import patterns
from django.conf.urls.defaults import include
from django.contrib import admin

urlpatterns = patterns('myapp',
  http ('POST', r'/user/(?P<username>\d+)$', 'views.view1'),
  http ('GET', r'/user/(?P<username>\d+)$', 'views.view2'),
  http ('DELETE', r'/user/(?P<username>\d+)$', 'views.delete'),

  # an url function will do the old way
  url (r'^', include(admin.site.urls)),
)

therefore maybe a request not http module name is better to consider

IMHO:
first (http.ajax, http.get, http.post...) solution is the most clear and the best fit to the explicit python policy
it is simple and I think it would boost urls dispatcher performance
when it may easy and fast filter lot of regexp pattern, hence there is no need to lookup in POST list regexps when we have a GET request

maybe distinguishing of ajax methods isn't a bad idea too:

from django.conf.urls.defaults import ajax
from django.conf.urls.defaults import patterns

urlpatterns = patterns('myapp',
  ajax.post (r'/user/(?P<username>\d+)$', 'views.view2'),
  ajax.get (r'/user/(?P<username>\d+)$', 'views.view2'),
  ajax.delete (r'/user/(?P<username>\d+)$', 'views.delete'),
)

It gives a little logic to urls.py but gives a performance, simplicity and readability in return

this in conclusion would give us:

from django.conf.urls.defaults import ajax
from django.conf.urls.defaults import http
from django.conf.urls.defaults import url
from django.conf.urls.defaults import patterns
from django.conf.urls.defaults import include
from django.contrib import admin

urlpatterns = patterns('myapp',
  ajax.post (r'/user/(?P<username>\d+)$', 'views.viewAjax1'),
  ajax.get (r'/user/(?P<username>\d+)$', 'views.viewAjax2'),
  ajax.delete (r'/user/(?P<username>\d+)$', 'views.delete'),
  ajax.all (r'/user/(?P<username>\d+)$', 'views.delete'), #all type of methods

  ajax (r'/user/(?P<username>\d+)$', 'views.delete'),  #all type of methods
  ajax ('POST', r'/user/(?P<username>\d+)$', 'views.view2'),
  ajax ('GET', r'/user/(?P<username>\d+)$', 'views.view2'),
  ajax ('DELETE', r'/user/(?P<username>\d+)$', 'views.view2'),

  http.post (r'/user/(?P<username>\d+)$', 'views.view2'),
  http.get (r'/user/(?P<username>\d+)$', 'views.view2'),
  http.delete (r'/user/(?P<username>\d+)$', 'views.delete'),
  
  http (r'/user/(?P<username>\d+)$', 'views.delete'), #all type of methods
  http ('POST', r'/user/(?P<username>\d+)$', 'views.view2')
  http ('GET', r'/user/(?P<username>\d+)$', 'views.view2')
  http ('DELETE', r'/user/(?P<username>\d+)$', 'views.view2')

  # an url function will do the old way
  url (r'^', include(admin.site.urls)),
)

comment:4 by Keryn Knight <keryn@…>, 13 years ago

Unless I'm misreading the code, method based dispatch already exists in one form, implemented in the new Class Based Views (see View in django.views.generic.base).

As such, implementing the same basic functionality elsewhere (even if its a better fit, which the urls configuration may well be) seems counter-intuitive, and against the oft harkened zen of there being only one true way.

comment:5 by Matt Harasymczuk, 13 years ago

Class Based Views View.dsipatch does not provide such clear and intuitive way of defining urls.
And require an application to run view to check if it able to handle a request.
IMHO this way gives you better scalability performance and easy of use.

Look at those sample code (I am thinking about last one), and you know already which view is when executed, without having to see the inner code of any classes and views.
When you look at somebody's else project you are not hoping to read views to see how one url is handled.
When request is POST it looks up in POST urls and quickly omit not fitting GET and DELETE methods.

My function views are full of those ifs:

if request.is_ajax:
  #do stuff
else:
  if request.method == "POST":
     #do something
   else:
     #do else

Hence my true code is almost two indents inside those boilerplate if structure.
Which is always the same... it takes a lot of python beauty and readibility from code.

I know I am able to do

urlpatterns = patterns('myapp',
  url (r'/ajax/user/(?P<username>\d+)', 'views.myview'),
  url (r'/post/user/(?P<username>\d+)', 'views.myview'),
  url (r'/get/user/(?P<username>\d+)', 'views.myview'),
  url (r'/delete/user/(?P<username>\d+)', 'views.myview'),
)

but what with reusability?

We have to change this to something like r'/user/post/...' and r'/user/get/...'
When first part of url determines app the second will have to be a method... and so on.
Every django setup has completely different approach to handle urls. Therefore I think that this standardization I mention in my second post will solve problem, at least will provide a reasonable and logic way do define urls.

Everywhere when I am looking for, people is talking about distinguishing methods inside view in nested ifs... I find it ugly, and necessary. IMHO urls dispatcher (the name dispatcher defines it!) should do this stuff.
Not some kind of inner class based views magic. I think that magic belongs to ruby and RoR, and explicit is the true pythonic way.

BTW. How this dispatch method got inside class based views?
Isn't it suppose to be in some kind of dispatcher in modules? IMHO its logically do not fit to the Class Based Views.

What do you think?

comment:6 by Łukasz Rekucki, 13 years ago

Resolution: duplicate
Status: newclosed

This looks like #2784, but with different syntax. As a side note, django-developers is usually a better place for longer discussions like this one.

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