13 | | == Models support properties == |
14 | | |
15 | | '''Status: Done''' |
16 | | |
17 | | Unlike before, properties are supported on models. |
18 | | |
19 | | {{{ |
20 | | #!python |
21 | | from django.db import models |
22 | | |
23 | | class Person(models.Model): |
24 | | first_name = models.CharField(maxlength=30) |
25 | | last_name = models.CharField(maxlength=30) |
26 | | |
27 | | def _get_full_name(self): |
28 | | return "%s %s" % (self.first_name, self.last_name) |
29 | | full_name = property(_get_full_name) |
30 | | }}} |
31 | | |
32 | | == Model class and Field classes renamed/relocated == |
33 | | |
34 | | '''Status: Done''' |
35 | | |
36 | | Difference: Import is from {{{django.db.models}}} instead of {{{django.core.meta}}}. This is easier to remember. |
37 | | |
38 | | {{{ |
39 | | #!python |
40 | | from django.db import models |
41 | | |
42 | | class Person(models.Model): |
43 | | first_name = models.CharField(maxlength=30) |
44 | | last_name = models.CharField(maxlength=30) |
45 | | }}} |
46 | | |
47 | | == Database connection relocated/renamed == |
48 | | |
49 | | '''Status: Done''' |
50 | | |
51 | | The connection is now available at {{{django.db.connection}}}. This is easier to remember, clearer and more consistent. |
52 | | |
53 | | Old: |
54 | | {{{ |
55 | | #!python |
56 | | from django.core.db import db |
57 | | cursor = db.cursor() |
58 | | }}} |
59 | | |
60 | | New: |
61 | | {{{ |
62 | | #!python |
63 | | from django.db import connection |
64 | | cursor = connection.cursor() |
65 | | }}} |
66 | | |
67 | | Backend-specific functions, if you should need them, are available at {{{django.db.backend}}}. |
68 | | |
69 | | Old: |
70 | | {{{ |
71 | | #!python |
72 | | from django.core import db |
73 | | db.quote_name('foo') |
74 | | }}} |
75 | | |
76 | | New: |
77 | | {{{ |
78 | | #!python |
79 | | from django.db import backend |
80 | | backend.quote_name('foo') |
81 | | }}} |
82 | | |
83 | | Also, the various backend functionality has been split into three separate modules for each backend -- {{{base.py}}}, {{{creation.py}}} and {{{introspection.py}}}. This is purely for performance and memory savings, so that basic, everyday Django usage doesn't have to load the introspective functionality into memory. |
84 | | |
85 | | == Interact directly with model classes, not with magic modules == |
86 | | |
87 | | '''Status: Done''' |
88 | | |
89 | | Import the model class directly from the module in which it was defined. No more {{{django.models.*}}} magic. |
90 | | |
91 | | {{{ |
92 | | #!python |
93 | | from myproject.people.models import Person |
94 | | p = Person(first_name='John', last_name='Smith') |
95 | | p.save() |
96 | | }}} |
97 | | |
98 | | This also removes the need for the {{{module_name}}} parameter. |
99 | | |
100 | | == Access table-level DB API functions via model classes, not with magic modules == |
101 | | |
102 | | '''Status: Done''' |
103 | | |
104 | | All "table-level" functions -- ways of retrieving records tablewide rather than performing instance-specific tasks -- are now accessed via a model class's {{{objects}}} attribute. They aren't direct methods of a model instance object because we want to keep the "table-wide" and "row-specific" namespaces separate. |
105 | | |
106 | | {{{ |
107 | | #!python |
108 | | from myproject.people.models import Person |
109 | | p_list = Person.objects.get_list() |
110 | | p = Person.objects.get_object() |
111 | | }}} |
112 | | |
113 | | This doesn't work from an instance. |
114 | | |
115 | | {{{ |
116 | | #!python |
117 | | p = Person.objects.get_object(pk=1) |
118 | | p.objects.get_list() # Raises AttributeError |
119 | | }}} |
120 | | |
121 | | == Override default manager name ("objects") == |
122 | | |
123 | | '''Status: Done''' |
124 | | |
125 | | If a model already has an {{{objects}}} attribute, you'll need to specify an alternate name for the magic {{{objects}}}. |
126 | | |
127 | | {{{ |
128 | | #!python |
129 | | class Person(models.Model): |
130 | | first_name = models.CharField(maxlength=30) |
131 | | last_name = models.CharField(maxlength=30) |
132 | | objects = models.TextField() |
133 | | people = models.Manager() |
134 | | |
135 | | p = Person(first_name='Mary', last_name='Jones', objects='Hello there.') |
136 | | p.save() |
137 | | p.objects == 'Hello there.' |
138 | | Person.people.get_list() |
139 | | }}} |
140 | | |
141 | | == Custom managers, and multiple managers == |
142 | | |
143 | | '''Status: Done''' |
144 | | |
145 | | You can create as many managers as you want. When necessary (such as on the admin), Django will use the first one defined, in order. |
146 | | |
147 | | If you define at least one custom manager, it will not get the default "objects" manager. |
148 | | |
149 | | {{{ |
150 | | #!python |
151 | | class Person(models.Model): |
152 | | first_name = models.CharField(maxlength=30) |
153 | | last_name = models.CharField(maxlength=30) |
154 | | people = models.Manager() |
155 | | fun_people = SomeOtherManager() |
156 | | }}} |
157 | | |
158 | | == Added a more powerful way of overriding model methods, removed hard-coded _pre_save(), _post_save(), etc. == |
159 | | |
160 | | '''Status: Done''' |
161 | | |
162 | | Proper subclassing of methods now works, so you can subclass the automatic {{{save()}}} and {{{delete()}}} methods. This removes the need for the {{{_pre_save()}}}, {{{_post_save()}}}, {{{_pre_delete()}}} and {{{_post_delete()}}} hooks -- all of which have been removed. Example: |
163 | | |
164 | | {{{ |
165 | | #!python |
166 | | class Person(models.Model): |
167 | | first_name = models.CharField(maxlength=30) |
168 | | last_name = models.CharField(maxlength=30) |
169 | | |
170 | | def save(self): |
171 | | self.do_something() |
172 | | super(Person, self).save() # Call the "real" save() method. |
173 | | self.do_something_else() |
174 | | }}} |
175 | | |
176 | | You can even skip saving (as requested in #1014). |
177 | | |
178 | | {{{ |
179 | | #!python |
180 | | class Person(models.Model): |
181 | | first_name = models.CharField(maxlength=30) |
182 | | last_name = models.CharField(maxlength=30) |
183 | | |
184 | | def save(self): |
185 | | if datetime.date.today() > datetime.date(2005, 1, 1): |
186 | | super(Person, self).save() # Call the "real" save() method. |
187 | | else: |
188 | | # Don't save. |
189 | | pass |
190 | | }}} |
191 | | |
192 | | == New API functionality: Overriding table-level functions == |
193 | | |
194 | | '''Status: Done''' |
195 | | |
196 | | You can override any table-level functions, such as {{{get_list()}}} or {{{get_object()}}}. Do this by creating a custom {{{models.Manager}}} subclass and passing it to your model. |
197 | | |
198 | | {{{ |
199 | | #!python |
200 | | from django.db import models |
201 | | class PersonManager(models.Manager): |
202 | | def get_list(self, **kwargs): |
203 | | # Changes get_list() to hard-code a limit=10. |
204 | | kwargs['limit'] = 10 |
205 | | return models.Manager.get_list(self, **kwargs) # Call the "real" get_list() method. |
206 | | |
207 | | class Person(models.Model): |
208 | | first_name = models.CharField(maxlength=30) |
209 | | last_name = models.CharField(maxlength=30) |
210 | | objects = PersonManager() |
211 | | }}} |
212 | | |
213 | | If a manager needs to access its associated class, it should use {{{self.klass}}}. Example: |
214 | | |
215 | | {{{ |
216 | | #!python |
217 | | class PersonManager(models.Manager): |
218 | | def get_fun_person(self): |
219 | | try: |
220 | | return self.get_object(fun__exact=True) |
221 | | except self.klass.DoesNotExist: |
222 | | print "Doesn't exist." |
223 | | }}} |
224 | | |
225 | | An alternative syntax for this is proposed in #1224. |
226 | | |
227 | | == Renamed !DoesNotExist exception == |
228 | | |
229 | | '''Status: Done''' |
230 | | |
231 | | Instead of {{{people.PersonDoesNotExist}}}, it's {{{Person.DoesNotExist}}}. |
232 | | |
233 | | Old: |
234 | | {{{ |
235 | | #!python |
236 | | from django.models.myapp import people |
237 | | try: |
238 | | people.get_object(pk=1) |
239 | | except people.PersonDoesNotExist: |
240 | | print "Not there" |
241 | | }}} |
242 | | |
243 | | New: |
244 | | {{{ |
245 | | #!python |
246 | | from path.to.myapp.models import Person |
247 | | try: |
248 | | Person.objects.get_object(pk=1) |
249 | | except Person.DoesNotExist: |
250 | | print "Not there" |
251 | | }}} |
252 | | |
253 | | == Removed !SilentVariableFailure exception == |
254 | | |
255 | | '''Status: Done''' |
256 | | |
257 | | Old behavior: Any exception that subclasses {{{django.core.template.SilentVariableFailure}}} fails silently in the template system. |
258 | | |
259 | | New behavior: Any exception that has a {{{silent_variable_failure}}} attribute fails silently in the template system. {{{django.core.template.SilentVariableFailure}}} no longer exists. |
260 | | |
261 | | == Automatic manipulators == |
262 | | |
263 | | '''Status: Mostly done, with some quirks left''' |
264 | | |
265 | | Old: |
266 | | {{{ |
267 | | #!python |
268 | | from django.models.myapp import people |
269 | | m1 = people.AddManipulator() |
270 | | m2 = people.ChangeManipulator(3) |
271 | | }}} |
272 | | |
273 | | New: |
274 | | {{{ |
275 | | #!python |
276 | | from path.to.myapp.models import Person |
277 | | m1 = Person.AddManipulator() |
278 | | m2 = Person.ChangeManipulator(3) |
279 | | }}} |
280 | | |
281 | | == Renamed 'class META' to 'class Meta' == |
282 | | |
283 | | '''Status: Done''' |
284 | | |
285 | | The {{{class META}}} within models should now be {{{class Meta}}}. The latter is nicer on the eyes. |
286 | | |
287 | | == Moved admin options to 'class Admin' == |
288 | | |
289 | | '''Status: Done''' |
290 | | |
291 | | Instead of {{{admin=meta.Admin}}} in the {{{class META}}}, all admin options are in an inner {{{class Admin}}}. |
292 | | |
293 | | Old: |
294 | | {{{ |
295 | | #!python |
296 | | class Person(meta.Model): |
297 | | first_name = meta.CharField(maxlength=30) |
298 | | last_name = meta.CharField(maxlength=30) |
299 | | class META: |
300 | | admin = meta.Admin( |
301 | | list_display = ('first_name', 'last_name') |
302 | | ) |
303 | | }}} |
304 | | |
305 | | New: |
306 | | {{{ |
307 | | #!python |
308 | | class Person(models.Model): |
309 | | first_name = models.CharField(maxlength=30) |
310 | | last_name = models.CharField(maxlength=30) |
311 | | class Admin: |
312 | | list_display = ('first_name', 'last_name') |
313 | | }}} |
314 | | |
315 | | == get_object_or_404 and get_list_or_404 now take model classes, not modules == |
316 | | |
317 | | '''Status: Done''' |
318 | | |
319 | | Old: |
320 | | {{{ |
321 | | #!python |
322 | | get_object_or_404(polls, pk=1) |
323 | | }}} |
324 | | |
325 | | New: |
326 | | {{{ |
327 | | #!python |
328 | | get_object_or_404(Poll, pk=1) |
329 | | }}} |
330 | | |
331 | | == Model methods no longer automatically have access to datetime and db modules == |
332 | | |
333 | | '''Status: Done''' |
334 | | |
335 | | Formerly, each model method magically had access to the {{{datetime}}} module and to the variable {{{db}}}, which represents the current database connection. Now, those have to be imported explicitly. |
336 | | |
337 | | Old: |
338 | | {{{ |
339 | | #!python |
340 | | def some_method(self): |
341 | | print datetime.datetime.now() |
342 | | cursor = db.cursor() |
343 | | cursor.execute("UPDATE something;") |
344 | | }}} |
345 | | |
346 | | New: |
347 | | {{{ |
348 | | #!python |
349 | | import datetime |
350 | | from django.db import connection |
351 | | |
352 | | # ... |
353 | | |
354 | | def some_method(self): |
355 | | print datetime.datetime.now() |
356 | | cursor = connection.cursor() |
357 | | cursor.execute("UPDATE something;") |
358 | | }}} |
359 | | |
360 | | == Moved "auth" and "core" models to django.contrib == |
361 | | |
362 | | '''Status: Done''' |
363 | | |
364 | | See http://groups.google.com/group/django-developers/browse_thread/thread/276d071a74543448/7d4b1c40c2d53393 |
365 | | |
366 | | * Old: {{{django.models.auth}}} |
367 | | * New: {{{django.contrib.auth.models}}} |
368 | | |
369 | | * Old: {{{django.models.core.sites}}} |
370 | | * New: {{{django.contrib.sites.models}}} |
371 | | |
372 | | * Old: {{{django.models.core.contenttypes}}} |
373 | | * New: {{{django.contrib.contenttypes.models}}} |
374 | | |
375 | | * Old: {{{django.models.core.packages}}} |
376 | | * New: {{{django.contrib.contenttypes.models}}} ("Packages" will most likely be removed in the future.) |
377 | | |
378 | | == Moved Session model and middleware from core to django.contrib == |
379 | | |
380 | | '''Status: Done''' |
381 | | |
382 | | The location of the session middleware has changed. |
383 | | |
384 | | * Old: {{{django.middleware.sessions.SessionMiddleware}}} |
385 | | * New: {{{django.contrib.sessions.middleware.SessionMiddleware}}} |
386 | | |
387 | | Make sure to update your {{{MIDDLEWARE_CLASSES}}} setting, if you're using sessions. |
388 | | |
389 | | Also, the {{{Session}}} model has moved from django/models/core.py to django/contrib/sessions/models.py. If you're accessing the {{{Session}}} model for some reason, note that location change. |
390 | | |
391 | | == Changed the parameters you pass to generic views == |
392 | | |
393 | | '''Status: Done''' |
394 | | |
395 | | Because there's no longer a concept of {{{module_name}}}, the "info_dicts" passed to [http://www.djangoproject.com/documentation/generic_views/ generic views] no longer accept {{{"app_label"}}} and {{{"module_name"}}}. Instead, pass the parameter {{{"model"}}}, which should be your model class. |
396 | | |
397 | | These examples assume models live in {{{myproject/blog/models.py}}}. |
398 | | |
399 | | Old: |
400 | | {{{ |
401 | | #!python |
402 | | info_dict = { |
403 | | 'app_label': 'blog', |
404 | | 'module_name': 'entries' |
405 | | } |
406 | | }}} |
407 | | |
408 | | New: |
409 | | {{{ |
410 | | #!python |
411 | | from myproject.blog.models import Entry |
412 | | info_dict = { |
413 | | 'model': Entry |
414 | | } |
415 | | }}} |
416 | | |
417 | | == Changed template names in generic views == |
418 | | |
419 | | '''Status: Done''' |
420 | | |
421 | | Because there's no longer a concept of {{{module_name}}}, [http://www.djangoproject.com/documentation/generic_views/ generic views] no longer create templates based on the {{{module_name}}}. Wherever they used {{{module_name}}}, they now use {{{model_name}}}, a lowercase version of the model name. |
422 | | |
423 | | Note that {{{app_label}}} remains the same. |
424 | | |
425 | | These examples assume models live in {{{myproject/blog/models.py}}}. |
426 | | |
427 | | * Old: {{{blog/entries_archive.html}}} |
428 | | * New: {{{blog/entry_archive.html}}} |
429 | | |
430 | | == Moved admin URLconf to shorten its path == |
431 | | |
432 | | '''Status: Done''' |
433 | | |
434 | | * Old: {{{django.contrib.admin.urls.admin}}} |
435 | | * New: {{{django.contrib.admin.urls}}} |
436 | | |
437 | | == Moved settings into an instance == |
438 | | |
439 | | '''Status: Done''' |
440 | | |
441 | | To make it easier to switch settings in situations where you would need multiple different settings - for example when trying to use multiple django projects within one server context or when using Django apps within a bigger WSGI scenario - the settings were moved out of a dedicated module {{{django.conf.settings}}} into an instance in the {{{django.conf}}} module. So now you need to import the {{{settings}}} object and reference settings as attributes of that instance. |
442 | | |
443 | | Wrappers around the Django machinery can make use of this by exchanging the settings instance with a proxy instance that delegates attribute access to a per-thread or per-location global. |
444 | | |
445 | | * Old: {{{from django.conf.settings import LANGUAGE_CODE}}} |
446 | | * New: {{{from django.conf import settings}}} |
447 | | |
448 | | == Renamed core database tables == |
449 | | |
450 | | '''Status: Done''' |
451 | | |
452 | | Renamed a bunch of the code Django tables. To upgrade, execute this SQL in your database: |
| 13 | == Database changes you'll need to make == |
| 14 | |
| 15 | === Rename core database tables === |
| 16 | |
| 17 | We've renamed a bunch of the code Django tables. To upgrade, execute this SQL in your database: |
| 81 | === Changes to model syntax === |
| 82 | |
| 83 | * {{{class META}}} should now be {{{class Meta}}}. The latter is easier on the eyes. |
| 84 | |
| 85 | * {{{module_name}}} is no longer a valid parameter to {{{class Meta}}}. |
| 86 | |
| 87 | === Moved admin options to 'class Admin' === |
| 88 | |
| 89 | Instead of {{{admin=meta.Admin}}} in the {{{class META}}}, all admin options are in an inner {{{class Admin}}}. |
| 90 | |
| 91 | Old: |
| 92 | {{{ |
| 93 | #!python |
| 94 | class Person(meta.Model): |
| 95 | first_name = meta.CharField(maxlength=30) |
| 96 | last_name = meta.CharField(maxlength=30) |
| 97 | class META: |
| 98 | admin = meta.Admin( |
| 99 | list_display = ('first_name', 'last_name') |
| 100 | ) |
| 101 | }}} |
| 102 | |
| 103 | New: |
| 104 | {{{ |
| 105 | #!python |
| 106 | class Person(models.Model): |
| 107 | first_name = models.CharField(maxlength=30) |
| 108 | last_name = models.CharField(maxlength=30) |
| 109 | class Admin: |
| 110 | list_display = ('first_name', 'last_name') |
| 111 | }}} |
| 112 | |
| 113 | === Model methods no longer automatically have access to datetime and db modules === |
| 114 | |
| 115 | Formerly, each model method magically had access to the {{{datetime}}} module and to the variable {{{db}}}, which represents the current database connection. Now, those have to be imported explicitly. |
| 116 | |
| 117 | Old: |
| 118 | {{{ |
| 119 | #!python |
| 120 | def some_method(self): |
| 121 | print datetime.datetime.now() |
| 122 | cursor = db.cursor() |
| 123 | cursor.execute("UPDATE something;") |
| 124 | }}} |
| 125 | |
| 126 | New: |
| 127 | {{{ |
| 128 | #!python |
| 129 | import datetime |
| 130 | from django.db import connection |
| 131 | |
| 132 | # ... |
| 133 | |
| 134 | def some_method(self): |
| 135 | print datetime.datetime.now() |
| 136 | cursor = connection.cursor() |
| 137 | cursor.execute("UPDATE something;") |
| 138 | }}} |
| 139 | |
| 140 | === Access table-level DB API functions via model classes, not with magic modules === |
| 141 | |
| 142 | All "table-level" functions -- ways of retrieving records tablewide rather than performing instance-specific tasks -- are now accessed via a model class's {{{objects}}} attribute. They aren't direct methods of a model instance object because we want to keep the "table-wide" and "row-specific" namespaces separate. |
| 143 | |
| 144 | {{{ |
| 145 | #!python |
| 146 | from myproject.people.models import Person |
| 147 | p_list = Person.objects.get_list() |
| 148 | p = Person.objects.get_object() |
| 149 | }}} |
| 150 | |
| 151 | This doesn't work from an instance. |
| 152 | |
| 153 | {{{ |
| 154 | #!python |
| 155 | p = Person.objects.get_object(pk=1) |
| 156 | p.objects.get_list() # Raises AttributeError |
| 157 | }}} |
| 158 | |
| 159 | === Override default manager name ("objects") === |
| 160 | |
| 161 | If a model already has an {{{objects}}} attribute, you'll need to specify an alternate name for the magic {{{objects}}}. |
| 162 | |
| 163 | {{{ |
| 164 | #!python |
| 165 | class Person(models.Model): |
| 166 | first_name = models.CharField(maxlength=30) |
| 167 | last_name = models.CharField(maxlength=30) |
| 168 | objects = models.TextField() |
| 169 | people = models.Manager() |
| 170 | |
| 171 | p = Person(first_name='Mary', last_name='Jones', objects='Hello there.') |
| 172 | p.save() |
| 173 | p.objects == 'Hello there.' |
| 174 | Person.people.get_list() |
| 175 | }}} |
| 176 | |
| 177 | === Custom managers, and multiple managers === |
| 178 | |
| 179 | You can create as many managers as you want. When necessary (such as on the admin), Django will use the first one defined, in order. |
| 180 | |
| 181 | If you define at least one custom manager, it will not get the default "objects" manager. |
| 182 | |
| 183 | {{{ |
| 184 | #!python |
| 185 | class Person(models.Model): |
| 186 | first_name = models.CharField(maxlength=30) |
| 187 | last_name = models.CharField(maxlength=30) |
| 188 | people = models.Manager() |
| 189 | fun_people = SomeOtherManager() |
| 190 | }}} |
| 191 | |
| 192 | === Added a more powerful way of overriding model methods, removed hard-coded _pre_save(), _post_save(), etc. === |
| 193 | |
| 194 | Proper subclassing of methods now works, so you can subclass the automatic {{{save()}}} and {{{delete()}}} methods. This removes the need for the {{{_pre_save()}}}, {{{_post_save()}}}, {{{_pre_delete()}}} and {{{_post_delete()}}} hooks -- all of which have been removed. Example: |
| 195 | |
| 196 | {{{ |
| 197 | #!python |
| 198 | class Person(models.Model): |
| 199 | first_name = models.CharField(maxlength=30) |
| 200 | last_name = models.CharField(maxlength=30) |
| 201 | |
| 202 | def save(self): |
| 203 | self.do_something() |
| 204 | super(Person, self).save() # Call the "real" save() method. |
| 205 | self.do_something_else() |
| 206 | }}} |
| 207 | |
| 208 | You can even skip saving (as requested in #1014). |
| 209 | |
| 210 | {{{ |
| 211 | #!python |
| 212 | class Person(models.Model): |
| 213 | first_name = models.CharField(maxlength=30) |
| 214 | last_name = models.CharField(maxlength=30) |
| 215 | |
| 216 | def save(self): |
| 217 | if datetime.date.today() > datetime.date(2005, 1, 1): |
| 218 | super(Person, self).save() # Call the "real" save() method. |
| 219 | else: |
| 220 | # Don't save. |
| 221 | pass |
| 222 | }}} |
| 223 | |
| 224 | === Database connection relocated/renamed === |
| 225 | |
| 226 | For any code that uses the raw database connection, use {{{django.db.connection}}} instead of {{{django.core.db.db}}}. |
| 227 | |
| 228 | Old: |
| 229 | {{{ |
| 230 | #!python |
| 231 | from django.core.db import db |
| 232 | cursor = db.cursor() |
| 233 | }}} |
| 234 | |
| 235 | New: |
| 236 | {{{ |
| 237 | #!python |
| 238 | from django.db import connection |
| 239 | cursor = connection.cursor() |
| 240 | }}} |
| 241 | |
| 242 | Backend-specific functions, if you should need them, are available at {{{django.db.backend}}}. |
| 243 | |
| 244 | Old: |
| 245 | {{{ |
| 246 | #!python |
| 247 | from django.core import db |
| 248 | db.quote_name('foo') |
| 249 | }}} |
| 250 | |
| 251 | New: |
| 252 | {{{ |
| 253 | #!python |
| 254 | from django.db import backend |
| 255 | backend.quote_name('foo') |
| 256 | }}} |
| 257 | |
| 258 | Also, the various backend functionality has been split into three separate modules for each backend -- {{{base.py}}}, {{{creation.py}}} and {{{introspection.py}}}. This is purely for performance and memory savings, so that basic, everyday Django usage doesn't have to load the introspective functionality into memory. |
| 259 | |
| 260 | === Renamed !DoesNotExist exception === |
| 261 | |
| 262 | Instead of {{{people.PersonDoesNotExist}}}, it's {{{Person.DoesNotExist}}}. |
| 263 | |
| 264 | Old: |
| 265 | {{{ |
| 266 | #!python |
| 267 | from django.models.myapp import people |
| 268 | try: |
| 269 | people.get_object(pk=1) |
| 270 | except people.PersonDoesNotExist: |
| 271 | print "Not there" |
| 272 | }}} |
| 273 | |
| 274 | New: |
| 275 | {{{ |
| 276 | #!python |
| 277 | from path.to.myapp.models import Person |
| 278 | try: |
| 279 | Person.objects.get_object(pk=1) |
| 280 | except Person.DoesNotExist: |
| 281 | print "Not there" |
| 282 | }}} |
| 283 | |
| 284 | === Moved admin URLconf to shorten its path === |
| 285 | |
| 286 | You'll need to change your URLconf to {{{include}}} the new location. |
| 287 | |
| 288 | * Old: {{{django.contrib.admin.urls.admin}}} |
| 289 | * New: {{{django.contrib.admin.urls}}} |
| 290 | |
| 291 | === get_object_or_404 and get_list_or_404 now take model classes, not modules === |
| 292 | |
| 293 | Old: |
| 294 | {{{ |
| 295 | #!python |
| 296 | get_object_or_404(polls, pk=1) |
| 297 | }}} |
| 298 | |
| 299 | New: |
| 300 | {{{ |
| 301 | #!python |
| 302 | get_object_or_404(Poll, pk=1) |
| 303 | }}} |
| 304 | |
| 305 | === Moved "auth" and "core" models to django.contrib === |
| 306 | |
| 307 | See http://groups.google.com/group/django-developers/browse_thread/thread/276d071a74543448/7d4b1c40c2d53393 |
| 308 | |
| 309 | * Old: {{{django.models.auth}}} |
| 310 | * New: {{{django.contrib.auth.models}}} |
| 311 | |
| 312 | * Old: {{{django.models.core.sites}}} |
| 313 | * New: {{{django.contrib.sites.models}}} |
| 314 | |
| 315 | * Old: {{{django.models.core.contenttypes}}} |
| 316 | * New: {{{django.contrib.contenttypes.models}}} |
| 317 | |
| 318 | * Old: {{{django.models.core.packages}}} |
| 319 | * New: {{{django.contrib.contenttypes.models}}} ("Packages" will most likely be removed in the future.) |
| 320 | |
| 321 | === Moved Session model and middleware from core to django.contrib === |
| 322 | |
| 323 | The location of the session middleware has changed. |
| 324 | |
| 325 | * Old: {{{django.middleware.sessions.SessionMiddleware}}} |
| 326 | * New: {{{django.contrib.sessions.middleware.SessionMiddleware}}} |
| 327 | |
| 328 | Make sure to update your {{{MIDDLEWARE_CLASSES}}} setting, if you're using sessions. |
| 329 | |
| 330 | Also, the {{{Session}}} model has moved from django/models/core.py to django/contrib/sessions/models.py. If you're accessing the {{{Session}}} model for some reason, note that location change. |
| 331 | |
| 332 | === Changed the parameters you pass to generic views === |
| 333 | |
| 334 | Because there's no longer a concept of {{{module_name}}}, the "info_dicts" passed to [http://www.djangoproject.com/documentation/generic_views/ generic views] no longer accept {{{"app_label"}}} and {{{"module_name"}}}. Instead, pass the parameter {{{"model"}}}, which should be your model class. |
| 335 | |
| 336 | These examples assume models live in {{{myproject/blog/models.py}}}. |
| 337 | |
| 338 | Old: |
| 339 | {{{ |
| 340 | #!python |
| 341 | info_dict = { |
| 342 | 'app_label': 'blog', |
| 343 | 'module_name': 'entries' |
| 344 | } |
| 345 | }}} |
| 346 | |
| 347 | New: |
| 348 | {{{ |
| 349 | #!python |
| 350 | from myproject.blog.models import Entry |
| 351 | info_dict = { |
| 352 | 'model': Entry |
| 353 | } |
| 354 | }}} |
| 355 | |
| 356 | === Changed template names in generic views === |
| 357 | |
| 358 | Because there's no longer a concept of {{{module_name}}}, [http://www.djangoproject.com/documentation/generic_views/ generic views] no longer create templates based on the {{{module_name}}}. Wherever they used {{{module_name}}}, they now use {{{model_name}}}, a lowercase version of the model name. |
| 359 | |
| 360 | Note that {{{app_label}}} remains the same. |
| 361 | |
| 362 | These examples assume models live in {{{myproject/blog/models.py}}}. |
| 363 | |
| 364 | * Old: {{{blog/entries_archive.html}}} |
| 365 | * New: {{{blog/entry_archive.html}}} |
| 366 | |
| 367 | === Moved settings into an instance === |
| 368 | |
| 369 | To make it easier to switch settings in situations where you would need multiple different settings - for example when trying to use multiple django projects within one server context or when using Django apps within a bigger WSGI scenario - the settings were moved out of a dedicated module {{{django.conf.settings}}} into an instance in the {{{django.conf}}} module. So now you need to import the {{{settings}}} object and reference settings as attributes of that instance. |
| 370 | |
| 371 | Wrappers around the Django machinery can make use of this by exchanging the settings instance with a proxy instance that delegates attribute access to a per-thread or per-location global. |
| 372 | |
| 373 | * Old: {{{from django.conf.settings import LANGUAGE_CODE}}} |
| 374 | * New: {{{from django.conf import settings}}} |
| 375 | |
| 376 | === Removed !SilentVariableFailure exception === |
| 377 | |
| 378 | Old behavior: Any exception that subclasses {{{django.core.template.SilentVariableFailure}}} fails silently in the template system. |
| 379 | |
| 380 | New behavior: Any exception that has a {{{silent_variable_failure}}} attribute fails silently in the template system. {{{django.core.template.SilentVariableFailure}}} no longer exists. |
| 381 | |
| 382 | |
| 383 | |
| 384 | |
| 385 | |
| 386 | == New functionality you can start using == |
| 387 | |
| 388 | === Models support properties === |
| 389 | |
| 390 | Unlike before, properties are supported on models. |
| 391 | |
| 392 | {{{ |
| 393 | #!python |
| 394 | from django.db import models |
| 395 | |
| 396 | class Person(models.Model): |
| 397 | first_name = models.CharField(maxlength=30) |
| 398 | last_name = models.CharField(maxlength=30) |
| 399 | |
| 400 | def _get_full_name(self): |
| 401 | return "%s %s" % (self.first_name, self.last_name) |
| 402 | full_name = property(_get_full_name) |
| 403 | }}} |
| 404 | |
| 405 | === You can override table-level functions === |
| 406 | |
| 407 | You can override any table-level functions, such as {{{get_list()}}} or {{{get_object()}}}. Do this by creating a custom {{{models.Manager}}} subclass and passing it to your model. |
| 408 | |
| 409 | {{{ |
| 410 | #!python |
| 411 | from django.db import models |
| 412 | class PersonManager(models.Manager): |
| 413 | def get_list(self, **kwargs): |
| 414 | # Changes get_list() to hard-code a limit=10. |
| 415 | kwargs['limit'] = 10 |
| 416 | return models.Manager.get_list(self, **kwargs) # Call the "real" get_list() method. |
| 417 | |
| 418 | class Person(models.Model): |
| 419 | first_name = models.CharField(maxlength=30) |
| 420 | last_name = models.CharField(maxlength=30) |
| 421 | objects = PersonManager() |
| 422 | }}} |
| 423 | |
| 424 | If a manager needs to access its associated class, it should use {{{self.klass}}}. Example: |
| 425 | |
| 426 | {{{ |
| 427 | #!python |
| 428 | class PersonManager(models.Manager): |
| 429 | def get_fun_person(self): |
| 430 | try: |
| 431 | return self.get_object(fun__exact=True) |
| 432 | except self.klass.DoesNotExist: |
| 433 | print "Doesn't exist." |
| 434 | }}} |
| 435 | |
| 436 | |
| 437 | |
| 438 | |
| 439 | |
| 440 | |
| 441 | == Stuff that still needs to be done == |
| 442 | |
| 443 | === Automatic manipulators === |
| 444 | |
| 445 | '''Status: Mostly done, with some quirks left''' |
| 446 | |
| 447 | Old: |
| 448 | {{{ |
| 449 | #!python |
| 450 | from django.models.myapp import people |
| 451 | m1 = people.AddManipulator() |
| 452 | m2 = people.ChangeManipulator(3) |
| 453 | }}} |
| 454 | |
| 455 | New: |
| 456 | {{{ |
| 457 | #!python |
| 458 | from path.to.myapp.models import Person |
| 459 | m1 = Person.AddManipulator() |
| 460 | m2 = Person.ChangeManipulator(3) |
| 461 | }}} |
| 462 | |