Overview
Django 1.11, released on April 4, 2017, is the last release of the 1.x branch and the last to support Python 2. It is an LTS (Long Term Support) release that will receive security fixes until April 2020. It introduces Subquery expressions in the ORM and template-based form widget rendering.
Key Features
Subquery expressions
Subquery and Exists expressions allow nesting queries directly inside ORM annotations or filters. This avoids N+1 queries and enables complex queries without raw SQL.
from django.db.models import OuterRef, Subquery, Exists
# Latest comment for each article
latest_comment = (
Comment.objects
.filter(article=OuterRef('pk'))
.order_by('-created_at')
.values('text')[:1]
)
articles = Article.objects.annotate(
latest_comment=Subquery(latest_comment)
)
for article in articles:
print(f"{article.title}: {article.latest_comment}")
# Filter articles that have at least one comment
has_comments = Comment.objects.filter(article=OuterRef('pk'))
commented_articles = Article.objects.filter(
Exists(has_comments)
)
Template-based widget rendering
Form widget rendering now uses the template engine instead of hard-coded Python. This makes customizing the appearance of form fields easier by simply overriding a template.
# Overriding the input widget template
# templates/django/forms/widgets/input.html
# <input
# type="{{ widget.type }}"
# name="{{ widget.name }}"
# class="form-control"
# {% if widget.value %}value="{{ widget.value }}"{% endif %}
# >
# Or via a custom widget
from django.forms import TextInput
class SearchField(TextInput):
template_name = 'widgets/search_field.html'
def get_context(self, name, value, attrs):
context = super().get_context(name, value, attrs)
context['widget']['placeholder'] = 'Search...'
return context
