List Info

Thread: @url tag - getting rid of urlpatterns




@url tag - getting rid of urlpatterns
country flaguser name
United States
2007-08-29 11:54:23
I've been using django for almost a year and I was always
frustrated
by its cumbersome urlpatterns system. While it is really
flexible, it
doesn't provide any shortcuts for widely-used url and views
naming
schemes.

Let me show in examples what I mean.

As everyone, I started with the tutorial and worked with the
following
system:

=== urls.py ===
urlpatterns = patterns('',
	(r'^$', 'apps.app1.views.index'),
	(r'^news/$', 'apps.app1.views.news'),
	(r'^members/profile/$',
'apps.app1.views.members.profile'),
	(r'^members/secure/$', 'apps.app1.views.members.secure'),
	(r'^app2/page/$', 'apps.app2.views.page'), # some other
app
	...
)

It becomes quite a large and hardy maintainable list when
the number
of applications and views grows. I do believe in DRY
principle and I
didn't like much that I had to repeat the URL bases and
parent module
names again and again. So I kept reading the docs and
involved the
system to the following:

=== urls.py ===
urlpatterns = patterns('',
	(r'^', include('apps.app1.urls')),
	(r'^app2', include('apps.app2.urls')),
)

=== apps/app1/urls/__init__.py ===
urlpatterns = patterns('apps.app1.views',
	(r'^index/$', 'index'),
	(r'^news/$', 'news'),
	(r'^members', include('apps.app1.urls.members'),
)

=== apps/app1/urls/members.py ===
urlpatterns = patterns('apps.app1.views.members',
	(r'^profile/$', 'profile'),
	(r'^secure/$', 'secure'),
)

=== apps/app1/views/__init__.py ===
def index(request):
	...
def news(request):
	...

=== apps/app1/views/members.py ===
def profile(request):
	....
def secure(request):
	...

(I skipped app2.* files for easier reading)

While this system had less redundancy and easier to maintain
(the DRY
benefits), it suffered from another DRY problem - there were
two
packages with the same structure (apps.app1.urls.* and
apps.app1.views.*), with the highly related content. When I
was
renaming a view, I had to browse through two package
structures and
change the things twice. Still frustrating, you see.

So what I decided, why do we have to keep urlpatterns apart
of the
views? Why wouldn't I put them in the same files? The
architecture
became:

=== urls.py ===
urlpatterns = patterns('',
	(r'^', include('apps.app1.views')), # note views, not urls
	(r'^app2', include('apps.app2.views')),
)

=== apps/app1/views/__init__.py ===
urlpatterns = patterns(__name__, # sic!
	(r'^index/$', 'index'),
	(r'^news/$', 'news'),
	(r'^members', include('apps.app1.views.members'),
)

def index(request):
	...
def news(request):
	...

=== apps/app1/views/members.py ===
urlpatterns = patterns(__name__,
	(r'^profile/$', 'profile'),
	(r'^secure/$', 'secure'),
)

def profile(request):
	....
def secure(request):
	...

This was a good change. I had 50% less files, and I put the
related
info within the same modules. Also note the usage of
__name__, which
increased the DRY factor a bit more 

Yet I wasn't fully satisfied. Whenever I renamed a view I
had to patch
the urlpatterns as well.
I also remembered an old inconvenience I always felt with
views
modules. If I place views functions in a views module, and
place like
"normal" helper functions there as well, they got
mixed. By looking at
the code it is sometimes hard to understand which function
is a view,
and which function is a helper.
So what I created is the url decorator which solved
the both
problems:

=== urls.py ===
urlpatterns = patterns('',
	(r'^', include('apps.app1.views')),
	(r'^app2', include('apps.app2.views')),
)

=== apps/app1/views/__init__.py ===
url(r'^index/$')
def index(request):
	...

url(r'^news/$')
def news(request):
	...

urlpatterns += include_urlpatterns(r'^members',
'apps.app1.views.members')

=== apps/app1/views/members.py ===
url(r'^profile/$)
def profile(request):
	....

url(r'^secure/$)
def secure(request):
	...

url(r'^path1/$', '^path2/$') # you can specify
several patterns
def multipath_view(request):
	...

def helper(): # easily distinguishable - no url!
	...

Summarizing, the benefits are:
- no more creating and supporting urlpattern maps (less
files, less
code, more DRY)
- have the url associated with a view in-place
- easily see if a function is a view
- fully compatible with other chained decorators

Implementation problems, or possible improvements:
- it is hackish
- the speed isn't constant time, it is O(N) where N is the
number of
currently loaded modules
- I would like to make it support the no-arguments syntax:

url()
def profile(request):
	....

and have the decorator automatically pull the function name
as the url
pattern, but that doesn't seem possible if there are
further
decorators (like user_passes_test or render_to)

The source code can be found at http://ww
w.djangosnippets.org/snippets/395/


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the
Google Groups "Django users" group.
To post to this group, send email to django-usersgooglegroups.com
To unsubscribe from this group, send email to
django-users-unsubscribegooglegroups.com
For more options, visit this group at htt
p://groups.google.com/group/django-users?hl=en
-~----------~----~----~----~------~----~------~--~---


Re: @url tag - getting rid of urlpatterns
country flaguser name
Russian Federation
2007-08-30 01:02:20
Ilya Semenov wrote:
> === apps/app1/views/__init__.py ===
> url(r'^index/$')
> def index(request):
> 	...
> 
> url(r'^news/$')
> def news(request):

While the decorator looks nice it creates in my opinion at
least as many 
problems as it solves.

1. You can apply decorators only to custom views, not to
generic views. 
And as generic views are commonly used one still should keep
a separate 
urls.py for them.

2. One of the best things (again, in my opinion) of urls.py
is that it 
shows whole user interface of an app in one place. You loose
this 
feature with decorators scattered all over views.

BTW, do you know that you can use function objects instead
of their 
names in url definition? So this:

     urlpatterns = patterns(__name__,
         (r'^index/$', 'index'),

becomes just:

     urlpatterns = patterns('',
         (r'^index/$', index),

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the
Google Groups "Django users" group.
To post to this group, send email to django-usersgooglegroups.com
To unsubscribe from this group, send email to
django-users-unsubscribegooglegroups.com
For more options, visit this group at htt
p://groups.google.com/group/django-users?hl=en
-~----------~----~----~----~------~----~------~--~---


Re: @url tag - getting rid of urlpatterns
country flaguser name
United States
2007-08-30 03:08:19
Ivan,

Thanks for reviewing the snippet.

> While the decorator looks nice it creates in my opinion
at least as many
> problems as it solves.
>
> 1. You can apply decorators only to custom views, not
to generic views.
> And as generic views are commonly used one still should
keep a separate
> urls.py for them.

First of all, the decorator is fully optional. It doesn't
replace
urlpatterns, it gently appends to them. There's nothing
wrong in
writing:

urlpatterns = urlpatterns('',
    ('^news/$', 'django.views.generic.object_list',
{'queryset':......})
)

url('^edit_news/$')
def edit_news(request):
    ....

Second, I think the use of generic views is over-estimated.
Generic
views do not even support access restrictions (user_passes_test) and
thus are most of the time proxied via custom one-line
views.

> 2. One of the best things (again, in my opinion) of
urls.py is that it
> shows whole user interface of an app in one place. You
loose this
> feature with decorators scattered all over views.

While I see the rationale in your words, that position is
very
arguable.

Encapsulation is one of the greatest programming principles.
From the
architectural point of view, the app-level urls.py shouldn't
bother
what members area urls are there in members module, since it
can
safely delegate the responsibility to manage the list of
members urls
and just pull the data when needed.

> BTW, do you know that you can use function objects
instead of their
> names in url definition? So this:
>
>      urlpatterns = patterns(__name__,
>          (r'^index/$', 'index'),
>
> becomes just:
>
>      urlpatterns = patterns('',
>          (r'^index/$', index),

Sure, that way it is more natural. Alas, that won't work,
since index
function is defined later in the module code. Alternatively,
one
should put urlpatterns to the very end of the module file,
which just
doesn't feel right to me.


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the
Google Groups "Django users" group.
To post to this group, send email to django-usersgooglegroups.com
To unsubscribe from this group, send email to
django-users-unsubscribegooglegroups.com
For more options, visit this group at htt
p://groups.google.com/group/django-users?hl=en
-~----------~----~----~----~------~----~------~--~---


Re: @url tag - getting rid of urlpatterns
country flaguser name
United States
2007-08-30 03:55:19
On 30 ΑΧΗ, 12:08, Ilya Semenov <seme...inetss.com> wrote:

> Second, I think the use of generic views is
over-estimated. Generic
> views do not even support access restrictions (user_passes_test) and
> thus are most of the time proxied via custom one-line
views.
Look at this:
http://www.d
jangoproject.com/documentation/url_dispatch/#passing-callabl
e-objects-instead-of-strings
You can wrap even generic views with decorator


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the
Google Groups "Django users" group.
To post to this group, send email to django-usersgooglegroups.com
To unsubscribe from this group, send email to
django-users-unsubscribegooglegroups.com
For more options, visit this group at htt
p://groups.google.com/group/django-users?hl=en
-~----------~----~----~----~------~----~------~--~---


Re: @url tag - getting rid of urlpatterns
country flaguser name
Russian Federation
2007-08-30 03:59:46
Ilya Semenov wrote:
> Second, I think the use of generic views is
over-estimated. Generic
> views do not even support access restrictions (user_passes_test) and
> thus are most of the time proxied via custom one-line
views.

Actually they support decoration perfectly well, right in
urls.py:

     from django.views.generic.list_detail import
object_detail
     from some_decorators import decorator

     urlpatterns = patterns('',
         (r'...', decorator(object_detail), { ... }),
     )

Though I agree that generic views require some time get used
to...

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the
Google Groups "Django users" group.
To post to this group, send email to django-usersgooglegroups.com
To unsubscribe from this group, send email to
django-users-unsubscribegooglegroups.com
For more options, visit this group at htt
p://groups.google.com/group/django-users?hl=en
-~----------~----~----~----~------~----~------~--~---


Re: @url tag - getting rid of urlpatterns
country flaguser name
United States
2007-08-30 16:08:59
On Aug 30, 3:08 am, Ilya Semenov <seme...inetss.com> wrote:
> Second, I think the use of generic views is
over-estimated.

I tend to agree a bit here.  I like having all my URLs in
one file and
all my views in another.  Using generic views usually
clutters the
urls file too much for me.  Generic views usually don't save
enough
code to be worth it in my experience, it just moves the code
to a
different place.  Also, if I ever wanted to change the view
up to do
more custom things, I would have to undue the generic view
setup and
create a regular view anyway, so why not just do that in the
first
place.

> > 2. One of the best things (again, in my opinion)
of urls.py is that it
> > shows whole user interface of an app in one place.
You loose this
> > feature with decorators scattered all over views.

Yes, I think this is a very nice thing about having a file
dedicated
to url conf.  It give you a nice summary of what the
application does
and how it's organized.  I would not like having to scan
through all
my views to figure out the URL structure.

> While I see the rationale in your words, that position
is very
> arguable.
>
> Encapsulation is one of the greatest programming
principles. From the
> architectural point of view, the app-level urls.py
shouldn't bother
> what members area urls are there in members module,
since it can
> safely delegate the responsibility to manage the list
of members urls
> and just pull the data when needed.

Isn't this what include() is for:
(r'^members/', include('myapp.members'))

All the members URLs can be encapsulated in the members
module.

Gary


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the
Google Groups "Django users" group.
To post to this group, send email to django-usersgooglegroups.com
To unsubscribe from this group, send email to
django-users-unsubscribegooglegroups.com
For more options, visit this group at htt
p://groups.google.com/group/django-users?hl=en
-~----------~----~----~----~------~----~------~--~---


[1-6]

about | contact  Other archives ( Real Estate discussion Medical topics )