Of course. Here's a comprehensive guide covering Django 1.11, including its features, common patterns, how to use it, and crucial information about its end-of-life status.

Important: Django 1.11 is End-of-Life (EOL)
This is the most important thing to know. Django 1.11 reached its official end-of-life on April 1, 2025.
- What this means: No security patches, bug fixes, or updates are being released for it.
- The risk: Running a project on Django 1.11 in a production environment is a significant security risk. Your application is vulnerable to known and newly discovered security flaws.
- Recommendation: You should immediately plan to upgrade your project to a supported, modern version of Django (currently 4.x or 5.x). The upgrade process can be complex, but it's essential for the security and maintainability of your project.
This guide is for understanding legacy codebases or for educational purposes only.
Key Features and Changes in Django 1.11
Django 1.11 was a major "Long-Term Support" (LTS) release, which meant it received security updates for several years. It introduced many useful features and marked the end of the Python 2.x support era.
A. Python 2 and 3 Compatibility
This was a major theme for 1.11. It was the last version to support Python 2.7. It introduced several features to help with the transition from Python 2 to Python 3.

__future__imports: Many internal components were updated to use modern Python 3 features.django.utils.six: Thesixlibrary was still heavily used internally to write code that worked on both Python 2 and 3.
B. Built-in JSONField
One of the most anticipated features. Before 1.11, you had to use third-party packages like django-jsonfield to store JSON data in your database.
# In your models.py
from django.db import models
class Product(models.Model):
name = models.CharField(max_length=100)
details = models.JSONField() # Store a dictionary or list directly!
# Usage
product = Product.objects.create(name="Laptop", details={"color": "black", "ram": "16GB"})
print(product.details['color']) # Output: 'black'
C. Template Improvements
-
{% url %}tag no longer requires a view name as the first argument: This was a long-standing point of confusion. You could now just pass the name of the URL pattern.<!-- Old way (still works) --> <a href="{% url 'myapp:detail' pk=object.pk %}">View</a> <!-- New, cleaner way in 1.11 --> <a href="{% url 'myapp:detail' pk=object.pk %}">View</a>Correction: The "no first argument" feature was more of a gradual improvement over several versions. The main point is that the
urltag became more robust and powerful. -
{% static %}tag: The{% load static %}tag and its usage became the standard way to link to static files (CSS, JS, images).
D. Password Validation
Django introduced a more robust and configurable password validation system. Instead of just checking the minimum length, you could now enforce rules like complexity requirements.
# In your settings.py
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
'OPTIONS': {
'min_length': 9, # Default was 8
}
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
E. Other Notable Features
on_deleteargument is required forForeignKeyandOneToOneField: This was a major breaking change for safety. You had to explicitly define what should happen when the object being referenced is deleted.models.CASCADE: (Default) Delete the object with the foreign key.models.PROTECT: Prevent deletion by raisingProtectedError.models.SET_NULL: Set the foreign key toNULL(requiresnull=True).models.SET_DEFAULT: Set the foreign key to its default value.
- Database Backends: Improved support for newer versions of PostgreSQL and MySQL.
- Management Commands: The
call_commandutility was enhanced.
How to Set Up a Django 1.11 Project
Here's how you would have created a project back in the day.
Step 1: Installation
You would have installed it using pip. It's recommended to use a virtual environment.
# Create and activate a virtual environment python -m venv myproject_env source myproject_env/bin/activate # On Windows: myproject_env\Scripts\activate # Install Django 1.11 pip install "Django==1.11"
Step 2: Project and App Creation
The django-admin and manage.py commands were the same as they are today.
# Create a new project django-admin startproject myproject # Navigate into the project directory cd myproject # Create a new app python manage.py startapp myapp
Step 3: Basic Project Structure
Your project would look like this:
myproject/
├── myproject/ # Project configuration package
│ ├── __init__.py
│ ├── settings.py # Main settings file
│ ├── urls.py # Project-level URL configuration
│ ├── wsgi.py # WSGI application entry point
│ └── asgi.py # ASGI application entry point (newer in 1.11)
├── myapp/ # Your app
│ ├── migrations/
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── models.py
│ ├── tests.py
│ └── views.py
└── manage.py # Command-line utility for the project
Step 4: Configure settings.py
You had to register your new app in INSTALLED_APPS.
# myproject/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'myapp', # Add your new app here
]
Common Code Patterns in Django 1.11
A. URLs (urls.py)
URL patterns were defined using the url() function.
# myproject/urls.py
from django.conf.urls import url
from django.contrib import admin
from myapp import views as myapp_views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^articles/([0-9]{4})/$', myapp_views.year_archive),
url(r'^articles/([0-9]{4})/([0-9]{2})/$', myapp_views.month_archive),
]
B. Views (views.py)
Views were simple functions that took a request and returned an HttpResponse.
# myapp/views.py
from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from .models import Article
def year_archive(request, year):
# Note: This is a simple example, not efficient for large datasets
articles = Article.objects.filter(pub_date__year=year)
output = "<h1>Articles for %s</h1>" % year
for article in articles:
output += "<p>%s</p>" % article.title
return HttpResponse(output)
def month_archive(request, year, month):
# A more common pattern using get_object_or_404
# This would typically render a template instead of returning plain HTML
return HttpResponse("You're looking at articles from %s %s." % (year, month))
C. Models (models.py)
Models defined the database schema. The on_delete argument was key.
# myapp/models.py
from django.db import models
from django.utils import timezone
class Article(models.Model):
author = models.ForeignKey('auth.User', on_delete=models.CASCADE) # on_delete is required!= models.CharField(max_length=200)
text = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
published_date = models.DateTimeField(blank=True, null=True)
def publish(self):
self.published_date = timezone.now()
self.save()
def __str__(self):
return self.title
D. Templates
Templates used the Django Template Language (DTL). The {% static %} tag was standard.
<!-- myapp/templates/myapp/article_list.html -->
{% extends "base.html" %}
{% block content %}
<h1>Latest Articles</h1>
{% for article in article_list %}
<h2><a href="{% url 'myapp:detail' pk=article.pk %}">{{ article.title }}</a></h2>
<p>{{ article.text|truncatewords:30 }}</p>
{% empty %}
<p>No articles are available.</p>
{% endfor %}
{% endblock %}
Migrating from Django 1.11
If you are stuck maintaining a 1.11 project, here is the high-level upgrade path.
- Create a Backup: Before you do anything, back up your entire project, including the database.
- Upgrade Dependencies: Upgrade
pip,setuptools, andwheel. - Upgrade to a Modern LTS Version: It's best to skip intermediate versions and go straight to a current LTS release like Django 4.2 or Django 3.2.
pip install --upgrade "Django==4.2"
- Run
python manage.py check: This command will find many common compatibility issues for you. - Address Code Changes:
- URLs: Replace
django.conf.urls.urlwithdjango.urls.re_path(for regex) ordjango.urls.path(for clean URLs). - Templates: Update
{% load staticfiles %}to{% load static %}. - Settings: Check for deprecated settings in the Django 1.11 to 2.0 migration guide.
- Imports: Update any internal Django imports that may have changed.
- URLs: Replace
- Database Migrations:
- Delete all migration files in your app's
migrations/folder (except__init__.py). - Run
python manage.py makemigrationsto generate new migration files based on your current models. - Run
python manage.py migrateto apply the changes to your database.
- Delete all migration files in your app's
- Thoroughly Test: Go through every feature of your application manually. Automated tests are even better. Pay close attention to forms, admin, and any custom logic.
The Django documentation has detailed version-specific migration guides that are invaluable for this process.
