No Time Dad

A blog about web development written by a busy dad

Tips for Managing Urls in Django

Urls are hard. Especially when your project grows and you end up with a few apps within your project. You can quickly find yourself in a tangled web of urls that can lead to mysterious errors and strange behavior. The examples here are specific to Django, but I think they probably could be applied in various ways to different frameworks too.

>>> import django
>>> django.__version__
'3.1.5'

App Urls vs Project Urls

The Django documentation is a work of art, but either I missed the chapter on application level urls or just didn’t understand it because this was a recent discovery for me. In the past I basically just jammed all of my urls into the project urls.py that is at the same level as settings.py.

The problem with this approach is when you have a few different applications. All of your urls are all jumbled together and it is super easy to create conflicts. Things can really get frustrating when you have a lot of urls because of how Django matches urls. A url for a view could very well resolve but because of the order of the urls in urlpatterns it could be the wrong view entirely.

So my new approach is create an app level urls.py and map the app_name to the project level urls.py. This allows me to separate out my urls so it isn’t such a tangled mess.

An important thing to note here is that Django will not create a urls.py in your app directory when you create a new app via django-admin startapp my_app. You have to create the urls.py yourself. In order to map the app urls.py in the project urls.py you need to include a variable app_name in your app’s urls.py.

# my_project/my_app/urls.py
from django.urls import path

from my_app.views import MyAwesomeListView

app_name = "my_app"
urlpatterns = [
  path(
    "list_all/", 
    MyAwesomeListView.as_view(), 
    name="my_awesome_list_view"),
]
# my_project/urls.py
from django.urls import include


urlpatterns = [
    path('fun_url/', include('my_app.urls')),
    path('admin/', admin.site.urls),
]

Now your views are mapped to fun_url/ in the project and you can visit your view at http://localhost:8000/fun_url/list_all/. Any other urls you add in my_app/urls.py will map to fun_url/.

I so wish I knew about this setup earlier…so much time wasted on mangled urls.

Url Namespacing

This was another one from the docs that I somehow missed or just neglected to see the importance of. Let me stress here- it is super important to name your urls. It will save you so much time and headaches. If you are hardcoding urls in your templates you are doing it wrong.

From the example above, you simply add the name argument to the path method in urls.py. Be sure to choose a name that is mostly unique because it might bite you later.

# my_project/my_app/urls.py
...
path(
    "list_all/", 
    MyAwesomeListView.as_view(), 
    name="my_awesome_list_view"),
...

Here I chose a name that matches the view name. You don’t have to do it this way, but I do think the mostly unique approach to path names is beneficial.

So now, for example, if you wanted to build a link to this view in a template you’d just need the app name and path name you just created.

# my_project/my_app/templates/my_app/my_awsome_list.html
...
<a href="{% url 'my_app:my_awesome_list_view' %}">Awesome List</a>
...

Django would create the html link element with the url http://localhost/fun_url/list_all/.

If for some reason you wanted to change the url path for this list view, say from list_all/ to list_all_items/, you’d only have to change urls.py. Any template, view, or form using the name my_awesome_list_view would automatically update with the new path http://localhost/fun_url/list_all_items/.

I’m sure there are more awesome url tricks I haven’t uncovered yet, but these are a few that have caused me major grief in the past. Django is full of suprises.