The Django framework started in 2003, as a project done by Adrian Holovaty and Simon Willison at the Journal-World newspaper in Lawrence, Kansas City in the United States. In 2005, Holovaty and Willison released the first public version of the framework, naming it after the Belgian-French guitarist Django Reinhardt.

Fast forward to 2022, the Django framework now operates under the guidance of the Django software foundation(DSF), the framework core has over one thousand contributors, it's been released in four major versions with hundreds of minor versions, and there are over three thousand packages specifically designed to work with the Django framework[1].

The Django framework has remained true to its origins as a Model-View-Controller (MVC) server-side framework designed to operate with relational databases. Nevertheless, Django has stayed up to date with most web development tendencies -- mainly via third party packages -- to operate alongside technologies like non-relational databases (NoSQL), real time Internet communication (e.g. WebSockets, streaming) and modern JavaScript practices. All this to the point, the Django framework is now the web development framework of choice for a wide array of organizations, supporing many high-profile sites and applications[2].

In this chapter you'll learn about the Django framework design principles, which are key to understanding the day to day aspects of working with the Django framework. In addition, you'll also learn about the four major Django versions and their differences. Next, you'll learn how to install Django in various ways: as a tar.gz file, with pip, using git and with virtualenv.

Once you install the Django framework, you'll learn how to start a Django project and how to set it up with a relational database. Next, you'll learn about the core building blocks in the Django framework -- urls, templates & apps -- and how they work with one another to set up content. Finally, you'll learn how to set up the Django admin site, which is a web based interface designed to access the relational database connected to a Django project.

Django framework design principles

If you work long enough in web development, you'll eventually come the conclusion you can produce the same results with just about any web framework and programming language. But while you can in fact produce identical results, what will vary drastically is the time you spend creating a solution: the time creating a prototype, the time adding new features, the time doing testing, the time doing debugging and the time deploying to scale, among other things.

In this sense, the Django framework uses a set of design principles that produces one of the most productive web development processes compared to many other web frameworks. Note, I'm not saying Django is a silver bullet (e.g. the best at prototyping, the most scalable), I'm saying that at the end of the day, the Django framework incorporates a set of design principles and trade-offs that make it one of most productive frameworks for building the features needed by most medium to large web applications.

Now, while you might think I'm biased -- after all I'm writing an entire book about the topic -- I'll lay out these design principles first, so you can gain a better understanding of what gives the Django framework this edge.

Don't repeat yourself (DRY) principle

Repetition might be good to emphasize a point, but when it comes to web development, it just leads to additional and time consuming work. In fact, the very nature of web development, which operates across multiple-tiers interacting with one another (e.g. HTML templates, business logic methods and databases) lends itself to repetition.

The Django framework really tries to force you not to repeat yourself, so let's see how Django enforces not repeating yourself and why this is a good thing.

Let's say you want to build a coffeehouse application to publish information about stores and also have a contact form for customers. The first thing you'll need to do is determine what kind of information is required for stores and the contact form. Figure 1-1 illustrates a mock-up of two Django models for each of these entities.

Figure 1-1. Django models for store and contact entities

Notice how the Django models in figure 1-1 each have different field names and a data type to restrict values. For example, the statement name = models.CharField(max_length=30) tells Django a store name should have maximum 30 characters, while the statement email = models.EmailField() tells Django the contact entity should contain a valid email value. If the coffeehouse is like most web applications, you'll generally end up doing the following for the store and contact entities:

The crux of doing this last task list is you have the potential of repeating dozens of similar pieces of information (e.g. names, value limits) in database definition language (DDL), HTML forms, business validation logic and URLs, among other things. A process that's not only time consuming, but also error prone.

Wouldn't it be easier that based on a statement like models.CharField(max_length=30) you could generate an HTML form input, a DDL statement and automatically validate information to only contain 30 characters ? This is exactly what Django's DRY design principle does.

Figure 1-2 illustrates the same Django models from Figure 1-1 and the various constructs you can generate from the same models without the need to repeat yourself.

Figure 1-2. Django models create separate constructs based on DRY principle

As you can see in figure 1-2, the entities that represent Django models are capable of generating HTML forms to present to the public, an administrative interface to manage the entities, validation logic to enforce entity values, as well as the DDL to generate database tables representing the entities.

While it's a little premature to discuss the actual techniques to generate such constructs from Django models, needless to say it's much simpler than keeping track of multiple references of the same thing (e.g. name, email) in HTML forms, DDL, validation logic and other locations.

In this sense, Django really helps you define things in a single place and not have to repeat them elsewhere. Note that it's always possible to repeat yourself to obtain custom behaviors, but by default, Django enforces DRY principles in nearly everything you do with it.

Explicit is better than implicit

Python, the programming language used by Django, has a mantra-like statement called "The Zen of Python" defined as part of the language's Python Enhancement Proposals (PEP), specifically PEP 20[3]. One of the statements in PEP 20 states "Explicit is better than implicit" and with Django being based on Python, this principle is also taken to heart.

Being explicit leads to web applications that are easily understood and maintained by a greater number of people. Adding new features or understanding the logic behind a web application can be hard enough for someone that didn't write it originally, but if you toss into the mix constructs that have implicit behaviors, users only face greater frustration trying to figure out what's being done implicitly. Explicit does require a little more work typing, but it's well worth it when you compare it to the potential effort you can face trying to debug or solve a problem.

Let's take a quick look at Django's explicitness in a common web development construct used across different MVC frameworks: a view method. A view method acts as the C(ontroller) in an MVC framework, charged with handling incoming requests, applying business logic and then routing requests with an appropriate response.

To get a better feel for this explicitness, I'll present a Django view method and an equivalent Ruby on Rails view method that performs the same logic of fetching a store by a given id and routing the response to a template. The following snippet is the Ruby on Rails version, note the lines with # which are comments and indicate what's happening.

class StoresController < ApplicationController
  def show
    # Automatic access to params, a ruby hash with request parameters and view parameters
    @store = Store.find(params[:id])
    # Instance variables like @store are automatically passed on to view template
    # Automatically uses template views/stores/show.html.erb

Although very succinct, notice all the implicit behavior surrounding the process to access data, pass data to a template and assign a template. The following snippet is an equivalent Django view method.

# Explicit request variable contains request parameters
# Other view parameters must be explicitly passed to views
def detail(request, store_id):
    store = Store.objects.get(id=store_id)
    # Instance variables must be explicitly passed on to a view template
    # Explicit template must be assigned
    return render(request, 'stores/detail.html', {'store': store})

Notice in this last snippet there's no guessing where input parameters come from, they're explicitly declared as arguments in the view method. In addition, values are explicitly passed to a template and the template is also explicitly declared, so the logic is much more amicable to newcomers.

The implicitness of the Ruby on Rails view method is often called 'magic' and is even considered a feature by many. It's called 'magic' because certain behaviors are provided behind the scenes. However, unless you know the framework and application down to a tee, it can be very difficult to pinpoint why certain things are happening, making it more difficult to fix or update things. So even though 'magic' may be able to save you a few minutes or hours in development time at the start, it can end up costing you hours or days in maintenance later.

So just like in Python, the Django framework will always favor an explicit approach over any implicit technique.

It's important to point out explicit doesn't equal verbose or redundant. While you'll certainly end up typing a little more code in Django vs. web frameworks that are implicitly driven (e.g. Rails), as it was described in the prior DRY principle section, the Django framework goes to great lengths to avoid having to introduce more code than necessary in a web application.

Finally, explicit also doesn't mean no defaults. The Django framework does use reasonable defaults where possible, it just doesn't use default values where it isn't obvious they're being used. In essence, the Django framework uses defaults, but avoids using defaults that produce 'magical' outcomes.

Loosely coupled architecture

The Django framework being an MVC framework operates across multiple tiers (e.g. HTML templates, business logic methods and databases). However, Django takes great care of maintaining a loosely couple architecture across all the components that operate across these tiers.

Being loosely coupled means there are no rigid dependencies between the parts that make up a Django application. For example, in Django it's perfectly valid to serve content directly from an HTML template, without the need to use business logic or set up database. Just like in Django it's also perfectly valid to forgo using an HTML template and return raw data directly from a business logic method (e.g. for a REST service).

A later section in this chapter entitled "Set up content: Understand URLs, templates and apps" goes into greater detail with examples on how Django's loosely coupled architecture works.

Django major, minor, patch and LTS versions

Django has been released in four major versions, identified by the one digit versioning scheme: 1, 2, 3 and 4. Each of these versions includes major design changes and breaking changes with respect to prior versions, that although don't alter the Django design principles enumerated earlier, they can dramatically change with respect to what related technologies they support. The next sections describe the differences between each of these Django major versions.

In addition to Django's major versions, each major version is also accompanied by minor and patch versions. Minor versions include considerable changes that are streamlined to work with the same major version (e.g. 4.0, 4.1, 4,2), where as patch versions include important updates applicable to minor versions (e.g. 4.0.1, 4.1.5, 4.2.8). In addition to major, minor and patch versions, Django also releases Long Term Support (LTS) versions. An LTS version is simply a major/minor Django version that's given a greater support horizon -- two and a half years -- vs. a regular Django version -- eight months. For this reason, Django LTS versions are often the version of choice for many projects due their longer support cycle for things like security patches and other updates. The Django 1 LTS version is version 1.11, while other Django major versions follow the more standardized x.2 minor version to designate an LTS version (e.g. Django 2 has 2.2 LTS; Django 3 has 3.2 LTS; Django 4 has 4.2 LTS, etc).

Tip The only reason not to use the most recent major/minor Django version is due to non-Django dependencies. This could be organizational, such as Python 2 or Python 3.5 being the only supported Python versions by an IT department, or project dependent, such as a certain package (e.g. database, utility or other) being only available for Django x.y version. Under all other circumstance, the latest major & minor Django version should be preferred, as it offers the most features and has the latest security updates.

Django 1

Django 1 versions are the only ones capable of running Python 2. So if you need to develop an application for Python 2, Django 1 versions are the only ones that support it, specifically Python 2.7. It's worth pointing out that various Django 1 versions -- including Django 1.11 LTS -- also support Python 3 versions, ranging from Python 3.4 to Python 3.7.

Django 2

Django 2 versions are designed to run with Python 3 -- ranging from Python 3.5 to Python 3.9 -- so forget about all the old Python 2 syntax if you want to use Django 2.

The other differences between Django 2 vs. Django 1 are mostly about new small features and deprecated functionalities, for example:

New features in Django 2

Deprecated features in Django 2

Django 3

Django 3 versions are designed with one major feature: asynchronous/non-blocking support. Asynchronous support is a major paradigm shift for Django -- inclusively for Python itself, see the appendix: Python asynchronous behavior: Coroutines, threads, processes, event loops, asyncio, async & await. For Django, it means it has to use an ASGI server[4] vs. a WSGI server, views must also use certain techniques[5], in addition to other Django parts (e.g. ORM, templates) requiring adherence to certain designs. It's important to note asynchronous design isn't a hard requirement for Django 3, which means you can completely ignore asynchronous design and still use Django 3 with synchronous design, as if it were Django 2.

Besides asynchronous support, Django 3 versions are designed to run with Python 3.6 to Python 3.9. Other changes from Django 2 to Django 3 are of a similar nature than those from Django 1 to Django 2, mainly new small features and deprecated functionalities.

New features in Django 3

Deprecated features in Django 3

Django 4

Django 4 versions are designed to run with Python 3.8 and up to Python 3.10. Most differences between Django 4 vs. Django 3 are mostly about new small features and deprecated functionalities, for example:

New features in Django 4

Deprecated features in Django 4