{"title":"Django version 0.96 release notes","version":"dev","locale":"en","docname":"releases/0.96","url":"/en/dev/releases/0.96/","canonical":"https://djangodocs.dev/en/dev/releases/0.96/","summary":"Welcome to Django 0.96! The primary goal for 0.96 is a cleanup and stabilization of the features introduced in 0.95.","html":"<h1>Django version 0.96 release notes<a class=\"heading-anchor\" href=\"#django-version-0-96-release-notes\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h1>\n<p>Welcome to Django 0.96!</p>\n<p>The primary goal for 0.96 is a cleanup and stabilization of the features\nintroduced in 0.95. There have been a few small <a class=\"reference internal\" href=\"#backwards-incompatible-changes\">backwards-incompatible\nchanges</a> since 0.95, but the upgrade process should be fairly simple\nand should not require major changes to existing applications.</p>\n<p>However, we’re also releasing 0.96 now because we have a set of\nbackwards-incompatible changes scheduled for the near future. Once\ncompleted, they will involve some code changes for application\ndevelopers, so we recommend that you stick with Django 0.96 until the\nnext official release; then you’ll be able to upgrade in one step\ninstead of needing to make incremental changes to keep up with the\ndevelopment version of Django.</p>\n<section id=\"backwards-incompatible-changes\">\n<h2>Backwards-incompatible changes<a class=\"heading-anchor\" href=\"#backwards-incompatible-changes\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>The following changes may require you to update your code when you switch from\n0.95 to 0.96:</p>\n<section id=\"mysqldb-version-requirement\">\n<h3><code class=\"docutils literal notranslate\">MySQLdb</code> version requirement<a class=\"heading-anchor\" href=\"#mysqldb-version-requirement\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>Due to a bug in older versions of the <code class=\"docutils literal notranslate\">MySQLdb</code> Python module (which\nDjango uses to connect to MySQL databases), Django’s MySQL backend now\nrequires version 1.2.1p2 or higher of <code class=\"docutils literal notranslate\">MySQLdb</code>, and will raise\nexceptions if you attempt to use an older version.</p>\n<p>If you’re currently unable to upgrade your copy of <code class=\"docutils literal notranslate\">MySQLdb</code> to meet\nthis requirement, a separate, backwards-compatible backend, called\n“mysql_old”, has been added to Django. To use this backend, change\nthe <code class=\"docutils literal notranslate\">DATABASE_ENGINE</code> setting in your Django settings file from this:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code>DATABASE_ENGINE <span class=\"o\">=</span> <span class=\"s2\">&quot;mysql&quot;</span>\n</code></pre></div>\n<p>to this:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code>DATABASE_ENGINE <span class=\"o\">=</span> <span class=\"s2\">&quot;mysql_old&quot;</span>\n</code></pre></div>\n<p>However, we strongly encourage MySQL users to upgrade to a more recent\nversion of <code class=\"docutils literal notranslate\">MySQLdb</code> as soon as possible, The “mysql_old” backend is\nprovided only to ease this transition, and is considered deprecated;\naside from any necessary security fixes, it will not be actively\nmaintained, and it will be removed in a future release of Django.</p>\n<p>Also, note that some features, like the new <code class=\"docutils literal notranslate\">DATABASE_OPTIONS</code>\nsetting (see the <a class=\"reference internal\" href=\"/en/dev/ref/databases/\"><span class=\"doc\">databases documentation</span></a> for details),\nare only available on the “mysql” backend, and will not be made available for\n“mysql_old”.</p>\n</section>\n<section id=\"database-constraint-names-changed\">\n<h3>Database constraint names changed<a class=\"heading-anchor\" href=\"#database-constraint-names-changed\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>The format of the constraint names Django generates for foreign key\nreferences have changed slightly. These names are generally only used\nwhen it is not possible to put the reference directly on the affected\ncolumn, so they are not always visible.</p>\n<p>The effect of this change is that running <code class=\"docutils literal notranslate\">manage.py reset</code> and\nsimilar commands against an existing database may generate SQL with\nthe new form of constraint name, while the database itself contains\nconstraints named in the old form; this will cause the database server\nto raise an error message about modifying nonexistent constraints.</p>\n<p>If you need to work around this, there are two methods available:</p>\n<ol class=\"arabic simple\">\n<li><p>Redirect the output of <code class=\"docutils literal notranslate\">manage.py</code> to a file, and edit the\ngenerated SQL to use the correct constraint names before\nexecuting it.</p></li>\n<li><p>Examine the output of <code class=\"docutils literal notranslate\">manage.py sqlall</code> to see the new-style\nconstraint names, and use that as a guide to rename existing\nconstraints in your database.</p></li>\n</ol>\n</section>\n<section id=\"name-changes-in-manage-py\">\n<h3>Name changes in <code class=\"docutils literal notranslate\">manage.py</code><a class=\"heading-anchor\" href=\"#name-changes-in-manage-py\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>A few of the options to <code class=\"docutils literal notranslate\">manage.py</code> have changed with the addition of fixture\nsupport:</p>\n<ul class=\"simple\">\n<li><p>There are new <code class=\"docutils literal notranslate\">dumpdata</code> and <code class=\"docutils literal notranslate\">loaddata</code> commands which, as\nyou might expect, will dump and load data to/from the\ndatabase. These commands can operate against any of Django’s\nsupported serialization formats.</p></li>\n<li><p>The <code class=\"docutils literal notranslate\">sqlinitialdata</code> command has been renamed to <code class=\"docutils literal notranslate\">sqlcustom</code> to\nemphasize that <code class=\"docutils literal notranslate\">loaddata</code> should be used for data (and <code class=\"docutils literal notranslate\">sqlcustom</code> for\nother custom SQL – views, stored procedures, etc.).</p></li>\n<li><p>The vestigial <code class=\"docutils literal notranslate\">install</code> command has been removed. Use <code class=\"docutils literal notranslate\">syncdb</code>.</p></li>\n</ul>\n</section>\n<section id=\"backslash-escaping-changed\">\n<h3>Backslash escaping changed<a class=\"heading-anchor\" href=\"#backslash-escaping-changed\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>The Django database API now escapes backslashes given as query parameters. If\nyou have any database API code that matches backslashes, and it was working\nbefore (despite the lack of escaping), you’ll have to change your code to\n“unescape” the slashes one level.</p>\n<p>For example, this used to work:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"c1\"># Find text containing a single backslash</span>\nMyModel<span class=\"o\">.</span>objects<span class=\"o\">.</span>filter<span class=\"p\">(</span>text__contains<span class=\"o\">=</span><span class=\"s2\">&quot;</span><span class=\"se\">\\\\\\\\</span><span class=\"s2\">&quot;</span><span class=\"p\">)</span>\n</code></pre></div>\n<p>The above is now incorrect, and should be rewritten as:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"c1\"># Find text containing a single backslash</span>\nMyModel<span class=\"o\">.</span>objects<span class=\"o\">.</span>filter<span class=\"p\">(</span>text__contains<span class=\"o\">=</span><span class=\"s2\">&quot;</span><span class=\"se\">\\\\</span><span class=\"s2\">&quot;</span><span class=\"p\">)</span>\n</code></pre></div>\n</section>\n<section id=\"removed-enable-psyco-setting\">\n<h3>Removed ENABLE_PSYCO setting<a class=\"heading-anchor\" href=\"#removed-enable-psyco-setting\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>The <code class=\"docutils literal notranslate\">ENABLE_PSYCO</code> setting no longer exists. If your settings file includes\n<code class=\"docutils literal notranslate\">ENABLE_PSYCO</code> it will have no effect; to use <a class=\"reference external\" href=\"https://psyco.sourceforge.net/\">Psyco</a>, we recommend\nwriting a middleware class to activate it.</p>\n</section>\n</section>\n<section id=\"what-s-new-in-0-96\">\n<h2>What’s new in 0.96?<a class=\"heading-anchor\" href=\"#what-s-new-in-0-96\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>This revision represents over a thousand source commits and over four hundred\nbug fixes, so we can’t possibly catalog all the changes. Here, we describe the\nmost notable changes in this release.</p>\n<section id=\"new-forms-library\">\n<h3>New forms library<a class=\"heading-anchor\" href=\"#new-forms-library\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p><code class=\"docutils literal notranslate\">django.newforms</code> is Django’s new form-handling library. It’s a\nreplacement for <code class=\"docutils literal notranslate\">django.forms</code>, the old form/manipulator/validation\nframework. Both APIs are available in 0.96, but over the next two\nreleases we plan to switch completely to the new forms system, and\ndeprecate and remove the old system.</p>\n<p>There are three elements to this transition:</p>\n<ul>\n<li><p>We’ve copied the current <code class=\"docutils literal notranslate\">django.forms</code> to\n<code class=\"docutils literal notranslate\">django.oldforms</code>. This allows you to upgrade your code <em>now</em>\nrather than waiting for the backwards-incompatible change and\nrushing to fix your code after the fact. Just change your\nimport statements like this:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"kn\">from</span> <span class=\"nn\">django</span> <span class=\"kn\">import</span> forms  <span class=\"c1\"># 0.95-style</span>\n<span class=\"kn\">from</span> <span class=\"nn\">django</span> <span class=\"kn\">import</span> oldforms <span class=\"k\">as</span> forms  <span class=\"c1\"># 0.96-style</span>\n</code></pre></div>\n</li>\n<li><p>The next official release of Django will move the current\n<code class=\"docutils literal notranslate\">django.newforms</code> to <code class=\"docutils literal notranslate\">django.forms</code>. This will be a\nbackwards-incompatible change, and anyone still using the old\nversion of <code class=\"docutils literal notranslate\">django.forms</code> at that time will need to change\ntheir import statements as described above.</p></li>\n<li><p>The next release after that will completely remove\n<code class=\"docutils literal notranslate\">django.oldforms</code>.</p></li>\n</ul>\n<p>Although the <code class=\"docutils literal notranslate\">newforms</code> library will continue to evolve, it’s ready for use\nfor most common cases. We recommend that anyone new to form handling skip the\nold forms system and start with the new.</p>\n<p>For more information about <code class=\"docutils literal notranslate\">django.newforms</code>, read the <a class=\"reference internal\" href=\"/en/dev/topics/forms/\"><span class=\"doc\">newforms\ndocumentation</span></a>.</p>\n</section>\n<section id=\"urlconf-improvements\">\n<h3>URLconf improvements<a class=\"heading-anchor\" href=\"#urlconf-improvements\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>You can now use any callable as the callback in URLconfs (previously, only\nstrings that referred to callables were allowed). This allows a much more\nnatural use of URLconfs. For example, this URLconf:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"kn\">from</span> <span class=\"nn\">django.conf.urls.defaults</span> <span class=\"kn\">import</span> <span class=\"o\">*</span>\n\nurlpatterns <span class=\"o\">=</span> patterns<span class=\"p\">(</span><span class=\"s2\">&quot;&quot;</span><span class=\"p\">,</span> <span class=\"p\">(</span><span class=\"s2\">&quot;^myview/$&quot;</span><span class=\"p\">,</span> <span class=\"s2\">&quot;mysite.myapp.views.myview&quot;</span><span class=\"p\">))</span>\n</code></pre></div>\n<p>can now be rewritten as:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"kn\">from</span> <span class=\"nn\">django.conf.urls.defaults</span> <span class=\"kn\">import</span> <span class=\"o\">*</span>\n<span class=\"kn\">from</span> <span class=\"nn\">mysite.myapp.views</span> <span class=\"kn\">import</span> myview\n\nurlpatterns <span class=\"o\">=</span> patterns<span class=\"p\">(</span><span class=\"s2\">&quot;&quot;</span><span class=\"p\">,</span> <span class=\"p\">(</span><span class=\"s2\">&quot;^myview/$&quot;</span><span class=\"p\">,</span> myview<span class=\"p\">))</span>\n</code></pre></div>\n<p>One useful application of this can be seen when using decorators; this\nchange allows you to apply decorators to views <em>in your\nURLconf</em>. Thus, you can make a generic view require login very\neasily:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"kn\">from</span> <span class=\"nn\">django.conf.urls.defaults</span> <span class=\"kn\">import</span> <span class=\"o\">*</span>\n<span class=\"kn\">from</span> <span class=\"nn\">django.contrib.auth.decorators</span> <span class=\"kn\">import</span> login_required\n<span class=\"kn\">from</span> <span class=\"nn\">django.views.generic.list_detail</span> <span class=\"kn\">import</span> object_list\n<span class=\"kn\">from</span> <span class=\"nn\">mysite.myapp.models</span> <span class=\"kn\">import</span> MyModel\n\ninfo <span class=\"o\">=</span> <span class=\"p\">{</span>\n    <span class=\"s2\">&quot;queryset&quot;</span><span class=\"p\">:</span> MyModel<span class=\"o\">.</span>objects<span class=\"o\">.</span>all<span class=\"p\">(),</span>\n<span class=\"p\">}</span>\n\nurlpatterns <span class=\"o\">=</span> patterns<span class=\"p\">(</span><span class=\"s2\">&quot;&quot;</span><span class=\"p\">,</span> <span class=\"p\">(</span><span class=\"s2\">&quot;^myview/$&quot;</span><span class=\"p\">,</span> login_required<span class=\"p\">(</span>object_list<span class=\"p\">),</span> info<span class=\"p\">))</span>\n</code></pre></div>\n<p>Note that both syntaxes (strings and callables) are valid, and will continue to\nbe valid for the foreseeable future.</p>\n</section>\n<section id=\"the-test-framework\">\n<h3>The test framework<a class=\"heading-anchor\" href=\"#the-test-framework\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>Django now includes a test framework so you can start transmuting fear into\nboredom (with apologies to Kent Beck). You can write tests based on\n<a class=\"reference external\" href=\"https://docs.python.org/3/library/doctest.html#module-doctest\" title=\"(in Python v3.14)\"><code class=\"xref py py-mod docutils literal notranslate\">doctest</code></a> or <a class=\"reference external\" href=\"https://docs.python.org/3/library/unittest.html#module-unittest\" title=\"(in Python v3.14)\"><code class=\"xref py py-mod docutils literal notranslate\">unittest</code></a> and test your views with a simple test\nclient.</p>\n<p>There is also new support for “fixtures” – initial data, stored in any of the\nsupported <a class=\"reference internal\" href=\"/en/dev/topics/serialization/\"><span class=\"doc\">serialization formats</span></a>, that will be\nloaded into your database at the start of your tests. This makes testing with\nreal data much easier.</p>\n<p>See <a class=\"reference internal\" href=\"/en/dev/topics/testing/\"><span class=\"doc\">the testing documentation</span></a> for the full\ndetails.</p>\n</section>\n<section id=\"improvements-to-the-admin-interface\">\n<h3>Improvements to the admin interface<a class=\"heading-anchor\" href=\"#improvements-to-the-admin-interface\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>A small change, but a very nice one: dedicated views for adding and\nupdating users have been added to the admin interface, so you no\nlonger need to worry about working with hashed passwords in the admin.</p>\n</section>\n</section>\n<section id=\"thanks\">\n<h2>Thanks<a class=\"heading-anchor\" href=\"#thanks\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>Since 0.95, a number of people have stepped forward and taken a major\nnew role in Django’s development. We’d like to thank these people for\nall their hard work:</p>\n<ul class=\"simple\">\n<li><p>Russell Keith-Magee and Malcolm Tredinnick for their major code\ncontributions. This release wouldn’t have been possible without them.</p></li>\n<li><p>Our new release manager, James Bennett, for his work in getting out\n0.95.1, 0.96, and (hopefully) future release.</p></li>\n<li><p>Our ticket managers Chris Beaven (aka SmileyChris), Simon Greenhill,\nMichael Radziej, and Gary Wilson. They agreed to take on the monumental\ntask of wrangling our tickets into nicely cataloged submission. Figuring\nout what to work on is now about a million times easier; thanks again,\nguys.</p></li>\n<li><p>Everyone who submitted a bug report, patch or ticket comment. We can’t\npossibly thank everyone by name – over 200 developers submitted patches\nthat went into 0.96 – but everyone who’s contributed to Django is listed\nin <a class=\"extlink-source reference external\" href=\"https://github.com/django/django/blob/main/AUTHORS\">AUTHORS</a>.</p></li>\n</ul>\n</section>","rootId":"django-version-0-96-release-notes","toc":[{"title":"Backwards-incompatible changes","anchor":"backwards-incompatible-changes","children":[{"title":"MySQLdb version requirement","anchor":"mysqldb-version-requirement","children":[]},{"title":"Database constraint names changed","anchor":"database-constraint-names-changed","children":[]},{"title":"Name changes in manage.py","anchor":"name-changes-in-manage-py","children":[]},{"title":"Backslash escaping changed","anchor":"backslash-escaping-changed","children":[]},{"title":"Removed ENABLE_PSYCO setting","anchor":"removed-enable-psyco-setting","children":[]}]},{"title":"What’s new in 0.96?","anchor":"what-s-new-in-0-96","children":[{"title":"New forms library","anchor":"new-forms-library","children":[]},{"title":"URLconf improvements","anchor":"urlconf-improvements","children":[]},{"title":"The test framework","anchor":"the-test-framework","children":[]},{"title":"Improvements to the admin interface","anchor":"improvements-to-the-admin-interface","children":[]}]},{"title":"Thanks","anchor":"thanks","children":[]}],"breadcrumbs":[{"docname":"releases/index","title":"Release notes","url":"/en/dev/releases/"}],"prev":{"docname":"releases/1.0-porting-guide","title":"Porting your apps from Django 0.96 to 1.0","url":"/en/dev/releases/1.0-porting-guide/"},"next":{"docname":"releases/0.95","title":"Django version 0.95 release notes","url":"/en/dev/releases/0.95/"},"formats":{"html":"/en/dev/releases/0.96/","markdown":"/en/dev/releases/0.96.md","json":"/en/dev/releases/0.96.json"},"source":"https://github.com/django/django/blob/main/docs/releases/0.96.txt","official":"https://docs.djangoproject.com/en/dev/releases/0.96/","inVersions":["dev","6.1","6.0","5.2","5.1","5.0","4.2","4.1","4.0","3.2","3.1","3.0","2.2","2.1","2.0","1.11","1.10","1.9","1.8"],"inLocales":["en"]}