Ticket #8438: porting.txt

File porting.txt, 19.0 KB (added by daonb <bennydaon@…>, 16 years ago)

It's a start...

Line 
1========================
2Porting 0.96 apps to 1.0
3========================
4
5Version 1.0 breaks compatibility with 0.96. This guide will help you port 0.96 projects and apps to 1.0. The first part of this document includes the common changes needed to run with 1.0. If after going through the first part your code still breaks, check the section `Other Changes`_ for a list of all compatibility issues.
6
7Common Changes
8==============
9
10models
11------
12 * The ``maxlength`` argument for model fields was replaced with ``max_length``.
13 * Rename your model's ``__str__`` function to ``__unicode__`` and use unicode strings: ``u'foo'`` instead of ``'foo'``.
14 * All of the Admin definitions moved from the ``Model`` definition to the new ``AdminModel`` class stored in an admin.py file. You can read about the new class in `The Django admin site`_. Here is an example with some of the most common changes::
15
16 # 0.96:
17 class Author(models.Model):
18 first_name = models.CharField(maxlength=30)
19 last_name = models.CharField(maxlength=30)
20 slug = models.CharField(maxlength=60)
21
22 class Admin:
23 fields = (
24 ('full name', {'fields': ('first_name','last_name'), 'classes': 'collapse wide'}),
25 )
26 js = (
27 "/static/my_code.js",
28 )
29
30 class Book(models.Model):
31 author = models.ForeignKey(Author, edit_inline=models.TABULAR)
32 title = models.CharField(maxlength=100)
33
34 # 1.0:
35 class Author(models.Model):
36 first_name = models.CharField(max_length=30)
37 last_name = models.CharField(max_length=30)
38
39 class Book(models.Model):
40 author = models.ForeignKey(Author)
41 title = models.CharField(max_length=100)
42
43 # admin.py
44 from django.contrib import admin
45 from models import Book, Author
46
47 class BookInline(admin.TabularInline):
48 model = Book
49 extra = 3
50
51 class AuthorAdmin(admin.ModelAdmin):
52 inlines = [BookInline]
53 fieldsets = (
54 ('full name', {'fields': ('first_name','last_name'), 'classes': ('collapse', 'wide')}),
55 )
56
57 class Media:
58 js = (
59 "/static/my_code.js",
60 )
61 admin.site.register(Author, AuthorAdmin)
62
63 NOTE: There is a script_ that your models.py and automatically outputs the corresponding source code for admin.py.
64
65.. _The Django admin site: http://www.djangoproject.com/documentation/admin/
66.. _script: http://www.djangosnippets.org/snippets/603/
67
68
69Templates
70---------
71 * By default the templating system automatically escapes the output of every variable tag. To learn more take a look at `automatic html escaping`_. To disable auto-escaping for an individual variable, use the safe filter::
72
73 This will be escaped: {{ data }}
74 This will not be escaped: {{ data|safe }}
75
76 To disable auto-escaping for an entire template, wrap the template (or just a particular section of the template) in the autoescape tag, like so::
77
78 {% autoescape off %}
79 ... normal template content here ...
80 {% endautoescape %}
81
82.. _automatic html escaping: http://www.djangoproject.com/documentation/templates/#automatic-html-escaping
83
84urls
85----
86 * If you are using the Admin you need to update your root ``urls.py``::
87
88 # 0.96:
89 from django.conf.urls.defaults import *
90
91 urlpatterns = patterns('',
92 (r'^admin/', include('django.contrib.admin.urls')),
93 )
94
95 # 1.0:
96 from django.conf.urls.defaults import *
97 from django.contrib import admin
98
99 # automatically load the INSTALLED_APPS admin.py modules
100 admin.autodiscover()
101
102 urlpatterns = patterns('',
103 (r'^admin/doc/', include('django.contrib.admindocs.urls')),
104 (r'^admin/(.*)', admin.site.root),
105 )
106
107views
108-----
109 * With 1.0 ``newforms`` were renamed ``forms`` and ``oldforms`` were removed. If you are already using new forms all you have to do is change your import statement::
110
111 # 0.96
112 from django import newforms as forms
113
114 # 1.0
115 from django import forms
116
117 If you are using the old forms, you will have to rewrite your forms - a good place to start is the `forms documentation`_.
118
119.. _forms documentation: http://www.djangoproject.com/documentation/forms/
120
121 * In 0.96 uploaded files -- that is, entries in ``request.FILES`` -- were represented by simple dictionaries with a few well-known keys. Uploaded files are now represented by an `UploadedFile object`_, and thus the file's information is accessible through object attributes. Thus, given ``f = request.FILES['file_field_name']``:
122
123 ===================== =====================
124 0.96 1.0
125 ===================== =====================
126 ``f['content']`` ``f.read()``
127 ``f['filename']`` ``f.name``
128 ``f['content-type']`` ``f.content_type``
129 ===================== =====================
130
131.. _UploadedFile object: http://www.djangoproject.com/documentation/upload_handling/#uploaded-file-objects
132
133Signals
134-------
135 * All handlers now must be declared as accepting ``**kwargs``.
136 * Signals are now instances of ``django.dispatch.Signal`` instead of anonymous objects.
137 * Connecting, disconnecting, and sending signals are done via methods on the ``Signal`` object instead of through module methods in ``django.dispatch.dispatcher``. The module-level methods are deprecated.
138 * The ``Anonymous`` and ``Any`` sender options no longer exist. You can still receive signals sent by any sender by using ``sender=None``
139 * So, a quick summary of the code changes you'd need to make:
140
141 ========================================================= ================================================
142 0.96 1.0
143 ========================================================= ================================================
144 ``def my_handler(sender)`` ``def my_handler(sender, **kwargs)``
145 ``my_signal = object()`` ``my_signal = django.dispatch.Signal()``
146 ``dispatcher.connect(my_handler, my_signal)`` ``my_signal.connect(my_handler)``
147 ``dispatcher.send(my_signal, sender)`` ``my_signal.send(sender)``
148 ``dispatcher.connect(my_handler, my_signal, sender=Any)`` ``my_signal.connect(my_handler, sender=None)``
149 ========================================================= ================================================
150
151.. Other Changes_:
152
153Other Changes
154=============
155 * The spaceless template tag removes *all* spaces between HTML tags instead of preserving a single space
156 * ``localflavor.usa`` has been renamed ``localflavor.us``. This change was made to match the naming scheme of other local flavors.
157 * Renamed ``SeesionBase.get_new_session_key() to ``_get_new_session_key()`` and ``removed get_new_session_object()``.
158 * Settings exception changed. ``EnvironmentError`` was split into an ``ImportError`` raised when Django fails to find the settings module and ``RuntimeError`` when you try to reconfigure settings after having already used them
159 * The models manager now returns a ``MultipleObjectsReturned`` exception instead of ``AssertionError``::
160
161 #0.96
162 try:
163 Model.objects.get(...)
164 except AssertionError:
165 ...
166
167 #1.0
168 try:
169 Model.objects.get(...)
170 except Model.MultipleObjectsReturned:
171 ...
172
173 * The manager option to class ``Admin`` no longer exists. In favor of this option, a ``ModelAdmin`` class may now define a queryset method::
174
175 class BookAdmin(admin.ModelAdmin):
176 def queryset(self, request):
177 """
178 Filter based on the current user.
179 """
180 return self.model._default_manager.filter(user=request.user)
181
182 * ``django.views.i18n.set_language`` requires a POST request. Previously, a GET request was used. The old behavior meant that state (the locale used to display the site) could be changed by a GET request, which is against the HTTP specification's recommendations.
183 Code calling this view must ensure that a POST request is now made, instead of a GET. This means you can no longer use a link to access the view, but must use a form submission of some kind (e.g. a button).
184 * ``django.http.HttpResponse.headers`` has been renamed to ``_headers`` and ``HttpResponse`` now supports containment checking directly::
185
186 # 0.96
187 if header in response.headers:
188
189 # 1.0
190 if header in response:
191
192 * The generic relation classes - ``GenericForeignKey`` and ``GenericRelation`` - have moved into the ``django.contrib.contenttypes module``::
193
194 #0.96
195 generic_field = models.GenericRelation(SomeOtherModel)
196
197 #1.0
198 from django.contrib.contenttypes import generic
199 ...
200 generic_field = generic.GenericRelation(SomeOtherModel)
201
202 * ``_()`` no longer in builtins so if you were previously relying on ``_()`` always being present, you should now explicitly import ``ugettext`` or ``ugettext_lazy``, if appropriate, and alias it to _ yourself::
203
204 from django.utils.translation import ugettext as _
205
206 * The ``LazyDate`` helper class was removed. Default field values and query arguments can both be callable objects, so instances of ``LazyDate`` can be replaced with a reference to datetime.now::
207
208 # 0.96
209 class Article(models.Model):
210 title = models.CharField(maxlength=100)
211 published = models.DateField(default=LazyDate())
212
213 # 1.0
214 from datetime import datetime
215 class Article(models.Model):
216 title = models.CharField(maxlength=100)
217 published = models.DateField(default=datetime.now)
218
219 * The ``LOGIN_URL`` constant moved from ``django.contrib.auth`` into the ``settings`` module. Instead of using ``from django.contrib.auth import LOGIN_URL`` refer to ``settings.LOGIN_URL``.
220
221 * Test client login method changed::
222
223 # 0.96
224 from django.test import Client
225 c = Client()
226 c.login('/path/to/login','myuser','mypassword')
227
228 # 1.0
229 ...
230 c.login(username='myuser', password='mypassword')
231
232
233 * Changes to ``django.core.management`` - calls to management services in your code will need to use the ``call_command``. For example, if you have some test code that calls flush and load_data::
234
235 from django.core import management
236 management.flush(verbosity=0, interactive=False)
237 management.load_data(['test_data'], verbosity=0)
238
239 You will need to change this code to read::
240
241 from django.core import management
242 management.call_command('flush', verbosity=0, interactive=False)
243 management.call_command('loaddata', 'test_data', verbosity=0)
244
245 * ``django-admin.py`` and ``manage.py`` now require subcommands to precede options::
246
247 django-admin.py --settings=foo.bar runserver
248
249 changed to::
250
251 django-admin.py runserver --settings=foo.bar
252
253 * Changed ``__init__()`` parameters in syndication framework's Feed class 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.
254 This only affects code that subclass Feed and overrides the ``__init__()`` method, and code that calls ``Feed.__init__()`` directly.
255 * ``django.newforms.forms.SortedDictFromList`` class removed, due to ``django.utils.datastructures.SortedDict`` gaining the ability to be instantiated with a sequence of tuples. Two things need to be done to fix your code:
256
257 1. Use ``django.utils.datastructures.SortedDict`` wherever you were using ``django.newforms.forms.SortedDictFromList``.
258 2. Since ``SortedDict``'s copy method doesn't return a deepcopy as ``SortedDictFromList'``s copy method did, you will need to update your code if you were relying on ``SortedDictFromList.copy`` to return a deepcopy. Do this by using ``copy.deepcopy`` instead of ``SortedDict``'s copy method.
259
260 * Change to ``APPEND_SLASH`` behaviour. In 0.96, if a URL didn't end in a slash or have a period in the final component of it's path, and ``APPEND_SLASH`` was True, Django would redirect to the same URL, but with a slash appended to the end.
261 Now, Django checks to see if the pattern without the trailing slash would be matched by something in your URL patterns. If so, no redirection takes place, because it is assumed you deliberately wanted to catch that pattern.
262 For most people, this won't require any changes. Some people, though, have URL patterns that look like this::
263
264 r'/some_prefix/(.*)$'
265
266 Previously, those patterns would have been redirected to have a trailing slash. If you always want a slash on such URLs, rewrite the pattern as::
267
268 r'/some_prefix/(.*/)$'
269
270 * Introduced ``DecimalField`` and changed ``FloatField`` to proper float - one that takes no ``max_digits``::
271
272 # 0.96
273 class MyModel(models.Model):
274 ...
275 field_name = models.FloatField(max_digits=10, decimal_places=3)
276
277 #1.0
278 class MyModel(models.Model):
279 ...
280 field_name = models.DecimalField(max_digits=10, decimal_places=3)
281
282 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.
283 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``.
284 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.
285 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).
286
287 For every application using a ``DecimalField``, do the following. We will use applications called some_app and another_app in this example::
288
289 ./manage.py dumpdata --format=xml some_app another_app > data-dump.xml
290 ./manage.py reset some_app another_app
291 ./manage.py loaddata data-dump.xml
292
293 Notes:
294
295 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.
296 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.
297 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.
298
299 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.
300 * Almost *all* of the database backend-level functions have been renamed and/or relocated. None of these were documented, but you'll need to change your code if you're using any of these functions:
301
302 ============================================= =========================================================
303 0.96 name/location 1.0 name/location
304 ============================================= =========================================================
305 django.db.backend.get_autoinc_sql django.db.connection.ops.autoinc_sql
306 django.db.backend.get_date_extract_sql django.db.connection.ops.date_extract_sql
307 django.db.backend.get_date_trunc_sql django.db.connection.ops.date_trunc_sql
308 django.db.backend.get_datetime_cast_sql django.db.connection.ops.datetime_cast_sql
309 django.db.backend.get_deferrable_sql django.db.connection.ops.deferrable_sql
310 django.db.backend.get_drop_foreignkey_sql django.db.connection.ops.drop_foreignkey_sql
311 django.db.backend.get_fulltext_search_sql django.db.connection.ops.fulltext_search_sql
312 django.db.backend.get_last_insert_id django.db.connection.ops.last_insert_id
313 django.db.backend.get_limit_offset_sql django.db.connection.ops.limit_offset_sql
314 django.db.backend.get_max_name_length django.db.connection.ops.max_name_length
315 django.db.backend.get_pk_default_value django.db.connection.ops.pk_default_value
316 django.db.backend.get_random_function_sql django.db.connection.ops.random_function_sql
317 django.db.backend.get_sql_flush django.db.connection.ops.sql_flush
318 django.db.backend.get_sql_sequence_reset django.db.connection.ops.sequence_reset_sql
319 django.db.backend.get_start_transaction_sql django.db.connection.ops.start_transaction_sql
320 django.db.backend.get_tablespace_sql django.db.connection.ops.tablespace_sql
321 django.db.backend.quote_name django.db.connection.ops.quote_name
322 django.db.backend.get_query_set_class django.db.connection.ops.query_set_class
323 django.db.backend.get_field_cast_sql django.db.connection.ops.field_cast_sql
324 django.db.backend.get_drop_sequence django.db.connection.ops.drop_sequence_sql
325 django.db.backend.OPERATOR_MAPPING django.db.connection.operators
326 django.db.backend.allows_group_by_ordinal django.db.connection.features.allows_group_by_ordinal
327 django.db.backend.allows_unique_and_pk django.db.connection.features.allows_unique_and_pk
328 django.db.backend.autoindexes_primary_keys django.db.connection.features.autoindexes_primary_keys
329 django.db.backend.needs_datetime_string_cast django.db.connection.features.needs_datetime_string_cast
330 django.db.backend.needs_upper_for_iops django.db.connection.features.needs_upper_for_iops
331 django.db.backend.supports_constraints django.db.connection.features.supports_constraints
332 django.db.backend.supports_tablespaces django.db.connection.features.supports_tablespaces
333 django.db.backend.uses_case_insensitive_names django.db.connection.features.uses_case_insensitive_names
334 django.db.backend.uses_custom_queryset django.db.connection.features.uses_custom_queryset
335 ============================================= =========================================================
336
Back to Top