Opened 4 years ago

Closed 4 years ago

#15645 closed (duplicate)

HTTP methods in urls.py

Reported by: haras 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: UI/UX:

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 Changed 4 years ago by julien

  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset
  • Triage Stage changed from Unreviewed to Design 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 Changed 4 years ago by haras

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 4 years ago by haras (previous) (diff)

comment:3 Changed 4 years ago by haras

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 Changed 4 years ago by Keryn Knight <keryn@…>

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 Changed 4 years ago by haras

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 Changed 4 years ago by lrekucki

  • Resolution set to duplicate
  • Status changed from new to closed

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