Basics of Django: Model-View-Template (MVT) Architecture
What makes Django so good at creating quick, efficient web applications? And why should I add it to my repertoire as a software developer?
What exactly is Django?
Django is ‘a high-level Python web framework that encourages rapid development and clean, pragmatic design.’¹ This framework is particularly well known for it’s emphasis on scalability, it’s strong and active community, and a focus on the core principle of keeping your code ‘DRY’ (read more about staying ‘DRY’ here).
The framework itself was released as an open-source project in the summer of 2005 and has since become one of the most popular Python web frameworks available today.
If you are already a Python user simply run a pip install of Django in your respective terminal/ubuntu interface. If not, you may be a bit lost, start here before diving into Django.
Model-View-Template (MVT) Architecture
Model-View-Template architecture, otherwise known as MVT, is a software design pattern within Django that utilizes:
- Models to handle data logic and structure of your database
- Views to handle the applications logic and functionality
- Templates to handle the layout and structure of the user facing application
Django is able to separate responsibilities in the code, which results in a well-organized and maintainable structure for building web applications, particularly those reliant on large data sets. I will go further into each entity later on in this reading, but for now, it is good to understand that the MVT architecture is the foundation on which Django is so successful.
Object Relational Mapping (ORM) in Django
Object Relational Mapping (ORM) is, in simple terms, a technique used to convert database records into objects within an object-oriented language, such as Python. ORM allows developers to avoid the need to write out specific SQL queries directly, leading to simplified database interactions and cleaner, more concise code. In Django, one of the most important features is it’s included ORM that works through Django’s pre-built model class.
Models
In Django, each database table is represented by a Python class called a model. The class attributes of these models represent the table columns or fields within the database table while each particular instance of a model represents the respective row within the database table.
Seems pretty straightforward, and that is quite literally the point. Django’s internal ORM is meant to make our lives as developers simpler by providing pre-built, simple entities to aid use in traversing our databases.
# import the models class from Django
from django.db import models
# creating a model class below
class Movie(models.Model):
title = models.CharField(max_length=25)
director = models.CharField(max_length=25)
release_date = models.DateField()
def __str__(self):
return self.title
When creating a model within Django, you define it by creating a Python class that subclasses models.Model. This signifies that the class we have just created, ‘Movie’, is a Django model and is now able to use all of Django’s built-in ORM functionalities.
But where is my __init__ method?
The __init__ method, your likely standard practice when building a Python class, is actually discouraged when working in Django. Django’s ORM has actually taken care of this by handling the initialization and instantiation of model classes internally. As an alternative to the __init__ method, Django docs encourage developers to use field definitions directly within the class.
# field definition examples
title = models.CharField(max_length=25)
director = models.CharField(max_length=25)
release_date = models.DateField()
Field Definitions
Field definitions are essentially Python attributes within a Django model class. These definitions are used to define the columns and the specific data they can hold within our database table. Django provides various field data types to map to their respective columns, such as CharField (string data field) and DateField (date data field) from above.
Note: With CharField, although not required, it is always good practice to set a character limit in the form of a max_length=(integer). This helps maintain consistency, compatibility, and performance for our database.
from django.db import models
# relational table example
class Director (models.Model):
name = models.CharField(max_length=15)
def __str__(self):
return self.name
class Movie (models.Model):
title = models.CharField(max_length=25)
# a foreign key field definition below
director = models.ForeignKey('Director', on_delete=models.CASCADE)
release_date = models.DateField()
def __str__(self):
return self.title
Above I have created a simple example of how relational tables can be represented in Django with the use of the ForeignKey field definition. This allows developers to tie one database to another through the use of a foreign key, the primary key of one table set within a specific field of another table.
This can be seen within the Movie model class in the line:
director = models.ForeignKey('Director', on_delete=models.CASCADE)
Within this field definition we have passed two arguments at this time:
- ‘Director’ which specifies the related model (database table) to utilize as the foreign key. This should be in string format in order to avoid any import conflicts.
- on_delete=models.CASCADE this specifies what happens when the particular instance of the Director class is deleted. In the case of this specific line, the ‘CASCADE’ means that if a particular director instance is deleted, ALL related movie instances will also be deleted.
There are many more field definitions and built-in functionalities within the Django library, feel free to explore the Django documentation to dive even deeper.
Views
A view is integral to the MVT architecture of Django as it works as the intermediary source between the models discussed earlier and the templates we will discuss next. A view, in Python terms, is a function that takes a request, being called, and returns are particular response. These views are technically part of the UI of an application as they are responsible for rendering the templates into your browser, after performing their associated tasks.
from django.shortcuts import render
from .models import Movie
def movie_list(request):
# my view request
movies = Movie.objects.all()
# render to the specific template
return render(request, 'movies/movie_list.html', {'movies': movies})
In the above view example, we first import the Movie model and the Django render function, which is used to render HTML templates with context data. Our ‘movie_list’ view takes in an argument of request, which is an HTTPRequest object within the Django framework. It represents the request sent by the client (user) to the server and contains all metadata/data related to this request.
The view functionality then queries the database to retrieve all instances of the Movie model (rows in the Movie table). As a return, our view renders a template with our data and returns an HTTP response.
- The first argument is the incoming HTTP request from the client.
- The second argument is the path to the template which you are rendering.
- The third, final argument, is a simple dictionary with a key of ‘movies’ and it’s associated value, the returned set of all movies from the database.
The view itself is what is known as a function-based view in Django, it is implemented as a python function and is typically simple and direct in functionality. Due to their simplicity, these views typically have limited reusability and can lead to organizational issues as your application grows.
Luckily, there is another form of view known as a class-based view, which is better suited for more broad scale usage and can be inherited across multiple different views. I won’t dive into class-based views in this piece but you can read more about them here.
Templates
Templates in Django are the final piece of our MVT architecture and act a blueprint or layout for your web applications. They aid in creating HTML pages that can show dynamic content using the data queried from your models through your view functions.
The key piece of templates is that while they create dynamic web pages with template tags and variables from Djang, they are, as you may have guessed, REUSABLE! Templates are fantastic in that they allow you to use pieces of them across multiple pages of your application, similar to components in React.
Four Key Components of Templates
HTML Structure
Django templates are mostly HTML and they provide the structure and the layout for your webpage. A template takes the queried data and implements it as designed. See template example below:
<!-- example of a Django template -->
<!DOCTYPE html>
<html>
<head>
<title>Movie List</title>
</head>
<body>
<h1>List of Movies</h1>
{% if movies %}
<ul>
{% for movie in movies %}
<li>{{ movie.title }} - {{ movie.director }} - {{ movie.release_date }}</li>
{% endfor %}
</ul>
{% else %}
<p>No movies available.</p>
{% endif %}
</body>
</html>
Template Tags
A template tag is a special code built within Django templates that controls how the template behaves (handles data). These tags are written in, what may be for you, an interesting syntax. While the code within these tags may resemble Python code syntax, which is done on purpose, it is not actual Python code. This syntax is part of Django’s own language designed specifically for templates and therefore has much more limited functionality, separate from that of any other Python library.
# these are template tags
{% if movies %} -- {% for movie in movies % } -- {% else %}
Template Variables
Template variables are pretty much exactly what they say they are, variables within your template. These variables represent their corresponding data from the attached view function. Template variables are wrapped in double curly braces within the template. When the template is rendered for the user, the corresponding value is rendered instead of the variable name (pretty simple here).
# this is a template variable
{{ movie.title }}
Inheritance and Extension
Much in the same sense as inheriting classes in Python, Django templates allow developers to build base parent templates which can then be shared and overridden within their child templates.
Child templates use template tags, {% extends ‘template_name’ %} to inherit the parent base template. In order to overwrite our parental template, we must block our title or content using pre-built template tags. These templates can be easily adjusted to determine the level of customization needed for a template’s children.
Below, I’ve refactored our basic template from earlier to demonstrate the use of template tags to overwrite our parent template.
<!-- this is our base parent template (slightly changed) -->
<!DOCTYPE html>
<html>
<head>
<!-- the block title tag below indicates that the
child will likely overwrite this title -->
<title>{% block title %}Movie List{% endblock %}</title>
</head>
<body>
<h1>List of Movies</h1>
<!-- the same goes for the block content tag below -->
{% block content %}
{% endblock %}
</body>
</html>
<!-- this is our child template which alters the base parent -->
{% extends 'base.html' %}
<!-- from where this tag begins, insert HTML until tag ends -->
{% block title %}Custom Movie List Title{% endblock %}
<!-- same here, insert the following HTML/tags/variables until tag closes -->
{% block content %}
{% if movies %}
<ul>
{% for movie in movies %}
<li>{{ movie.title }} - {{ movie.director }} - {{ movie.release_date }}</li>
{% endfor %}
</ul>
{% else %}
<p>No movies available.</p>
{% endif %}
<!-- the corresponding closing tag for block content above -->
{% endblock %}
Conclusion
Phew, we’ve reached the end of our maybe not so basic introduction into Django and it’s MVT architecture. We’ve learned how the framework’s separation of responsibilities, models, views, and templates, leads to faster web development and fosters clean, dry code. We’ve touched on a few topics regarding how these entities work in conjunction with one another to render a dynamic HTML page. And we’ve looked at the similarities, and differences, the Django framework has from it’s core Python language. This framework is more than just speed of working with large databases, it is about a clean, organized codebase that is easily scalable and customizable to your development needs.
For more on Django, visit the official Django Project, or my personal favorite, Geeks for Geeks on Django.
¹ ²“Django.” Django Project, www.djangoproject.com/. Accessed 28 Nov. 2023.