This article is about Django's View and URL components.
When an HTTP request is ingested by CGI, WSGI (Web Server Gateway Interface), Django's URL pattern matcher routes the request to a designated view. The role of a view is to process the request and generate an appropriate response.
The role of View process the request into a response. In the process, View interacts with Model and Template typically. A view function takes a Python request object, which is parsed from the raw HTTP bytes received from WSGI. It facilitates access to data from databases, media assets, or other services, and ultimately returns an HTTP response.
There can be multiple urls.py in a project. By default, the root urls.py is located at ${project}/urls.py. This project-level urls.py dispatches requests to other app-specific urls.py files or directly to view functions that accept the request object as the first parameter.
# Example project structure:
project/
├── urls.py # Root URL configuration
├── app_a/
│ └── urls.py # App A's URL configuration
└── app_b/
└── urls.py # App B's URL configuration
The first lesson about HTTP is the URL. The URL is structured as follows:
{scheme}://{origin}}/{URI}?{query_parameters}
By the way, GET request does not have a body. Therefore, the query string is the only way to pass data to the server. While a POST request has a body, so the query string is not necessary. For system mutating requests such as submitting object creation forms, POST is used. It is the programmer's duty to handle methods of HTTP request to different tasks.
The urls.py file is responsible for mapping URL formats to corresponding views. It serves as a routing mechanism, directing incoming requests to the appropriate view based on the URI.
Creating an urls.py for each app is the best way to make Django apps reusable. Example of urls.py in an app:
# app_a/urls.py:
from django.urls import path
from . import views
app_name = 'app_a' # Namespace
urlpatterns = [
path('view/', views.view_name, name='view_name'),
path('another-view/', views.cbv.as_views(), name='another_view'),
path('sub-app/', include('app_a.sub_app.urls')),
]
The path lives in django.urls.path, and a few key objects under "django.urls" are:
django.urls.path(), map urlPath:str to view callable. An example of a use case: routing a request to a view functiondjango.urls.reverse(), map urlname:str to relativePath:str. An example of a use case: generating a visiting URL for a model datadjango.urls.include(), include apps' URLs patterns, with a nickname namespacingdjango.urls.resolve(), map urlPath:str to urlname:str. It is the reverse of the "reverse" function.Namespaces are essential for organizing URL patterns, especially when multiple apps are involved. It is used in templates to avoid naming conflicts:
<a href="{% url 'app_a:view_name' %}">Link to App A View</a>
This way, one can ensure that the correct view is referenced, even if there are other views with the same name in different apps.
There are two types of Views, function-based view FBV and class-based view CBV.
Function based view is a simple function that takes a request and returns a response.
Class-based view is a class that inherits from Django's View class and has a method get and
post to handle simple scenarios. To use CBV well, one may want to check CBV's callbacks (dispatch, get_context_data) which can be found in the base CBV classes. CBV is more complicated and there is little discussion of the callback order. One may better log the function call to understand the flow.
from django.http import JsonResponse
def my_view(request):
data = {"message": "Hello, World!"}
return JsonResponse(data)
from django.views import View
from django.http import JsonResponse
class MyView(View):
def get(self, request):
data = {"message": "Hello, World!"}
return JsonResponse(data)
def post(self, request):
# Handle POST request
pass
For simple testing, FBV is more convenient. For example, a simple view that returns a JSON response. However, it would be more convenient to use CBV for composing/group Views that have similar behaviors. Code reuse makes the project more readable and maintainable. It is the OOP concept: composition. Another example is the Model inheritance.
CBVs allow for better organization of views that share similar behaviors through inheritance and mixins. Logging function calls can help understand the flow in more complex CBVs.
There is also a set of built-in mixings. Mixings are classes with some methods that can be shared
but are not intended to be used alone or in the base class. For example, LoginRequiredMixin, PermissionRequiredMinx are Django built-in mixins.
CBV with mixins covers additional functionalities, such as authentication, permissions, and more. These mixins can be combined to create powerful views that maintain clean and readable code.
Example of CBV with mixins:
class CBView(SomeMixins,View):
def dispatch(self)
pass
Besides the official doc guide, a quick overview of classed-based view members is found at Classy Class-Based Views website
In Django, the Context is a crucial component used for composing HTML content that will be returned as an HTTP response. When rendering a template, the context acts as a bridge between the data from your views (often from models) and the presentation logic defined in the templates.
from django.shortcuts import render
def my_view(request):
context = {
'title': 'My Page Title',
'items': ['Item 1', 'Item 2', 'Item 3'],
}
return render(request, 'my_template.html', context)
And in HTML:
<h1>{{ title }}</h1>
<ul>
{% for item in items %}
<li>{{ item }}</li>
{% endfor %}
</ul>
Django's templating engine processes the HTML file and replaces the variables defined in the context with their corresponding values. This mechanism allows for the dynamic generation of HTML, making it possible to create responsive and interactive web applications.
While pagination is often associated with the concept of QuerySet in Django's ORM, it is also a common feature utilized within views. Pagination helps manage large datasets by breaking them into smaller, manageable chunks, allowing users to navigate through data without overwhelming them with information in a single HTTP response.
Django provides a built-in Paginator which borrows from the Functional Programming concept. Paginator acts on a QuerySet and returns a Page object. The Page object can be used in the template to render the pagination. It is like a generator in Python and evaluates the QuerySet lazily.
This means that items are not fetched from the database until they are actually needed, optimizing performance and reducing memory usage. The Page object behaves similarly to a generator in Python, enabling efficient handling of large datasets.
from django.core.paginator import Paginator
from django.shortcuts import render
from .models import Item
def item_list(request):
item_list = Item.objects.all() # Get all items
paginator = Paginator(item_list, 10) # Show 10 items per page
page_number = request.GET.get('page') # Get the page number from the query string
page_obj = paginator.get_page(page_number) # Get the corresponding Page object
return render(request, 'item_list.html', {'page_obj': page_obj})
The HTTP request object is a dictionary-like object that contains all the information about the incoming request. It is passed to the view as the first parameter. You can access various attributes such as:
Full Documentation about Request and Response is found here
Request and Response are the cargo/envelope of the HTTP communication. The request object is a dictionary-like object that contains all the information about the request. It is passed to the view as the first parameter.
Django provides different response classes to handle various types of responses:
HttpResponse: The most basic response class. One can return plain text, HTML content, or any other type of content.
from django.http import HttpResponse
def my_view(request):
return HttpResponse("<h1>Hello, World!</h1>")
from django.http import JsonResponse
def my_json_view(request):
data = {"message": "Hello, JSON!"}
return JsonResponse(data)
By leveraging Django's powerful view system and URL routing, developers can create dynamic web applications that respond effectively to user interactions.