|
1 ========================================= |
|
2 Porting your apps from Django 0.96 to 1.0 |
|
3 ========================================= |
|
4 |
|
5 .. highlight:: python |
|
6 |
|
7 Django 1.0 breaks compatibility with 0.96 in some areas. |
|
8 |
|
9 This guide will help you port 0.96 projects and apps to 1.0. The first part of |
|
10 this document includes the common changes needed to run with 1.0. If after going |
|
11 through the first part your code still breaks, check the section `Less-common |
|
12 Changes`_ for a list of a bunch of less-common compatibility issues. |
|
13 |
|
14 .. seealso:: |
|
15 |
|
16 The :doc:`1.0 release notes </releases/1.0>`. That document explains the new |
|
17 features in 1.0 more deeply; the porting guide is more concerned with |
|
18 helping you quickly update your code. |
|
19 |
|
20 Common changes |
|
21 ============== |
|
22 |
|
23 This section describes the changes between 0.96 and 1.0 that most users will |
|
24 need to make. |
|
25 |
|
26 Use Unicode |
|
27 ----------- |
|
28 |
|
29 Change string literals (``'foo'``) into Unicode literals (``u'foo'``). Django |
|
30 now uses Unicode strings throughout. In most places, raw strings will continue |
|
31 to work, but updating to use Unicode literals will prevent some obscure |
|
32 problems. |
|
33 |
|
34 See :doc:`/ref/unicode` for full details. |
|
35 |
|
36 Models |
|
37 ------ |
|
38 |
|
39 Common changes to your models file: |
|
40 |
|
41 Rename ``maxlength`` to ``max_length`` |
|
42 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
43 |
|
44 Rename your ``maxlength`` argument to ``max_length`` (this was changed to be |
|
45 consistent with form fields): |
|
46 |
|
47 Replace ``__str__`` with ``__unicode__`` |
|
48 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
49 |
|
50 Replace your model's ``__str__`` function with a ``__unicode__`` method, and |
|
51 make sure you `use Unicode`_ (``u'foo'``) in that method. |
|
52 |
|
53 Remove ``prepopulated_from`` |
|
54 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
55 |
|
56 Remove the ``prepopulated_from`` argument on model fields. It's no longer valid |
|
57 and has been moved to the ``ModelAdmin`` class in ``admin.py``. See `the |
|
58 admin`_, below, for more details about changes to the admin. |
|
59 |
|
60 Remove ``core`` |
|
61 ~~~~~~~~~~~~~~~ |
|
62 |
|
63 Remove the ``core`` argument from your model fields. It is no longer |
|
64 necessary, since the equivalent functionality (part of :ref:`inline editing |
|
65 <admin-inlines>`) is handled differently by the admin interface now. You don't |
|
66 have to worry about inline editing until you get to `the admin`_ section, |
|
67 below. For now, remove all references to ``core``. |
|
68 |
|
69 Replace ``class Admin:`` with ``admin.py`` |
|
70 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
71 |
|
72 Remove all your inner ``class Admin`` declarations from your models. They won't |
|
73 break anything if you leave them, but they also won't do anything. To register |
|
74 apps with the admin you'll move those declarations to an ``admin.py`` file; |
|
75 see `the admin`_ below for more details. |
|
76 |
|
77 .. seealso:: |
|
78 |
|
79 A contributor to djangosnippets__ has written a script that'll `scan your |
|
80 models.py and generate a corresponding admin.py`__. |
|
81 |
|
82 __ http://www.djangosnippets.org/ |
|
83 __ http://www.djangosnippets.org/snippets/603/ |
|
84 |
|
85 Example |
|
86 ~~~~~~~ |
|
87 |
|
88 Below is an example ``models.py`` file with all the changes you'll need to make: |
|
89 |
|
90 Old (0.96) ``models.py``:: |
|
91 |
|
92 class Author(models.Model): |
|
93 first_name = models.CharField(maxlength=30) |
|
94 last_name = models.CharField(maxlength=30) |
|
95 slug = models.CharField(maxlength=60, prepopulate_from=('first_name', 'last_name')) |
|
96 |
|
97 class Admin: |
|
98 list_display = ['first_name', 'last_name'] |
|
99 |
|
100 def __str__(self): |
|
101 return '%s %s' % (self.first_name, self.last_name) |
|
102 |
|
103 New (1.0) ``models.py``:: |
|
104 |
|
105 class Author(models.Model): |
|
106 first_name = models.CharField(max_length=30) |
|
107 last_name = models.CharField(max_length=30) |
|
108 slug = models.CharField(max_length=60) |
|
109 |
|
110 def __unicode__(self): |
|
111 return u'%s %s' % (self.first_name, self.last_name) |
|
112 |
|
113 New (1.0) ``admin.py``:: |
|
114 |
|
115 from django.contrib import admin |
|
116 from models import Author |
|
117 |
|
118 class AuthorAdmin(admin.ModelAdmin): |
|
119 list_display = ['first_name', 'last_name'] |
|
120 prepopulated_fields = { |
|
121 'slug': ('first_name', 'last_name') |
|
122 } |
|
123 |
|
124 admin.site.register(Author, AuthorAdmin) |
|
125 |
|
126 The Admin |
|
127 --------- |
|
128 |
|
129 One of the biggest changes in 1.0 is the new admin. The Django administrative |
|
130 interface (``django.contrib.admin``) has been completely refactored; admin |
|
131 definitions are now completely decoupled from model definitions, the framework |
|
132 has been rewritten to use Django's new form-handling library and redesigned with |
|
133 extensibility and customization in mind. |
|
134 |
|
135 Practically, this means you'll need to rewrite all of your ``class Admin`` |
|
136 declarations. You've already seen in `models`_ above how to replace your ``class |
|
137 Admin`` with a ``admin.site.register()`` call in an ``admin.py`` file. Below are |
|
138 some more details on how to rewrite that ``Admin`` declaration into the new |
|
139 syntax. |
|
140 |
|
141 Use new inline syntax |
|
142 ~~~~~~~~~~~~~~~~~~~~~ |
|
143 |
|
144 The new ``edit_inline`` options have all been moved to ``admin.py``. Here's an |
|
145 example: |
|
146 |
|
147 Old (0.96):: |
|
148 |
|
149 class Parent(models.Model): |
|
150 ... |
|
151 |
|
152 class Child(models.Model): |
|
153 parent = models.ForeignKey(Parent, edit_inline=models.STACKED, num_in_admin=3) |
|
154 |
|
155 |
|
156 New (1.0):: |
|
157 |
|
158 class ChildInline(admin.StackedInline): |
|
159 model = Child |
|
160 extra = 3 |
|
161 |
|
162 class ParentAdmin(admin.ModelAdmin): |
|
163 model = Parent |
|
164 inlines = [ChildInline] |
|
165 |
|
166 admin.site.register(Parent, ParentAdmin) |
|
167 |
|
168 See :ref:`admin-inlines` for more details. |
|
169 |
|
170 Simplify ``fields``, or use ``fieldsets`` |
|
171 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
172 |
|
173 The old ``fields`` syntax was quite confusing, and has been simplified. The old |
|
174 syntax still works, but you'll need to use ``fieldsets`` instead. |
|
175 |
|
176 Old (0.96):: |
|
177 |
|
178 class ModelOne(models.Model): |
|
179 ... |
|
180 |
|
181 class Admin: |
|
182 fields = ( |
|
183 (None, {'fields': ('foo','bar')}), |
|
184 ) |
|
185 |
|
186 class ModelTwo(models.Model): |
|
187 ... |
|
188 |
|
189 class Admin: |
|
190 fields = ( |
|
191 ('group1', {'fields': ('foo','bar'), 'classes': 'collapse'}), |
|
192 ('group2', {'fields': ('spam','eggs'), 'classes': 'collapse wide'}), |
|
193 ) |
|
194 |
|
195 |
|
196 New (1.0):: |
|
197 |
|
198 class ModelOneAdmin(admin.ModelAdmin): |
|
199 fields = ('foo', 'bar') |
|
200 |
|
201 class ModelTwoAdmin(admin.ModelAdmin): |
|
202 fieldsets = ( |
|
203 ('group1', {'fields': ('foo','bar'), 'classes': 'collapse'}), |
|
204 ('group2', {'fields': ('spam','eggs'), 'classes': 'collapse wide'}), |
|
205 ) |
|
206 |
|
207 |
|
208 .. seealso:: |
|
209 |
|
210 * More detailed information about the changes and the reasons behind them |
|
211 can be found on the `NewformsAdminBranch wiki page`__ |
|
212 |
|
213 * The new admin comes with a ton of new features; you can read about them in |
|
214 the :doc:`admin documentation </ref/contrib/admin/index>`. |
|
215 |
|
216 __ http://code.djangoproject.com/wiki/NewformsAdminBranch |
|
217 |
|
218 URLs |
|
219 ---- |
|
220 |
|
221 Update your root ``urls.py`` |
|
222 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
223 |
|
224 If you're using the admin site, you need to update your root ``urls.py``. |
|
225 |
|
226 Old (0.96) ``urls.py``:: |
|
227 |
|
228 from django.conf.urls.defaults import * |
|
229 |
|
230 urlpatterns = patterns('', |
|
231 (r'^admin/', include('django.contrib.admin.urls')), |
|
232 |
|
233 # ... the rest of your URLs here ... |
|
234 ) |
|
235 |
|
236 New (1.0) ``urls.py``:: |
|
237 |
|
238 from django.conf.urls.defaults import * |
|
239 |
|
240 # The next two lines enable the admin and load each admin.py file: |
|
241 from django.contrib import admin |
|
242 admin.autodiscover() |
|
243 |
|
244 urlpatterns = patterns('', |
|
245 (r'^admin/(.*)', admin.site.root), |
|
246 |
|
247 # ... the rest of your URLs here ... |
|
248 ) |
|
249 |
|
250 Views |
|
251 ----- |
|
252 |
|
253 Use ``django.forms`` instead of ``newforms`` |
|
254 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
255 |
|
256 Replace ``django.newforms`` with ``django.forms`` -- Django 1.0 renamed the |
|
257 ``newforms`` module (introduced in 0.96) to plain old ``forms``. The |
|
258 ``oldforms`` module was also removed. |
|
259 |
|
260 If you're already using the ``newforms`` library, and you used our recommended |
|
261 ``import`` statement syntax, all you have to do is change your import |
|
262 statements. |
|
263 |
|
264 Old:: |
|
265 |
|
266 from django import newforms as forms |
|
267 |
|
268 New:: |
|
269 |
|
270 from django import forms |
|
271 |
|
272 If you're using the old forms system (formerly known as ``django.forms`` and |
|
273 ``django.oldforms``), you'll have to rewrite your forms. A good place to start |
|
274 is the :doc:`forms documentation </topics/forms/index>` |
|
275 |
|
276 Handle uploaded files using the new API |
|
277 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
278 |
|
279 Replace use of uploaded files -- that is, entries in ``request.FILES`` -- as |
|
280 simple dictionaries with the new :class:`~django.core.files.UploadedFile`. The |
|
281 old dictionary syntax no longer works. |
|
282 |
|
283 Thus, in a view like:: |
|
284 |
|
285 def my_view(request): |
|
286 f = request.FILES['file_field_name'] |
|
287 ... |
|
288 |
|
289 ...you'd need to make the following changes: |
|
290 |
|
291 ===================== ===================== |
|
292 Old (0.96) New (1.0) |
|
293 ===================== ===================== |
|
294 ``f['content']`` ``f.read()`` |
|
295 ``f['filename']`` ``f.name`` |
|
296 ``f['content-type']`` ``f.content_type`` |
|
297 ===================== ===================== |
|
298 |
|
299 Work with file fields using the new API |
|
300 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
301 |
|
302 The internal implementation of :class:`django.db.models.FileField` have changed. |
|
303 A visible result of this is that the way you access special attributes (URL, |
|
304 filename, image size, etc) of these model fields has changed. You will need to |
|
305 make the following changes, assuming your model's |
|
306 :class:`~django.db.models.FileField` is called ``myfile``: |
|
307 |
|
308 =================================== ======================== |
|
309 Old (0.96) New (1.0) |
|
310 =================================== ======================== |
|
311 ``myfile.get_content_filename()`` ``myfile.content.path`` |
|
312 ``myfile.get_content_url()`` ``myfile.content.url`` |
|
313 ``myfile.get_content_size()`` ``myfile.content.size`` |
|
314 ``myfile.save_content_file()`` ``myfile.content.save()`` |
|
315 ``myfile.get_content_width()`` ``myfile.content.width`` |
|
316 ``myfile.get_content_height()`` ``myfile.content.height`` |
|
317 =================================== ======================== |
|
318 |
|
319 Note that the ``width`` and ``height`` attributes only make sense for |
|
320 :class:`~django.db.models.ImageField` fields. More details can be found in the |
|
321 :doc:`model API </ref/models/fields>` documentation. |
|
322 |
|
323 Use ``Paginator`` instead of ``ObjectPaginator`` |
|
324 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
325 |
|
326 The ``ObjectPaginator`` in 0.96 has been removed and replaced with an improved |
|
327 version, :class:`django.core.paginator.Paginator`. |
|
328 |
|
329 Templates |
|
330 --------- |
|
331 |
|
332 Learn to love autoescaping |
|
333 ~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
334 |
|
335 By default, the template system now automatically HTML-escapes the output of |
|
336 every variable. To learn more, see :ref:`automatic-html-escaping`. |
|
337 |
|
338 To disable auto-escaping for an individual variable, use the :tfilter:`safe` |
|
339 filter: |
|
340 |
|
341 .. code-block:: html+django |
|
342 |
|
343 This will be escaped: {{ data }} |
|
344 This will not be escaped: {{ data|safe }} |
|
345 |
|
346 To disable auto-escaping for an entire template, wrap the template (or just a |
|
347 particular section of the template) in the :ttag:`autoescape` tag: |
|
348 |
|
349 .. code-block:: html+django |
|
350 |
|
351 {% autoescape off %} |
|
352 ... unescaped template content here ... |
|
353 {% endautoescape %} |
|
354 |
|
355 Less-common changes |
|
356 =================== |
|
357 |
|
358 The following changes are smaller, more localized changes. They should only |
|
359 affect more advanced users, but it's probably worth reading through the list and |
|
360 checking your code for these things. |
|
361 |
|
362 Signals |
|
363 ------- |
|
364 |
|
365 * Add ``**kwargs`` to any registered signal handlers. |
|
366 |
|
367 * Connect, disconnect, and send signals via methods on the |
|
368 :class:`~django.dispatch.Signal` object instead of through module methods in |
|
369 ``django.dispatch.dispatcher``. |
|
370 |
|
371 * Remove any use of the ``Anonymous`` and ``Any`` sender options; they no longer |
|
372 exist. You can still receive signals sent by any sender by using |
|
373 ``sender=None`` |
|
374 |
|
375 * Make any custom signals you've declared into instances of |
|
376 :class:`django.dispatch.Signal` instead of anonymous objects. |
|
377 |
|
378 Here's quick summary of the code changes you'll need to make: |
|
379 |
|
380 ================================================= ====================================== |
|
381 Old (0.96) New (1.0) |
|
382 ================================================= ====================================== |
|
383 ``def callback(sender)`` ``def callback(sender, **kwargs)`` |
|
384 ``sig = object()`` ``sig = django.dispatch.Signal()`` |
|
385 ``dispatcher.connect(callback, sig)`` ``sig.connect(callback)`` |
|
386 ``dispatcher.send(sig, sender)`` ``sig.send(sender)`` |
|
387 ``dispatcher.connect(callback, sig, sender=Any)`` ``sig.connect(callback, sender=None)`` |
|
388 ================================================= ====================================== |
|
389 |
|
390 Comments |
|
391 -------- |
|
392 |
|
393 If you were using Django 0.96's ``django.contrib.comments`` app, you'll need to |
|
394 upgrade to the new comments app introduced in 1.0. See |
|
395 :doc:`/ref/contrib/comments/upgrade` for details. |
|
396 |
|
397 Template tags |
|
398 ------------- |
|
399 |
|
400 :ttag:`spaceless` tag |
|
401 ~~~~~~~~~~~~~~~~~~~~~ |
|
402 |
|
403 The spaceless template tag now removes *all* spaces between HTML tags, instead |
|
404 of preserving a single space. |
|
405 |
|
406 Local flavors |
|
407 ------------- |
|
408 |
|
409 U.S. local flavor |
|
410 ~~~~~~~~~~~~~~~~~ |
|
411 |
|
412 ``django.contrib.localflavor.usa`` has been renamed to |
|
413 :mod:`django.contrib.localflavor.us`. This change was made to match the naming |
|
414 scheme of other local flavors. To migrate your code, all you need to do is |
|
415 change the imports. |
|
416 |
|
417 Sessions |
|
418 -------- |
|
419 |
|
420 Getting a new session key |
|
421 ~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
422 |
|
423 ``SessionBase.get_new_session_key()`` has been renamed to |
|
424 ``_get_new_session_key()``. ``get_new_session_object()`` no longer exists. |
|
425 |
|
426 Fixtures |
|
427 -------- |
|
428 |
|
429 Loading a row no longer calls ``save()`` |
|
430 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
431 |
|
432 Previously, loading a row automatically ran the model's ``save()`` method. This |
|
433 is no longer the case, so any fields (for example: timestamps) that were |
|
434 auto-populated by a ``save()`` now need explicit values in any fixture. |
|
435 |
|
436 Settings |
|
437 -------- |
|
438 |
|
439 Better exceptions |
|
440 ~~~~~~~~~~~~~~~~~ |
|
441 |
|
442 The old :exc:`EnvironmentError` has split into an :exc:`ImportError` when |
|
443 Django fails to find the settings module and a :exc:`RuntimeError` when you try |
|
444 to reconfigure settings after having already used them |
|
445 |
|
446 ``LOGIN_URL`` has moved |
|
447 ~~~~~~~~~~~~~~~~~~~~~~~ |
|
448 |
|
449 The ``LOGIN_URL`` constant moved from ``django.contrib.auth`` into the |
|
450 ``settings`` module. Instead of using ``from django.contrib.auth import |
|
451 LOGIN_URL`` refer to :setting:`settings.LOGIN_URL <LOGIN_URL>`. |
|
452 |
|
453 :setting:`APPEND_SLASH` behavior has been updated |
|
454 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
455 |
|
456 In 0.96, if a URL didn't end in a slash or have a period in the final |
|
457 component of its path, and ``APPEND_SLASH`` was True, Django would redirect |
|
458 to the same URL, but with a slash appended to the end. Now, Django checks to |
|
459 see whether the pattern without the trailing slash would be matched by |
|
460 something in your URL patterns. If so, no redirection takes place, because it |
|
461 is assumed you deliberately wanted to catch that pattern. |
|
462 |
|
463 For most people, this won't require any changes. Some people, though, have URL |
|
464 patterns that look like this:: |
|
465 |
|
466 r'/some_prefix/(.*)$' |
|
467 |
|
468 Previously, those patterns would have been redirected to have a trailing |
|
469 slash. If you always want a slash on such URLs, rewrite the pattern as:: |
|
470 |
|
471 r'/some_prefix/(.*/)$' |
|
472 |
|
473 Smaller model changes |
|
474 --------------------- |
|
475 |
|
476 Different exception from ``get()`` |
|
477 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
478 |
|
479 Managers now return a :exc:`MultipleObjectsReturned` exception |
|
480 instead of :exc:`AssertionError`: |
|
481 |
|
482 Old (0.96):: |
|
483 |
|
484 try: |
|
485 Model.objects.get(...) |
|
486 except AssertionError: |
|
487 handle_the_error() |
|
488 |
|
489 New (1.0):: |
|
490 |
|
491 try: |
|
492 Model.objects.get(...) |
|
493 except Model.MultipleObjectsReturned: |
|
494 handle_the_error() |
|
495 |
|
496 ``LazyDate`` has been fired |
|
497 ~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
498 |
|
499 The ``LazyDate`` helper class no longer exists. |
|
500 |
|
501 Default field values and query arguments can both be callable objects, so |
|
502 instances of ``LazyDate`` can be replaced with a reference to ``datetime.datetime.now``: |
|
503 |
|
504 Old (0.96):: |
|
505 |
|
506 class Article(models.Model): |
|
507 title = models.CharField(maxlength=100) |
|
508 published = models.DateField(default=LazyDate()) |
|
509 |
|
510 New (1.0):: |
|
511 |
|
512 import datetime |
|
513 |
|
514 class Article(models.Model): |
|
515 title = models.CharField(max_length=100) |
|
516 published = models.DateField(default=datetime.datetime.now) |
|
517 |
|
518 ``DecimalField`` is new, and ``FloatField`` is now a proper float |
|
519 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
520 |
|
521 Old (0.96):: |
|
522 |
|
523 class MyModel(models.Model): |
|
524 field_name = models.FloatField(max_digits=10, decimal_places=3) |
|
525 ... |
|
526 |
|
527 New (1.0):: |
|
528 |
|
529 class MyModel(models.Model): |
|
530 field_name = models.DecimalField(max_digits=10, decimal_places=3) |
|
531 ... |
|
532 |
|
533 If you forget to make this change, you will see errors about ``FloatField`` |
|
534 not taking a ``max_digits`` attribute in ``__init__``, because the new |
|
535 ``FloatField`` takes no precision-related arguments. |
|
536 |
|
537 If you're using MySQL or PostgreSQL, no further changes are needed. The |
|
538 database column types for ``DecimalField`` are the same as for the old |
|
539 ``FloatField``. |
|
540 |
|
541 If you're using SQLite, you need to force the database to view the |
|
542 appropriate columns as decimal types, rather than floats. To do this, you'll |
|
543 need to reload your data. Do this after you have made the change to using |
|
544 ``DecimalField`` in your code and updated the Django code. |
|
545 |
|
546 .. warning:: |
|
547 |
|
548 **Back up your database first!** |
|
549 |
|
550 For SQLite, this means making a copy of the single file that stores the |
|
551 database (the name of that file is the ``DATABASE_NAME`` in your settings.py |
|
552 file). |
|
553 |
|
554 To upgrade each application to use a ``DecimalField``, you can do the |
|
555 following, replacing ``<app>`` in the code below with each app's name: |
|
556 |
|
557 .. code-block:: bash |
|
558 |
|
559 $ ./manage.py dumpdata --format=xml <app> > data-dump.xml |
|
560 $ ./manage.py reset <app> |
|
561 $ ./manage.py loaddata data-dump.xml |
|
562 |
|
563 Notes: |
|
564 |
|
565 1. It's important that you remember to use XML format in the first step of |
|
566 this process. We are exploiting a feature of the XML data dumps that makes |
|
567 porting floats to decimals with SQLite possible. |
|
568 |
|
569 2. In the second step you will be asked to confirm that you are prepared to |
|
570 lose the data for the application(s) in question. Say yes; we'll restore |
|
571 this data in the third step, of course. |
|
572 |
|
573 3. ``DecimalField`` is not used in any of the apps shipped with Django prior |
|
574 to this change being made, so you do not need to worry about performing |
|
575 this procedure for any of the standard Django models. |
|
576 |
|
577 If something goes wrong in the above process, just copy your backed up |
|
578 database file over the original file and start again. |
|
579 |
|
580 Internationalization |
|
581 -------------------- |
|
582 |
|
583 :func:`django.views.i18n.set_language` now requires a POST request |
|
584 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
585 |
|
586 Previously, a GET request was used. The old behavior meant that state (the |
|
587 locale used to display the site) could be changed by a GET request, which is |
|
588 against the HTTP specification's recommendations. Code calling this view must |
|
589 ensure that a POST request is now made, instead of a GET. This means you can |
|
590 no longer use a link to access the view, but must use a form submission of |
|
591 some kind (e.g. a button). |
|
592 |
|
593 ``_()`` is no longer in builtins |
|
594 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
595 |
|
596 ``_()`` (the callable object whose name is a single underscore) is no longer |
|
597 monkeypatched into builtins -- that is, it's no longer available magically in |
|
598 every module. |
|
599 |
|
600 If you were previously relying on ``_()`` always being present, you should now |
|
601 explicitly import ``ugettext`` or ``ugettext_lazy``, if appropriate, and alias |
|
602 it to ``_`` yourself:: |
|
603 |
|
604 from django.utils.translation import ugettext as _ |
|
605 |
|
606 HTTP request/response objects |
|
607 ----------------------------- |
|
608 |
|
609 Dictionary access to ``HttpRequest`` |
|
610 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
611 |
|
612 ``HttpRequest`` objects no longer directly support dictionary-style |
|
613 access; previously, both ``GET`` and ``POST`` data were directly |
|
614 available on the ``HttpRequest`` object (e.g., you could check for a |
|
615 piece of form data by using ``if 'some_form_key' in request`` or by |
|
616 reading ``request['some_form_key']``. This is no longer supported; if |
|
617 you need access to the combined ``GET`` and ``POST`` data, use |
|
618 ``request.REQUEST`` instead. |
|
619 |
|
620 It is strongly suggested, however, that you always explicitly look in |
|
621 the appropriate dictionary for the type of request you expect to |
|
622 receive (``request.GET`` or ``request.POST``); relying on the combined |
|
623 ``request.REQUEST`` dictionary can mask the origin of incoming data. |
|
624 |
|
625 Accessing ``HTTPResponse`` headers |
|
626 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
627 |
|
628 ``django.http.HttpResponse.headers`` has been renamed to ``_headers`` and |
|
629 :class:`~django.http.HttpResponse` now supports containment checking directly. |
|
630 So use ``if header in response:`` instead of ``if header in response.headers:``. |
|
631 |
|
632 Generic relations |
|
633 ----------------- |
|
634 |
|
635 Generic relations have been moved out of core |
|
636 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
637 |
|
638 The generic relation classes -- ``GenericForeignKey`` and ``GenericRelation`` |
|
639 -- have moved into the :mod:`django.contrib.contenttypes` module. |
|
640 |
|
641 Testing |
|
642 ------- |
|
643 |
|
644 :meth:`django.test.Client.login` has changed |
|
645 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
646 |
|
647 Old (0.96):: |
|
648 |
|
649 from django.test import Client |
|
650 c = Client() |
|
651 c.login('/path/to/login','myuser','mypassword') |
|
652 |
|
653 New (1.0):: |
|
654 |
|
655 # ... same as above, but then: |
|
656 c.login(username='myuser', password='mypassword') |
|
657 |
|
658 Management commands |
|
659 ------------------- |
|
660 |
|
661 Running management commands from your code |
|
662 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
663 |
|
664 :mod:`django.core.management` has been greatly refactored. |
|
665 |
|
666 Calls to management services in your code now need to use |
|
667 ``call_command``. For example, if you have some test code that calls flush and |
|
668 load_data:: |
|
669 |
|
670 from django.core import management |
|
671 management.flush(verbosity=0, interactive=False) |
|
672 management.load_data(['test_data'], verbosity=0) |
|
673 |
|
674 ...you'll need to change this code to read:: |
|
675 |
|
676 from django.core import management |
|
677 management.call_command('flush', verbosity=0, interactive=False) |
|
678 management.call_command('loaddata', 'test_data', verbosity=0) |
|
679 |
|
680 Subcommands must now precede options |
|
681 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
682 |
|
683 ``django-admin.py`` and ``manage.py`` now require subcommands to precede |
|
684 options. So: |
|
685 |
|
686 .. code-block:: bash |
|
687 |
|
688 $ django-admin.py --settings=foo.bar runserver |
|
689 |
|
690 ...no longer works and should be changed to: |
|
691 |
|
692 .. code-block:: bash |
|
693 |
|
694 $ django-admin.py runserver --settings=foo.bar |
|
695 |
|
696 Syndication |
|
697 ----------- |
|
698 |
|
699 ``Feed.__init__`` has changed |
|
700 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
701 |
|
702 The ``__init__()`` method of the syndication framework's ``Feed`` class now |
|
703 takes an ``HttpRequest`` object as its second parameter, instead of the feed's |
|
704 URL. This allows the syndication framework to work without requiring the sites |
|
705 framework. This only affects code that subclasses ``Feed`` and overrides the |
|
706 ``__init__()`` method, and code that calls ``Feed.__init__()`` directly. |
|
707 |
|
708 Data structures |
|
709 --------------- |
|
710 |
|
711 ``SortedDictFromList`` is gone |
|
712 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
713 |
|
714 ``django.newforms.forms.SortedDictFromList`` was removed. |
|
715 :class:`django.utils.datastructures.SortedDict` can now be instantiated with |
|
716 a sequence of tuples. |
|
717 |
|
718 To update your code: |
|
719 |
|
720 1. Use :class:`django.utils.datastructures.SortedDict` wherever you were |
|
721 using ``django.newforms.forms.SortedDictFromList``. |
|
722 |
|
723 2. Because :meth:`django.utils.datastructures.SortedDict.copy` doesn't |
|
724 return a deepcopy as ``SortedDictFromList.copy()`` did, you will need |
|
725 to update your code if you were relying on a deepcopy. Do this by using |
|
726 ``copy.deepcopy`` directly. |
|
727 |
|
728 Database backend functions |
|
729 -------------------------- |
|
730 |
|
731 Database backend functions have been renamed |
|
732 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
733 |
|
734 Almost *all* of the database backend-level functions have been renamed and/or |
|
735 relocated. None of these were documented, but you'll need to change your code |
|
736 if you're using any of these functions, all of which are in :mod:`django.db`: |
|
737 |
|
738 ======================================= =================================================== |
|
739 Old (0.96) New (1.0) |
|
740 ======================================= =================================================== |
|
741 ``backend.get_autoinc_sql`` ``connection.ops.autoinc_sql`` |
|
742 ``backend.get_date_extract_sql`` ``connection.ops.date_extract_sql`` |
|
743 ``backend.get_date_trunc_sql`` ``connection.ops.date_trunc_sql`` |
|
744 ``backend.get_datetime_cast_sql`` ``connection.ops.datetime_cast_sql`` |
|
745 ``backend.get_deferrable_sql`` ``connection.ops.deferrable_sql`` |
|
746 ``backend.get_drop_foreignkey_sql`` ``connection.ops.drop_foreignkey_sql`` |
|
747 ``backend.get_fulltext_search_sql`` ``connection.ops.fulltext_search_sql`` |
|
748 ``backend.get_last_insert_id`` ``connection.ops.last_insert_id`` |
|
749 ``backend.get_limit_offset_sql`` ``connection.ops.limit_offset_sql`` |
|
750 ``backend.get_max_name_length`` ``connection.ops.max_name_length`` |
|
751 ``backend.get_pk_default_value`` ``connection.ops.pk_default_value`` |
|
752 ``backend.get_random_function_sql`` ``connection.ops.random_function_sql`` |
|
753 ``backend.get_sql_flush`` ``connection.ops.sql_flush`` |
|
754 ``backend.get_sql_sequence_reset`` ``connection.ops.sequence_reset_sql`` |
|
755 ``backend.get_start_transaction_sql`` ``connection.ops.start_transaction_sql`` |
|
756 ``backend.get_tablespace_sql`` ``connection.ops.tablespace_sql`` |
|
757 ``backend.quote_name`` ``connection.ops.quote_name`` |
|
758 ``backend.get_query_set_class`` ``connection.ops.query_set_class`` |
|
759 ``backend.get_field_cast_sql`` ``connection.ops.field_cast_sql`` |
|
760 ``backend.get_drop_sequence`` ``connection.ops.drop_sequence_sql`` |
|
761 ``backend.OPERATOR_MAPPING`` ``connection.operators`` |
|
762 ``backend.allows_group_by_ordinal`` ``connection.features.allows_group_by_ordinal`` |
|
763 ``backend.allows_unique_and_pk`` ``connection.features.allows_unique_and_pk`` |
|
764 ``backend.autoindexes_primary_keys`` ``connection.features.autoindexes_primary_keys`` |
|
765 ``backend.needs_datetime_string_cast`` ``connection.features.needs_datetime_string_cast`` |
|
766 ``backend.needs_upper_for_iops`` ``connection.features.needs_upper_for_iops`` |
|
767 ``backend.supports_constraints`` ``connection.features.supports_constraints`` |
|
768 ``backend.supports_tablespaces`` ``connection.features.supports_tablespaces`` |
|
769 ``backend.uses_case_insensitive_names`` ``connection.features.uses_case_insensitive_names`` |
|
770 ``backend.uses_custom_queryset`` ``connection.features.uses_custom_queryset`` |
|
771 ======================================= =================================================== |
|
772 |