Skip to content

Instantly share code, notes, and snippets.

@dg
Created June 5, 2011 19:29
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save dg/1009307 to your computer and use it in GitHub Desktop.
Save dg/1009307 to your computer and use it in GitHub Desktop.
Routing in Django verus Nette Framework

DJANGO

In urls.py

# urls like "articles/2011/tutorial03" or "articles/2011/tutorial03.html" or "articles/2011/tutorial03.htm"

urlpatterns = patterns('',
    (r'articles/(?P<year>\d+)/(?P<item>[^/]+)(?:\.htm(?:l)?)?/?\$', 'articles.detail'),
)

In template:

<p><a href="{% url articles.views.detail article.year article.id %}">The Article</a></p>
  • Regular expression is hard to read.
  • Any change of URL means to change some templates.
  • Redirect to the prefered URL (e.g. articles/2011/tutorial03.htm -> articles/2011/tutorial03) must developer provide itself.

source: https://docs.djangoproject.com/en/1.3/intro/tutorial03/

Nette Framework

In bootstrap.php

$router[] = new Route('articles/<year \d+>/<item>[.htm[l]]', 'Articles:detail');

In template:

<p><a n:href="Articles:detail $article->year, $article->id">The Article</a></p>
  • Route mask is easy to read.
  • Any change of URL means to change one line in bootstrap.php.
  • Redirect to the prefered URL (e.g. articles/2011/tutorial03.htm -> articles/2011/tutorial03) is done automatically.
@ondrejmirtes
Copy link

Django also supports reverse URLs constructing: https://docs.djangoproject.com/en/dev/ref/templates/builtins/#std:templatetag-url

In template:

{% url articles.detail year=article.year id=article.id %}

The ugly route pattern is because it's a native way to write named regexp in Python.

@ondrejmirtes
Copy link

Django is in fact really close to Nette (block inheritance in templates, forms definition and rendering and URL routing), but additionally it has built-in ORM with administration generating, generic views (you don't have to write single line of controller code for typical tasks like list view of objects, detail object view or monthly/yearly-based archive) and a big ecosystem of libraries.

@dg
Copy link
Author

dg commented Jun 5, 2011

Ah, I did not know about the {% url %}. It is interesting that official tutorial hid this information.

@jkbrzt
Copy link

jkbrzt commented Jun 5, 2011

The tutorial covers only the basics, a much better place to learn more about URLs in Django is the documentation:
https://docs.djangoproject.com/en/dev/topics/http/urls/

A couple of useful features:

URLs can be named and referred to by a name:

# project.eshop.urls
urlpatterns = ('',
    url('^products$', views.product_list, name='product_list'),
    url('^products/(.+)$', views.product_detail, name='product_detail'),
)

urlpatterns can also be included and 'mounted' anywhere :

# project.urls
urlpatterns('',
   url(r'^eshop/', include('project.eshop.urls'))
)

Now, to get the URL of a product:

>>> from django.core.urlresolvers import reverse
>>> print reverse('product_detail', args=[product.id])
/eshop/products/5

Also: name-spacing, django.db.models.permalink, etc.

@honzakral
Copy link

Why would anyone have two URL schemas? wth .html and without? You could always
write a middleware in Django that would handle the redirects for you, 5 lines
of code to apply it globally but I see no point in such functionality
whatsoever.
Django has a bultin middleware (enabled by default though customizable via
settings) that would dynamically append slash ('/') to any URL without trailing
slash that returns 404.

For me these systems are perfectly equivalent in the features compared it just
comes down to personal preference (having normal ugly regexps or custom more
readable notation) for parameter definitions.

@dg
Copy link
Author

dg commented Jun 5, 2011

Why would anyone have two URL schemas?

This is a common case. When I redesign website, I have an old URLs and the new URLs. I must keep the old URLs and redirect them to the new ones (SEO). In this example the old URL is "articles/2011/tutorial03.html" and the new is "articles/2011/tutorial03". So it is not about ".html" suffix, this is about redirecting various URLs pointing to the same page.

@honzakral
Copy link

Oh I see. I have actually never seen a redesign where the only change was .html suffix.

Django provides a pluggable application (django.contrib.redirects) that enables you to define any arbitrary redirects in the DB. For a case like this (redesign and new URL schema) we always wrote a simple midlleware that would for example extract the article_id from GET params, get it from the DB and redirect the user to the article's proper URL. For instance:

http://www.example.com/clanek.phtml?id=123 became http://example.com/2005/12/10/articles/whats-new/

@dg
Copy link
Author

dg commented Jun 5, 2011

Suffix was just example, so use your example.

$router[] = new Route('<year>/<month>/<day>/articles/<slug>', 'Articles:detail');
$router[] = new Route('clanek.phtml', 'Articles:detail');

Nette Framework automatically redirects to the first URL, if there are more routes pointing to the same action. No more lines of code or configuration is required. Do we understand each other?

@honzakral
Copy link

Ahhh, nice. Now I understand, thanks. That's a nifty feature. In Django one would have to declare this explicitly, still one line but much uglier:

url('^some/(params)/', 'view'),
url('^other/(param)/', lambda r, p: redirect('view', p)),

@honzajavorek
Copy link

I am not a Django expert, but when I first tried to move from Nette to Django, I really missed the canonical URL routing. I love this feature and Django's routing seems to be everything in the world but canonical. In fact, you can use (and in documentation you are encouraged to use) two or more different representations of URL. That makes me confused and unsure. Moreover, is there an easy way to run Django app at a different location than server root (e.g. subfolder)?

@Almad
Copy link

Almad commented Jun 14, 2011

Littlemaple: Well, "canonical" is IMO usually Model.get_absolute_url variant and non-canonical URIs are domain-specific.

You can run application in non-root uri using url('prefix', include('app.url')), althrough I don't see any relevance to this discussion ;)

BTW, you are not required to use named regexp variant (althrough I'd trade flexibility over readability in this case), and

(r'articles/(\d+)/(.+)/$', 'articles.detail'),

is imho fairly readable.

@honzajavorek
Copy link

@Almad, canonical for me means that route/url has only one single representation in my app. There are at least three in Django, which are mixing everywhere (with friends called resolve and reverse): /some/path, named_route, and project_package.app.views.gargamel. There is only one single representation of route/url in Nette: Presenter:action. Moreover, I don't like the way Django handles GET params and makes difference between params in your regular expression and extra params in querystring. This is very confusing for me, I like clear approach, single representation, single explanation.

@Almad
Copy link

Almad commented Jun 14, 2011

@littlemaple: Oh, I thought you were thinking abotu canonical URI.

I agree there should be one preferred way, this is why I am using only reverse() with named routes, althrough I can view others as "aliases", in the same way there should be one canonical URI, but content can be displayed under variety of non-canonical ones.

Rest is nette vs. django flame (OK, some PHP vs. Python flame included) I will not dwell into, as I don't like either of those much; however, I'd say that while working with GET params is not very comfortable in Django, I agree with the distinction between parameters and application routing. GET should be really parameters and not resource identificators...and also because of funny http caching mess.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment