Django URL dispatching and request processing
In any web framework when we talk about MVC/MTV things we forget the most important part, which is the router. The router is the part of any web framework where the decision is made about which controller is going to take the responsibility for serving the request.
Before going further in our discussion we should know that in Django, the C in MVC is performed partly by the framework itself and the rest by the Django views. So, we can safely conclude that in Django views are the controllers. If you are concerned about calling these patterns with some strict term then you can call it MTV that is Model Template View. We will have a separate article on Django MVC/MTV with a lot of architectural discussions.
The router/url dispatcher and ROOT_URLCONF
Some call it the router, some call it URL dispatcher—neither is wrong. The router or URL dispatcher is contained within the Django framework and it consults the URLConfs for routing a request to a proper view callable. To help the URL dispatcher decide, we have to provide the framework with the information of which view it should call according to the URL requested with. To help the URL dispatcher we provide a setting in the settings.py called ROOT_URLCONF. Django finds and loads the module indicated by ROOT_URLCONF', then it finds a variable called urlpatterns` inside the module.
urlpatterns is a list/iterable of a url() instance. Actually url() is a function that returns an instance of RegexURLPattern. Instead of calling that instance of RegexURLPattern class many people use url() instance. It's just a difference in using the terms—don’t take this difference in choice of terms as technically wrong.
Note: Remember we call the list/iterable of RegexURLPattern URLConfs.
url() and RegexURLPattern
urlpatterns is the list where all the rules are written about how a view should be resolved and how the request should be dispatched to the view with what parameters. As mentioned previously url() returns a RegexURLPattern` object.
We write the rules with regular expressions. The first parameter of url() is a regular expression. Here is an example:
# urls.py
from django.conf.urls import url
from django.http import HttpResponse
# defining a view function here only for demonstration purpose.
def home_view(request):
return HttpResponse("Hi, there! This is the home view")
urlpatterns = [
url(r'^home$', home_view)
]
So, if your domain name is example.com and if you want to visit example.com/home you will see Hi, there! This is the home view in your browser.
Regular expression is a very powerful weapon; you can use it, you can misuse it. So use it with caution. Let's say you have the urlpatterns as the following:
urlpatterns = [
url(r'^home', home_view),
url(r'^homemade', homemade_view)
]
Never in the lifetime of your web application will the homemade_view be reached. Look at the first regex pattern. It says that any URL that starts with home will be served by the home_view view callable. Now, look at the second pattern—it starts with home. As home was matched by the first pattern the second pattern will never be reached, touched, used, or executed ever.
Look at the URL example.com/home and look at the pattern ^home. Did you notice that the pattern is missing a forward slash /? You do not need to worry about the path separator at the start of your url patterns. Even you do not need to care about that when the url pattern is from a nested urlconf.
Nested URLConfs
No matter how untidy and unsmart of a person you are, Django will try its best to make you smarter, tidier, and better. Without making your scripts dirty by using long list of URL patterns you can divide them in modules or apps and keep your business logic clean and separated.
Remember what we did in our first introductory Django application. We included the URL module from aap1. Let's flashback.
The root urlpatterns
urlpatterns = [
url(r'^', include(urls))
]
urlpatterns inside app1
urlpatterns = [
url(r'^hello$', hello)
]
See, we could put all our patterns and link the views inside the root urls.py but we did not do so. Instead we delegated the responsibility to different modules. Separation of concern is a best practice in software engineering.
Let's make our problem bigger and try to keep tidy. The root urlpatterns
urlpatterns = [
url(r'^$', home_view),
url(r'^404-info$', _404_view),
url(r'^external-redirect$', external_redirect_view),
url(r'^app1', include(app1.urls)),
url(r'^app2', include(app2.urls)),
url(r'^app3', include(app3.urls)),
url(r'^app4', include(app4.urls))
]
In the root urlconf we have directly linked three URL patterns with their views. The rest are included views. The included views URLs are nested URLs. Let's expand the app1's urlpatterns.
urlpatterns = [
url(r'^hello$', hello),
url(r'^hello-template$', hello_template)
]
Let's assume your application's domain name is example.com. Now let's take a look at the url to view mapping.
- example.com maps to home_view view callable
- example.com/404-info maps to _404_view view callable
- example.com/external-redirect maps to external_redirect_view view callable
- example.com/hello maps to app1's hello view callable
- example.com/hello-template maps to app1's hello_template view callable
Notice that in app1's patterns we never prepended /. No matter whether your urlpatters are nested or root, you should think every url pattern as independent—Django will take care of the rest.
Views with parameters and URL patterns
In this article we are talking about function based views for simplicity. I mentioned that a view need not be exactly a function, nor a class; it needs to be a callable object, no matter what that is. Again, in explaining parameters I am going to take the simplest path. I will explain about different types of views in details in a dedicated article.
There are two types of parameters
- Positional parameter
- Keyword or optional parameter
Positional Parameter
Let's say you have an ID for each article published on your website. For example example.com/articles/1. The last part is the ID of the article. But this ID changes for each individual article. You can write the pattern as follows.
url(r'^articles/([0-9]+)', article_details_view)
[0-9]+ means any sequence of digits. () indicates a group in regular expression. We provide positional arguments by means of groups. Let's define a view to clarify our concept.
from django.http import HttpResponse
def article_details_view(req, article_id):
txt = "You have requested article with id: %s" % article_id
return HttpResponse(txt)
Go to the url example.com/articles/1 and you will see the following text on your browser.
You have requested article with id: 1"
Go to the url example.com/articles/5 and you will see the following text on your browser.
You have requested article with id: 5"
That indicates that our application is working properly. Remember that we can provide more than one positional parameter.
Optional/Keyword parameter
Optional parameters are like keyword parameter in Python functions. It is not mandatory and if not given then some default is provided by us. We can also ignore them with **kwargs. Let's say we have user profiles in our website and each user has one unique username. So, a URL can look like this example.com/profiles/userX
This time let's define the view first.
from django.http import HttpResponse
def user_profile_view(req, username):
txt = "Your username is: %s" % username
return HttpResponse(txt)
And your URL looks like
url(r'^profiles/(?P<username>[a-zA-Z]+)', user_profile_view)
In regular expression (?P<>) indicates a named group. The group name is placed within open and closed angle brackets.
We will see more examples in future articles. In the meantime keep practicing with the code written in this article and also study more on regular expressions.
*Stop by the homepage to search and compare SDKs, Dev Tools, and Libraries.
Recent Stories
Top DiscoverSDK Experts
Compare Products
Select up to three two products to compare by clicking on the compare icon () of each product.
{{compareToolModel.Error}}
{{CommentsModel.TotalCount}} Comments
Your Comment