Migrating to Django 2.0: What You Need to Know
Django 2.0 has landed, and it’s a big one. After years of supporting both Python 2 and Python 3, Django has finally made the leap to Python 3-only. If you’re still on Django 1.11, here’s your comprehensive migration guide.
The Big Change: Python 3 Only
Let’s address the elephant in the room first. Django 2.0 requires Python 3.4, 3.5, or 3.6. Python 2.7 is no longer supported.
If you’re still on Python 2.7, this is your signal to migrate. Python 2’s end-of-life is approaching (January 2020), and the Django team is leading the ecosystem forward.
My recommendation: Target Python 3.6. It has f-strings, better async support, and will be supported for years to come.
The New URL Routing: path() and re_path()
This is the change you’ll notice most in your daily coding. The old url() function with regex patterns is being replaced with the simpler path() function.
Before (Django 1.x):
from django.conf.urls import url
urlpatterns = [
url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
url(r'^articles/(?P<article_id>[0-9]+)/$', views.article_detail),
]
After (Django 2.0):
from django.urls import path
urlpatterns = [
path('articles/<int:year>/', views.year_archive),
path('articles/<int:article_id>/', views.article_detail),
]
The new syntax is cleaner and more readable. Path converters like <int:year> replace the regex patterns, and they’re much easier to understand at a glance.
Built-in Path Converters
Django 2.0 ships with five path converters:
str- Matches any non-empty string, excluding/int- Matches zero or any positive integerslug- Matches slug strings (letters, numbers, hyphens, underscores)uuid- Matches formatted UUIDspath- Matches any non-empty string, including/
For complex patterns, re_path() still supports full regex:
from django.urls import re_path
urlpatterns = [
re_path(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
]
Mobile-Friendly Admin
The Django admin has received a facelift. It’s now responsive and works beautifully on mobile devices. No code changes required—just upgrade and enjoy.
This might seem minor, but it’s a quality-of-life improvement that matters when you’re debugging production issues from your phone.
Window Expressions
Django 2.0 adds support for window functions in the ORM, bringing powerful analytical queries to your Django apps.
from django.db.models import F, Window
from django.db.models.functions import Rank
queryset = Movie.objects.annotate(
rank=Window(
expression=Rank(),
order_by=F('rating').desc(),
)
)
If you’ve been reaching for raw SQL for ranking, running totals, or moving averages, window expressions are your new best friend.
Migration Steps
Here’s my recommended migration path:
1. Ensure You’re on Django 1.11
Django 1.11 is the bridge release. It includes all the deprecation warnings for 2.0 changes. Fix all warnings before proceeding.
python -Wd manage.py test
2. Check Your Dependencies
Many third-party packages needed updates for Django 2.0. Check that your dependencies are compatible:
pip list --outdated
Key packages to verify:
- Django REST Framework (3.7+)
- django-debug-toolbar (1.9+)
- celery (4.0+)
- django-allauth (0.34+)
3. Update Your Python Version
If you’re not already on Python 3, this is when you need to migrate. This is often the harder part of the upgrade.
4. Run the Django Upgrade
Update your requirements:
Django>=2.0,<2.1
Then run:
pip install -r requirements.txt
python manage.py migrate
python manage.py test
5. Update URL Patterns
Convert your URL patterns from url() to path(). This can be done incrementally—both work in Django 2.0.
Breaking Changes to Watch For
A few gotchas that have caught teams I’ve worked with:
on_delete is now required on ForeignKey:
# Django 1.x - on_delete was optional
author = models.ForeignKey(User)
# Django 2.0 - on_delete is required
author = models.ForeignKey(User, on_delete=models.CASCADE)
context in signals removed:
The context parameter in signals has been removed. Update your signal handlers accordingly.
Bytestring reverse URLs:
reverse() now always returns a string, not bytes. If you were handling bytes, update your code.
Should You Upgrade Now?
Yes, if:
- You’re starting a new project
- You’re already on Python 3
- You want the latest security updates
Wait a bit if:
- Critical dependencies don’t support Django 2.0 yet
- You’re on Python 2.7 and the migration will take significant effort
For production systems, I recommend waiting 2-3 months after a major release for the ecosystem to stabilize. Django 2.0.1 and 2.0.2 will iron out edge cases.
Final Thoughts
Django 2.0 is a mature, well-thought-out release. The Python 3-only requirement is the right call—it simplifies the codebase and allows the team to use modern Python features.
The new URL routing alone is worth the upgrade. It makes Django code more readable and approachable for newcomers.
Take your time with the migration, test thoroughly, and enjoy the cleaner syntax.
The future is Python 3. Welcome to it.