= Backwards-incompatible changes = As Django is still in pre-1.0 mode, we haven't yet committed to maintaining backwards compatibility in any APIs. Although we're keeping such changes to a minimum, Django developers should be acutely aware of these changes. Of course, once we reach 1.0, we'll be strongly committed to backward compatibility. This page lists all backwards-incompatible changes to Django since the 0.95 release. For changes prior to 0.95, see the OlderBackwardsIncompatibleChanges page. == Table of Contents == Changes made after Django [source:/django/tags/releases/0.95 0.95]: * [3512] August 2, 2006: [http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#Databaseconstraintnameschanged Database constraint names changed] * [3552] August 11, 2006: [http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#Backslashescapingchanged Backslash escaping changed] * [3877] September 27, 2006: [http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#RemovedENABLE_PSYCOsetting Removed ENABLE_PSYCO setting] * [4724] + [4767] March 14, 2007: [http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#EnforcingMySQLdbversion Enforcing MySQLdb version] Changes made after Django [source:/django/tags/releases/0.96 0.96]: * [4885] March 31, 2007: [http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#Changedspacelesstemplatetagtoremoveallspaces Changed 'spaceless' template tag to remove all spaces] * [4954] April 8, 2007: [http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#Renamedlocalflavor.usatolocalflavor.us Renamed `localflavour.usa` to `localflavor.us`] * [4985] April 9, 2007: [http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#RemovedLazyDate Removed `LazyDate`] * [5042] April 20, 2007: [http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#MySQLIntrospectionChange MySQL Introspection change] * [5072] April 25, 2007: [http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#LOGIN_URLisnowasetting Login URL is now a setting] * [5152] May 5, 2007: [http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#TestClientloginmethodchanged Test Client `login()` method changed] * [5172] May 8, 2007: [http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#Genericrelationshavemoved Generic relations have moved] * [5237] May 14, 2007: [http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#Newforms:clean_datachangedtocleaned_data Newforms: clean_data changed to cleaned_data] * [5302] May 20, 2007: [http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#RenamedFloatFieldtoDecimalField Renamed FloatField to DecimalField] * [5516] June 22, 2007: [http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#Urlpatternsnowcached Urlpatterns now cached] * [5609] July 4, 2007: [http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#Unicodemerge Unicode merge] * [5654] July 12, 2007: [http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#Changed__init__parametersinsyndicationframeworksFeedclass Changed __init__() parameters in syndication framework's Feed class] * [5708] July 15, 2007: [http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#POfilesmustbeUTF-8encoded PO files must be UTF-8 encoded] * [5752] July 23, 2007: [http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#Addedinteractiveargumenttorun_tests Added `interactive` argument to `run_tests`] * [5769] July 28, 2007: [http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#Changedformatforfirstargumenttorun_tests Changed format for first argument to `run_tests` ] * [5819] August 6, 2007: [http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#Minorchangetonewformsargumentsanddatadictionaryhandling Minor change to newforms arguments and data dictionary handling ] * [5898] August 16, 2007: [http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges#Changestomanagement.pycommands Changes to management.py commands ] == Database constraint names changed == As of [3512], the format of the constraint names Django generates for foreign key references changed slightly. These names are only used sometimes, when it is not possible to put the reference directly on the affected column, so this is not always visible. The effect of this change is that {{{manage.py reset app_name}}} and similar commands may generate SQL with invalid constraint names and thus generate an error when run against the database (the database server will complain about the constraint not existing). To fix this, you will need to tweak the output of {{{manage.py sqlreset app_name}}} to match the correct constraint names and pass the results to the database server manually. == Backslash escaping changed == As of [3552], the Django database API now escapes backslashes given as query parameters. If you have any database API code that match backslashes, and it was working before (despite the broken escaping), you'll have to change your code to "unescape" the slashes one level. For example, this used to work: {{{ #!python # Code that matches a single backslash MyModel.objects.filter(text__contains='\\\\') }}} But it should be rewritten as this: {{{ #!python # Code that matches a single backslash MyModel.objects.filter(text__contains='\\') }}} == Removed ENABLE_PSYCO setting == As of [3877], the {{{ENABLE_PSYCO}}} setting no longer exists. If your settings file includes {{{ENABLE_PSYCO}}}, nothing will break per se, but it just won't do anything. If you want to use [http://psyco.sourceforge.net/ Psyco] with Django, write some [wiki:PsycoMiddleware custom middleware that activates Psyco]. == Enforcing MySQLdb version == As of [4724], Django raises an error if you try to use the MySQL backend with a {{{MySQLdb}}} (MySQL Python module) version earlier than 1.2.1p2. There were significant, production-related bugs in earlier versions, so we have upgraded the minimum requirement. In [4767], we added a {{{mysql_old}}} backend, which is identical to the {{{mysql}}} backend prior to the change in [4724]. You can use this backend if upgrading the {{{MySQLdb}}} module is not immediately possible. However, the {{{mysql_old}}} backend is deprecated, and we will not continue developing it. == Changed 'spaceless' template tag to remove all spaces == As of [4885], the {{{spaceless}}} template tag removes *all* spaces between HTML tags instead of preserving a single space. For example, for this template code: {{{ {% spaceless %}
{% endspaceless %} }}} The old output was this: {{{ }}} The new output is this: {{{ }}} As always, {{{spaceless}}} only affects space *between* HTML tags, not space within HTML tags or space in plain text. == Renamed `localflavor.usa` to `localflavor.us` == As of [4954], `localflavor.usa` has been renamed `localflavor.us`. This change was made to match the naming scheme of other local flavors. == Removed `LazyDate` == The `LazyDate` helper class was removed in [4985]. Default field values and query arguments can both be callable objects, so instances of `LazyDate` can be replaced with a reference to a callable that evaluates to the current date (i.e., `datetime.now`). For example, the following model using `LazyDate`: {{{ #!python class Article(models.Model): title = models.CharField(maxlength=100) published = models.DateField(default=LazyDate()) }}} can be redefined as: {{{ #!python from datetime import datetime class Article(models.Model): title = models.CharField(maxlength=100) published = models.DateField(default=datetime.now) }}} Note that the new model definition refers to the name of the `datetime.now()` function, but doesn't actually call the function. The call to `datetime.now()` is deferred until the value is actually required (i.e., when the model is saved). == MySQL Introspection Change == In [5042] a small change was made in the `mysql` backend in how it interpreted `CHAR(n)` columns in the database. They are now mapped to Django's !CharField class, rather than the previous !TextField. See ticket #4048 for details. This will only be apparent if you introspect a database table using 0.96 and again using [5042] or later: you will have slightly different Django models generated if there are any `CHAR(n)` columns. However, no real code changes should be necessary. == LOGIN_URL is now a setting == In [5072], we moved the LOGIN_URL constant from django.contrib.auth into the settings module. This was part of a broader change to make these URLs (including logout and post-login redirect) configurable. Code that previously read {{{ #!python from django.contrib.auth import LOGIN_URL }}} should be changed to refer to `settings.LOGIN_URL` instead. == Test Client `login()` method changed == The implementation of `django.test.Client.login()` operated as a wrapper around a series of GET and POST calls accessing a nominated login URL. This approach was fragile, and tightly bound to specific templates and login mechanims. In [5152], we changed the implementation of the `login()` method on the test Client. `login()` now accepts a list of credentials, and exercises these credentials directly on the cookies and Session objects of a site, without accessing or using a login page. This breaks the dependence on specific template formatting, and enables the login mechanism to work with any authentication backend, and any login decorator. Existing uses of `login()`, e.g.: {{{ #!python c = Client() c.login('/path/to/login','myuser','mypassword') }}} should be modified to read: {{{ #!python c = Client() c.login(username='myuser', password='mypassword') }}} The keyword arguments `username` and `password` *must* be given explicitly. If an alternate authentication scheme is in use, the credentials required by that scheme can be provided instead of `username` and `password`. == Generic relations have moved == In [5172], the various generic relation classes (!GenericForeignKey and !GenericRelation) have moved into the django.contrib.contenttypes module. This is because they will not work if contenttypes is not an installed app. Making the import path reflect the dependency should help remind people of this. Code using generic relations should change from saying things like {{{ #!python generic_field = models.GenericRelation(SomeOtherModel) }}} to using {{{ #!python from django.contrib.contenttypes import generic ... generic_field = generic.GenericRelation(SomeOtherModel) }}} No database changes or other model changes are required. == Newforms: clean_data changed to cleaned_data == If you are accessing form data that has been cleaned in newforms, you could previously use the {{{clean_data}}} attribute on the form. In [5237], this was changed to {{{cleaned_data}}} to avoid a name-clash (see [5231] for why the change was necessary). You will need to do a search-and-replace in your form code and replace clean_data with cleaned_data everywhere. == Renamed !FloatField to !DecimalField == In [5302], we fixed a slight inconsistency in Django's model field system. Previously, we had a !FloatField that had a maximum number of digits and decimal places. However, although it was stored in the database as a fixed precision value, it was stored in Python as a float, so precision was lost due to rounding problems. We now have a !DecimalField class that is the same as the old !FloatField, except that it is represented in Python by a Decimal type (and still stored as a fixed precision value in the database). We have also introduced a proper !FloatField that is represented as a float in Python and stored as a "double precision" field in the database. To make your code compatible with these changes, you need to change all occurrences of !FloatField in your code to !DecimalField. You only need to rename the field type -- all the parameters are the same. So change {{{ #!python class MyModel(models.Model): ... field_name = models.FloatField(max_digits=10, decimal_places=3) # old code! }}} to {{{ #!python class MyModel(models.Model): ... field_name = models.DecimalField(max_digits=10, decimal_places=3) # new code! }}} If you forget to make this change, you will see errors about !FloatField not taking a max_digits attribute in {{{__init__}}}, since the new !FloatField takes no precision-related arguments. If you are using MySQL or PostgreSQL, there are '''no further changes needed'''. The database column types for !DecimalField are the same as for the old !FloatField. If you are using SQLite, you need to force the database to view the appropriate columns as decimal types, rather than floats. To do this, follow this procedure for every application you have that contains a !DecimalField. Do this ''after'' you have made the change to using !DecimalField in your code and updated the Django code. '''Warning: Back up your database first! ''' For SQLite, this means making a copy of the single file that stores the database (the name of that file is the DATABASE_NAME in your settings.py file). For every application using a !DecimalField, do the following. We will use applications called {{{some_app}}} and {{{another_app}}} in this example {{{ ./manage.py dumpdata --format=xml some_app another_app > data-dump.xml ./manage.py reset some_app another_app ./manage.py loaddata data-dump.xml }}} Notes: 1. It is important that you remember to use XML format in the first step of this process. We are exploiting a feature of the XML data dumps that makes porting floats to decimals with SQLite possible. 2. In the second step you will be asked to confirm that you are prepared to lose the data for the application(s) in question. We restore this data in the third step, of course. 3. !DecimalField is not used in any of the apps shipped with Django prior to this change being made, so you do not need to worry about performing this procedure for any of the standard Django applications. If something goes wrong in the above process, just copy your backed up database file over the top of the original file and start again. == Urlpatterns now cached == In [5516], a speed improvement was made for reverse URL lookups, particularly. Part of this involved caching information that was unlikely to change: the {{{urlpatterns()}}} contents. If you were somehow relying on the fact that you could change your {{{urls.py}}} files and not have to restart Django, you can no longer do that. Edits require a restart. == Unicode merge == In [5609], the UnicodeBranch was merged into trunk. For any installations using ASCII data, this is fully backwards compatible. If you were using non-ASCII data, Django would have behaved unreliably in some cases previously and so backwards-compatibility was neither completely possible nor desirable. However, some people may have been able to get by with non-ASCII data successfully. They might now experience some different errors to previously. Porting code to work correctly with non-ASCII data is fairly simple. Follow [http://code.djangoproject.com/wiki/UnicodeBranch#PortingApplicationsTheQuickChecklist this checklist] for fastest results. == Changed {{{__init__()}}} parameters in syndication framework's Feed class == In [5654], we changed the {{{Feed}}} class' {{{__init__()}}} method to take an {{{HttpRequest}}} object as its second parameter, instead of the feed's URL. This allows the syndication framework to work without requiring the sites framework. This only affects people who have subclassed {{{Feed}}} and overridden the {{{__init__()}}} method, and people who have called {{{Feed.__init__()}}} directly from their own code. {{{Feed.__init__()}}} had not been documented. == PO files must be UTF-8 encoded == In [5708] we added the ability to use non-ASCII strings as input to the translation framework. This was a frequently requested feature by developers whose original strings were not in English. A consequence of this change is that we have to specify the encoding of the PO files (the files used by translators to create translations). Following common practice in other projects, we have chosen UTF-8 as the encoding. This change only affects third-party developers who are using Django's make-messages.py script to extract their strings for their own PO files. The PO files will need to be saved and edited as UTF-8 files and the ''charset'' set correctly in the header of the file. All of Django's core translation files already satisfy this requirement. Note that existing code and message catalogs will not break even if you update your code to [5708] or later. This change only takes effect the next time you run make-messages.py. == Added `interactive` argument to `run_tests` == In [5752] the parameter `interactive` was added to the run_tests method. This parameter allows tests to be added to automated build scripts where interactive questions (such as being asked if it is ok to delete an existing test database) cannot be asked. If you are using the default Django test runner, no change is required. However, if you have defined a custom test runner, you must be able to accept (and handle appropriately) the `interactive` argument to the test runner. For most test runners, the only change required will be to change the call to `create_test_db` to use the keyword argument `autoclobber=not interactive`. == Changed format for first argument to `run_tests` == In [5769], the first argument to `run_tests` was changed to allow end-users to invoke individual tests, rather than requiring that entire applications be tested. This change has no effect on most end users. However, it does require a significant change to the prototype of run_test, which will be backwards incompatible for anyone with a customized test runner. Previously, the first argument to `run_tests` was a list of application modules. These modules would then be searched for test cases. After [5769], the first argument to `run_tests` is a list of test labels. Whereas run_tests was previously given a list of already-loaded applications, it is now the responsibility of the test runner to load the appropriate applications. Anyone with a customized test runner will need to incorporate the appropriate calls to `get_app()` as part of their test runner. == Minor change to newforms arguments and data dictionary handling == In [5819], `FileField` and `ImageField` were added to newforms. Implementing these fields required some subtle changes to the way data binding occurs in newforms. This change will have no effect on most end users. The simple cases for binding forms do not change. However: * The second argument to a form instance is now taken by file data. As a result, if you implicitly relied upon the order of the keyword arguments `auto_id`, `prefix` and `initial` when instantiating forms, your forms will no longer interpret these options correctly. To overcome this problem, explicitly name these keyword arguments - for example, use: {{{ f = ContactForm(data, auto_id=True) }}} rather than: {{{ f = ContactForm(data, True) }}} * A `files` argument was added to the prototype for `value_from_datadict()` to allow for the processing of file data. If you have written any custom widgets that provide their own data dictionary handling, you will need to modify those widgets to accomodate the new argument. If your custom widget has no need to handle file data, all you will need to do is change the method definition from: {{{ def value_from_datadict(self, data, name): }}} to: {{{ def value_from_datadict(self, data, files, name): }}} == Changes to management.py commands == In [5898], we refactored `management.py`. This was partially to clean up a 1700 line file, but also to allow user applications to register commands for use in `management.py`. As a result, any calls to management services in your code will need to be adjusted. For example, if you have some test code that calls `flush` and `load_data`: {{{ >>> from django.core import management >>> management.flush(verbosity=0, interactive=False) >>> management.load_data(['test_data'], verbosity=0) }}} You will need to change this code to read: {{{ >>> from django.core import management >>> management.call_command('flush', verbosity=0, interactive=False) >>> management.call_command('load_data', ['test_data'], verbosity=0) }}}