Saying Goodbye to Old Django

We recently wrote about our switch from TravisCI and Coveralls to CircleCI and CodeCov in How We Maintain High Levels of Code Quality.

In addition to adding Django 2.0 compatibility testing, we are dropping support for Django 1.8, 1.9, and 1.10 since their official support has ended. Python 3.3 has reached end of life and is removed from compatibility testing as well. Django 2.0 no longer supports Python 2, so that combination is also removed. This table shows the combinations of Django and Python supported by Pinax:

Django \ Python 2.7 3.4 3.5 3.6
1.11 * * * *
2.0 * * *

About Automated Testing

We run detox locally to see which test configurations fail and why. In addition, we use isort for automated import sorting and flake8-quotes for enforcing our double quote standard.

Django 2.0 Deprecations

These are the most common deprecations we encountered when upgrading to Django 2.0 as well as their resolutions.

1. url

from django.core.urlresolvers import reverse


from django.urls import reverse


In, we change MIDDLEWARE_CLASSES to MIDDLEWARE per the 1.11 docs.

3. django.shortcuts.render()

The django.shortcuts.render_to_response() method is deprecated in favor of django.shortcuts.render().

4. User.is_authenticated and User.is_anonymous

We change references to User.is_authenticated() and User.is_anonymous() to property references (User.is_authenticated and User.is_anonymous).

5. SessionAuthenticationMiddleware

We remove references to SessionAuthenticationMiddleware class.

This middleware is no longer needed since session authentication is unconditionally enabled in Django 1.10+.

6. assignment_tag becomes simple_tag

We change out @register.assignment_tag for @register.simple_tag.

Django 2.0 Updates

These are the most common updates we made when upgrading to Django 2.0:

7. on_delete=models.CASCADE

We add on_delete=models.CASCADE (or some other value) to all ForeignKey and OneToOne model fields. This is required for Django 2.0.


In reusable apps that could be used for both 1.11 and 2.0 we create a module, if needed.

Currently the only import issue we know is mock which did not exist in Python standard library unittest until Python 3.3. See pinax-announcements‘s for example imports, then use from .compat import mock.

9. URL namespacing

We need to add app_name = "pinax_announcements" or similar to

For example:

from django.conf.urls import url

from . import views

urlpatterns = [


from django.conf.urls import url

from . import views

app_name = "pinax_likes"

urlpatterns = [

10. Use of path() in Projects

For new applications not requiring Django 1.11 compatibility we use path so we can write url definitions like this:

from django.urls import path

from . import views

app_name = "pinax_likes"

urlpatterns = [
    path("^like/<int:content_type_id>:<int:object_id>/$", views.LikeToggleView.as_view(), name="like_toggle")


There might be other issues to consider as you upgrade your site or app. These are just the ones we’ve come across so far while updating ours. For the full list of new features and changes in Django 2.0, see the official release notes.

If you need help, upgrading your site or building something new, don’t hesitate to let us know.

Happy upgrading!


Original draft had incorrect references to django.urls when they should have been django.conf.urls. I originally edited Kati's draft removing the conf by mistake. It has been fixed. Sorry for the confusion.

–Patrick Altman

Original draft should have referenced import reverse, but incorrectly referenced import include, url. I have corrected this.

–Kati Michel

Let's Keep in Touch

We'll update you when we post new articles like this one.

Javascript Testing with Jasmine and Karma Countdown to the Perseus 5.0 “Scaife” Launch