bgneal@4: Upgrading to Django 1.4 bgneal@4: ####################### bgneal@4: bgneal@4: :date: 2012-04-15 14:50 bgneal@4: :tags: Django bgneal@4: :slug: upgrading-to-django-1.4 bgneal@4: :author: Brian Neal bgneal@4: bgneal@4: `Django 1.4`_ came out recently, and I took a few hours to upgrade my first site bgneal@4: yesterday. I thought it would be useful for my own reference to write down what bgneal@4: I did. I hope it will be useful to others. I'd love to read what you had to do, bgneal@4: so if you went through this process and blogged about it, please leave a bgneal@4: comment. Please keep in mind these aren't hard and fast steps or a recipe to bgneal@4: follow, as my sites are probably nothing like yours and may use different bgneal@4: features of Django. bgneal@4: bgneal@4: Preparation bgneal@4: ----------- bgneal@4: bgneal@4: The first thing I did was to read very carefully the `Django 1.4 release bgneal@4: notes`_. The Django team does a great job of documenting what has changed, so it bgneal@4: is well worth your time to read the release notes. It is also a good idea to at bgneal@4: least skim the `Django Deprecation Timeline`_. After reading these, you should bgneal@4: make a list of the things you want to change, add, or remove. bgneal@4: bgneal@4: Tips bgneal@4: ---- bgneal@4: bgneal@4: After deciding what areas you want or need to change in your code, these tips bgneal@4: may be useful to help you implement the changes. bgneal@4: bgneal@4: #. **Run with warnings turned on**. Use this command to run the development bgneal@4: server: ``$ python -Wall manage.py runserver``. Django makes use of `Python's bgneal@4: warning system`_ to flag features that are deprecated. By running Python with bgneal@4: the ``-Wall`` switch, you'll see these warnings in the development server bgneal@4: output. bgneal@4: bgneal@4: #. **Use the debugger to track down warnings**. Not sure where a pesky warning bgneal@4: is coming from? Just open the Django source code in your editor and put a bgneal@4: ``import pdb; pdb.set_trace()`` line right above or below the warning. You bgneal@4: can then use the debugger's ``w`` command to get a stack trace and find out bgneal@4: exactly what code is leading to the warning. In my case I kept getting a few bgneal@4: warnings with no idea where they were coming from. I used this technique to bgneal@4: verify the warnings were coming from third party code and not my own. For bgneal@4: more information on using the debugger (and you really **should** know how to bgneal@4: use this invaluable tool), see the `Pdb documentation`_. bgneal@4: bgneal@4: My upgrade experience bgneal@4: --------------------- bgneal@4: bgneal@4: Here is a list of things that I did during my port. Again, you may not need to bgneal@4: do these, and the next site I upgrade may have a different list. All of these bgneal@4: changes (except for the first) are described in the `Django 1.4 release notes`_. bgneal@4: bgneal@4: #. **Upgrade my Django debug toolbar**. As of this writing, the Django debug bgneal@4: toolbar I got from PyPI was not compatible with Django 1.4. I simply bgneal@4: uninstalled it and grabbed the development version from GitHub with bgneal@4: ``pip install git+https://github.com/django-debug-toolbar/django-debug-toolbar.git``. bgneal@4: bgneal@4: #. **Remove the ADMIN_MEDIA_PREFIX setting**. The admin application in bgneal@4: Django 1.4 now relies on the ``staticfiles`` application (introduced in bgneal@4: Django 1.3) to handle the serving of static assets. bgneal@4: bgneal@4: #. **Remove use of the {% admin_media_prefix %} template tag**. Related to the bgneal@4: above, this tag is now deprecated. I had a custom admin view that used this bgneal@4: template tag, and I simply replaced it with ``{{ STATIC_URL }}/admin``. bgneal@4: bgneal@4: #. **Remove verify_exists on URLFields**. The ``verify_exists`` option to bgneal@4: the ``URLField`` has been removed for performance and security reasons. I had bgneal@4: always set this to ``False``; now I just had to remove it altogether. bgneal@4: bgneal@4: #. **Add the require_debug_false filter to logging settings**. As explained in bgneal@4: the release notes, this change prevents admin error emails from being sent bgneal@4: while in ``DEBUG`` mode. bgneal@4: bgneal@4: #. **django.conf.urls.defaults is deprecated**. I changed my imports in all bgneal@4: ``urls.py`` files to use ``django.conf.urls`` instead of bgneal@4: ``django.conf.urls.defaults`` to access ``include()``, ``patterns()``, and bgneal@4: ``url()``. The Django team had recently moved these functions and updated the bgneal@4: docs and tutorial to stop using the frowned upon ``from bgneal@4: django.conf.urls.defaults import *``. bgneal@4: bgneal@4: #. **Enable the new clickjacking protection**. A nice new feature is some new bgneal@4: middleware that adds the ``X-Frame-Options`` header to all response headers. bgneal@4: This provides clickjacking_ protection in modern browsers. bgneal@4: bgneal@4: #. **Add an admin password reset feature**. By adding a few new lines to your bgneal@4: ``urlconf`` you get a nifty new password reset feature for your admin. bgneal@4: bgneal@4: #. **Update to the new manage.py**. This was the biggest change with the most bgneal@4: impact. The Django team has finally removed a long standing wart with its bgneal@4: ``manage.py`` utility. Previously, ``manage.py`` used to play games with your bgneal@4: ``PYTHONPATH`` which led to confusion when migrating to production. It could bgneal@4: also lead to having your settings imported twice. See the next section in bgneal@4: this blog entry for more on what I did here. bgneal@4: bgneal@4: Reorganizing for the new manage.py bgneal@4: ---------------------------------- bgneal@4: bgneal@4: The change with the largest impact for me was reorganizing my directory bgneal@4: structure for the new ``manage.py`` command. Before this change, I had organized bgneal@4: my directory structure like this: bgneal@4: bgneal@4: :: bgneal@4: bgneal@4: mysite/ bgneal@4: media/ bgneal@4: static/ bgneal@4: mysite/ bgneal@4: myapp1/ bgneal@4: __init__.py bgneal@4: models.py bgneal@4: views.py bgneal@4: urls.py bgneal@4: myapp2/ bgneal@4: __init__.py bgneal@4: models.py bgneal@4: views.py bgneal@4: urls.py bgneal@4: settings/ bgneal@4: __init__.py bgneal@4: base.py bgneal@4: local.py bgneal@4: production.py bgneal@4: test.py bgneal@4: apache/ bgneal@4: myproject.wsgi bgneal@4: logs/ bgneal@4: templates/ bgneal@4: manage.py bgneal@4: urls.py bgneal@4: LICENSE bgneal@4: fabfile.py bgneal@4: requirements.txt bgneal@4: bgneal@4: After replacing the contents of my old ``manage.py`` with the new content, I bgneal@4: then reorganized my directory structure to this: bgneal@4: bgneal@4: :: bgneal@4: bgneal@4: mysite/ bgneal@4: media/ bgneal@4: static/ bgneal@4: myapp1/ bgneal@4: __init__.py bgneal@4: models.py bgneal@4: views.py bgneal@4: urls.py bgneal@4: myapp2/ bgneal@4: __init__.py bgneal@4: models.py bgneal@4: views.py bgneal@4: urls.py bgneal@4: myproject/ bgneal@4: settings/ bgneal@4: __init__.py bgneal@4: base.py bgneal@4: local.py bgneal@4: production.py bgneal@4: test.py bgneal@4: apache/ bgneal@4: myproject.wsgi bgneal@4: logs/ bgneal@4: templates/ bgneal@4: urls.py bgneal@4: LICENSE bgneal@4: fabfile.py bgneal@4: manage.py bgneal@4: requirements.txt bgneal@4: bgneal@4: It is a subtle change, but I like it. It now makes it clear that my project is bgneal@4: just an application itself, consisting of the top-level ``urls.py``, settings, bgneal@4: templates and logs. The ``manage.py`` file is now at the top level directory bgneal@4: also, which seems right. bgneal@4: bgneal@4: I had always made my imports as ``from app.models import MyModel`` instead of bgneal@4: ``from myproject.app.models``, so I didn't have to update any imports. bgneal@4: bgneal@4: Since I use the "settings as a package" scheme, I did have to update the imports bgneal@4: in my settings files. For example, in my ``local.py`` I had to change ``from bgneal@4: settings.base import *`` to ``myproject.settings.base import *``. bgneal@4: bgneal@4: What I didn't do bgneal@4: ---------------- bgneal@4: bgneal@4: Django 1.4's largest new feature is probably its support for timezones. I bgneal@4: decided for this project not to take advantage of that. It would require a lot bgneal@4: of changes, and it isn't really worth it for this small site. I may use it on bgneal@4: the next site I convert to Django 1.4, and I will definitely be using it on new bgneal@4: projects. bgneal@4: bgneal@4: Conclusion bgneal@4: ---------- bgneal@4: bgneal@4: The upgrade process went smoother and quicker than I thought thanks to the bgneal@4: excellent release notes and the Django team's use of Python warnings to flag bgneal@4: deprecated features. bgneal@4: bgneal@4: bgneal@4: .. _Django 1.4: https://www.djangoproject.com/weblog/2012/mar/23/14/ bgneal@4: .. _Django 1.4 release notes: https://docs.djangoproject.com/en/1.4/releases/1.4/ bgneal@4: .. _Django Deprecation Timeline: https://docs.djangoproject.com/en/1.4/internals/deprecation/ bgneal@4: .. _Python's warning system: http://docs.python.org/library/warnings.html bgneal@4: .. _Pdb documentation: http://docs.python.org/library/pdb.html bgneal@4: .. _clickjacking: http://en.wikipedia.org/wiki/Clickjacking