Jeff Croft

I’m a product designer in Seattle, WA. I recently worked at Simply Measured, and previously co-founded Lendle.

Some of my past clients include Facebook, Microsoft, Yahoo, and the University of Washington.

I’ve authored two books on web and interactive design and spoken at dozens of conferences around the world.

I’m currently accepting contract work and considering full-time opportunities.

Blog entry // 11.13.2008 // 4:50 PM // 28 Comments

Django URL template tag follow-up

After reading the comments on my last post, about my beefs with Django’s URL template tag, it sounds like I made an assumption that was incorrect: I had understood that the {% url %} tag is now considered a best practice, and using the get_asbsolute_url method in templates is now considered a less-than-best practice.

Turns out, most people agree with me that get_absolute_url is still a very valid method of getting the URL for an object’s detail page for use in templates. Sounds like most people are now doing the same thing I do, which is to use the {% url %} tag when it makes sense (for pages not associated with an object, or for views with few or no arguments), and to use get_absolute_url for object detail pages

As I said, this is what I do, and it generally works fine. But, it still bothers my aesthetic sense. It just doesn’t feel right for a template designer to have to understand the difference between an object detail view and a regular-old view and know when it’s appropriate to use which method for getting the view’s URL.

What’s more, this new “best practice” of combining get_absolute_url and {% url %} is not documented at all. In fact, the documentation definitely suggests a preference for the {% url %}, which I why I initially had the idea that I wasn’t supposed to use get_absolute_url anymore.

But whatever. Given our new understanding of what the best practice is, the workflow for a template designer, who shouldn’t need to know or understand programming, is:

  1. Ignore the documentation’s preference for {% url %} and use what makes sense in a given circumstance.
  2. In order to know what makes sense in a given circumstance, figure out whether the view you want to link to is an object detail view or not.
  3. If it’s an object detail view, use get_absolute_url.
  4. If not, dig through the Python code to find the friendly name your programmer has given to the view you want to link to. You’ll want to look at URL configurations for this. Please note that these URL configurations may be strewn across many apps, including some third-party apps that don’t reside in your project at all. Just read the Python import statements in your base URL configuration and you should be able to find them. Don’t know what a Python import statement or a base URL configuration is? Bummer.
  5. Once you’ve found the friendly name, read the regular expressions in the URL configuration to figure out what arguments need to be passed to the {% url %} tag. It may help to find and read the associated view function. I know you probably don’t know Python or regular expressions, but sometimes life is hard.
  6. Construct your {% url %} tag. Use filters to parse data like dates and times into the format expected by your view function. If the filter you’re looking for doesn’t exist, you may need to write it. Except you can’t, because you don’t know Python. Sorry about that.
  7. Be very careful not to make any typos in your {% url %} tag, or you will almost certainly face a NoReverseMatch exception, which will be nonsense to you and very difficult to debug.
  8. Save your file and refresh the page. If you see an error, ur doin it wrong. Go back to step one and try again.

Does that sound about right? :)

Comments

  1. 001 // Adam E. // 11.13.2008 // 5:15 PM

    Hmm, yeah, I see the point :).

    I have a couple of thoughts. The point of going through this pain is for flexibility, consistency and DRY-ity of URLs in your site. Those things are always important, but maybe not that important for specific sites/apps/teams. If the importance to you is less than the hurdle to get your designers comfortable with the system, then by all means, just do it the “old” way and have the designers put /path/to/foo/{{ something }}/ in your templates. I mean, Django is supposed to be at least somewhat about practicality, right?

    But I think there are better ways. If you find yourself in a situation with designers who would have trouble fully grasping all the steps you’ve outlined above (and that’s no knock on the designers, that shouldn’t really be their main job), then the job falls to the developer to make it simpler. I really think that some combination of sensible naming, documentation, and adding methods/properties on the objects should get you to a place where it shouldn’t be too hard for a designer to figure out what’s needed.

    Honestly though, I’m still working through this for my own stuff. I haven’t settled on anything yet, but currently I’m using get_absolute_url as before, and just striving for some consistent naming in my url patterns. I’m still struggling to decide when it makes sense to add a method/property to an object to generate the URL, and when it makes sense to use {% url %} with arguments.

    What I do like though is that there are a couple of good ways to do it that can be mixed and matched depending on the situation/team. Of course it would be nice if there was One Obvious Way To Do It, but it’s a fairly tricky problem, and I think the current tools are a pretty good solution for now.

  2. 002 // Jeff Croft // 11.13.2008 // 5:42 PM

    I have a couple of thoughts. The point of going through this pain is for flexibility, consistency and DRY-ity of URLs in your site.

    Absolutely. Those things are very important to me.

    I really think that some combination of sensible naming, documentation, and adding methods/properties on the objects should get you to a place where it shouldn’t be too hard for a designer to figure out what’s needed.

    I generally agree with this, but the one thing that doesn’t seem possible to overcome right now is that sometimes you need a method, i.e. {{ entry.get_whatever_url }} and sometimes you need the {% url %} tag. I mean, we’re can’t even be consistent on whether or not a template author should be using variable syntax or tag syntax.

    Of course it would be nice if there was One Obvious Way To Do It, but it’s a fairly tricky problem, and I think the current tools are a pretty good solution for now.

    It’s true. This isn’t a problem of missing functionality or not being able to do something, it’s just a problem of consistency. Sometimes you need a method. Sometimes you need a tag. Sometimes the tag takes arguments. Sometimes it doesn’t. For the situations I work in, where I code this stuff and then designers need to create the templates, this really is a lot for them to grasp — and I’ve got really smart designers.

    It’s certainly not the biggest problem in Djangoland, but it’s one that affects me a lot. :)

  3. 003 // Trey Piepmeier // 11.13.2008 // 6:10 PM

    The Zen of Python:

    There should be one— and preferably only one —obvious way to do it.

  4. 004 // James Bennett // 11.14.2008 // 2:34 AM

    Well, here’s the problem: you can either have something that dynamically looks up the URLs but, as a result, requires some knowledge of how the URLs work, or you can have something that doesn’t require that knowledge but, as a result, can’t look up the URLs dynamically and so requires inflexible hard-coding.

    The documentation definitely has a preference for doing things dynamically, but that doesn’t mean — as far as I can tell, and I’ve both read and written quite a bit of the documentation — that it favors one solution over another.

    Personally, I approach this as a case where both solutions are necessary and useful:

    1. For many common cases {% url %}, coupled with good documentation and good communication between designers and developers, gets the job done. These are the cases where you’re either doing a one-off pattern name plus arguments that only appears in one template, or where you’re dealing with a named pattern that doesn’t take arguments.
    2. For the rest of the cases — namely, those where you’re dealing with a name plus arguments and you’re going to be using it over and over (and hence it’ll be tedious to write out a {% url %} call every time), your friendly local developer can supply you with a shortcut in the form of a method you can hit on an object, decorated with permalink, as often happens with get_absolute_url.

    It’s also worth noting that you can often simplify repetition within a template by using one of the newer features of the {% url %} tag: in the form {% url some_pattern_name arg1=foo,arg2=bar as some_url %}, it sets the variable some_url for reuse.

    So basically, to me, this comes down to developers and designers working together to share information: designers explaining what they need in terms of backend support, and developers explaining what support they can provide and how to use it.

    Also, Trey:

    Special cases aren’t special enough to break the rules.
    Although practicality beats purity.

  5. 005 // Damien // 11.14.2008 // 5:33 AM

    About step 4 and 5, you can get list of view in the project documentation (e.g.: http://jeffcroft.com/admin/doc/v…). The page probably could be more helpful for template authors by showing the friendly url names and an example of url syntax for each view.

  6. 006 // Jeff Croft // 11.14.2008 // 4:04 PM

    So basically, to me, this comes down to developers and designers working together to share information: designers explaining what they need in terms of backend support, and developers explaining what support they can provide and how to use it

    Agreed. But that’s not to say we shouldn’t make it easier, if we can. I’m not sure if we can or not.

    Here’s one suggestion: why not update the Django admin docs such that they show URL names? That alone would go a long way towards not requiring designers and template authors to dig through Python URL confs.

  7. 007 // Jeff Croft // 11.14.2008 // 4:04 PM

    The page probably could be more helpful for template authors by showing the friendly url names and an example of url syntax for each view.

    Hah. You beat me to it. :)

  8. 008 // Kenny Belitzky // 12.09.2008 // 11:37 AM

    before digging into python code to search for urls, you could add this command to the workflow: ./manage.py show_urls

    this command is not included by default but you can find it on the django-command-extensions app

  9. 009 // DannyLK // 12.18.2008 // 8:55 AM

    ??????? ????????. ???????? ??????? ?? ???????, ?? ?????? ?? ????? ???????

  10. 010 // fl // 12.22.2008 // 12:23 PM

    .

  11. 011 // fl // 12.22.2008 // 5:36 PM

    ..

  12. 012 // Praveen // 01.09.2009 // 1:32 AM

    Both can accomplish your goal, but naming the urls is a more robust and in other ways a better solution.

    If you in a week decide that the url should not be /category/… but instead /whatever/… you would have to recode your get_absolute_url method. However, if you use the names in urls, everything would still work after changing the urls. The reason is that when using the name of an url, Django will find what the absolute url will be given the args you use, in your example the id. In a way, you could say that when using named urls, django is both writing and running get_absolute_url for you.

    Another plus when using named urls, is that you can give your urls name that give meaning. That will make it a lot easier to understand what’s going on in the template when you read your own code 6 months from now. Especially if you have several views of one model.

    Say you had a model for blog posts. Then you might want to have an url for your own blogs, another one for your friend’s blogs ect. You could still use get_absolute_url, but this time, it would be harder to make and use, as you now would need to know what url to get. Also when reading the template it would be hard to see which url is being displayed. Instead using tags like {% url friends_blog args=x %} {% url your_blog args=x %} {% url all_blog args=x %} would be easy to understand after not working with the code for a long time.

  13. 013 // Unternehmensberatung Russland // 02.02.2009 // 1:43 PM

    Your blog is very usefull. Many interesting things.

  14. 014 // Skip Hire // 02.03.2009 // 5:57 AM

    I think this is an excellent article. Anyone got any more info about it?

  15. 015 // conficker // 03.31.2009 // 10:28 PM

    Ah, if I would have just read a little more.

  16. 016 // SJL Web Design // 05.03.2009 // 4:31 AM

    excellent article, thanks for the great tips.

  17. 017 // Jake Wedding // 05.11.2009 // 9:40 AM

    Thanks for the help Jeff :)

  18. 018 // Joe Landlords // 05.16.2009 // 2:08 PM

    great advice, thank you so much!

  19. 019 // Seivan Heidari // 05.22.2009 // 6:26 PM

    Well, I personally DESPISE the url-tag. And I have only been doing Django for like a week.

    Coming from Rails, logic was something you kept in the models.

    What if you have to change the urls, which mean that you have to grep through all the damn templates, and change the arguments. When you just could have a blogpost.get_the_fucking_post() method inside your model.

    No, arguments, no looking through the url-confs, no LOUD error-messages. Just do a couple of those, and write them down in a nice neat .txt and hand them over to the designers.

    Can’t be that hard can it? Does anyone think I am wrong?

  20. 020 // ?????? ?????? // 07.28.2009 // 7:04 PM

    ?????????? ???? ????????

  21. 021 // Webdesigner // 09.18.2009 // 2:37 AM

    Hi everybody, nice article thanks a lot. I would like to learn Django and was wondering how difficult this is for someone coming from a Ruby on Rails background. How long will it take me to learn it? And what are Django’s main benefits over Ruby on Rails? Thanks a lot and best wishes from Germany.

  22. 022 // jon // 11.05.2009 // 8:19 AM

    Hey, Is your template usefull for wordpress? Because your temp and wp temp looks the same!

    • wp_list_categories replaces list_cats and wp_list_cats
      • get_author_posts_url replaces the_author_link
      • wp_dropdown_categories replaces dropdown_cats
      • wp_get_archives replaces get_archives
      • wp_list_bookmarks replaces get_bookmarks_list, get_links_list, get_bookmarks, and some other bookmark list template tags.
      • wp_link_pages replaces link_pages
      • the_modified_date (new?)
      • next_post_link and previous_post_link replace previous_post and previous_post (Still verifying)

    nice evening.

  23. 023 // John Garage // 11.14.2009 // 6:44 AM

    Thanks for the help with Django

  24. 024 // green laser pointers // 11.19.2009 // 7:19 AM

    Very nice tips. Thank you very much.

  25. 025 // Wandtattoo // 12.17.2009 // 11:59 PM

    Thanks for your graet help an best regards

  26. 026 // Martin // 01.07.2010 // 10:34 AM

    Thanks for your help. Very nice tips!

  27. 027 // hier // 01.27.2010 // 1:32 PM

    excellent article, thanks for the great tips.

  28. 028 // Marken-Webtipp // 02.09.2010 // 5:11 AM

    Very nice, thanks for sharing this information.

Tags for this entry