Changes between Version 45 and Version 46 of RemovingTheMagic


Ignore:
Timestamp:
Jan 22, 2006, 9:22:12 PM (19 years ago)
Author:
Adrian Holovaty
Comment:

Reorganized page in to database changes, code changes, functionality additions and to-do

Legend:

Unmodified
Added
Removed
Modified
  • RemovingTheMagic

    v45 v46  
    1111Play with it! The branch is available via Subversion at http://code.djangoproject.com/svn/django/branches/magic-removal .
    1212
    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
     17We've renamed a bunch of the code Django tables. To upgrade, execute this SQL in your database:
    45318
    45419{{{
     
    46934}}}
    47035
    471 == Changed database table naming scheme ==
    472 
    473 '''Status: Done'''
     36=== Database table-naming scheme has been changed ===
    47437
    47538Database table names formerly were created by joining the app_label and module_name. Example: {{{polls_polls}}}.
     
    48144To upgrade, you'll either have to explicitly set {{{db_table}}} in your models or rename your database tables to fit the new naming scheme Django expects.
    48245
    483 == Namespace simplification ==
    484 
    485 '''Status: Done'''
     46
     47
     48
     49
     50
     51== Code changes you'll need to make ==
     52
     53=== Model class and Field classes renamed/relocated ===
     54
     55Change your models to import from {{{django.db.models}}} instead of {{{django.core.meta}}}.
     56
     57{{{
     58#!python
     59from django.db import models
     60
     61class Person(models.Model):
     62    first_name = models.CharField(maxlength=30)
     63    last_name = models.CharField(maxlength=30)
     64}}}
     65
     66=== Interact directly with model classes, not with magic modules ===
     67
     68Import the model class directly from the module in which it was defined. No more {{{django.models.*}}} magic.
     69
     70{{{
     71#!python
     72from myproject.people.models import Person
     73p = Person(first_name='John', last_name='Smith')
     74p.save()
     75}}}
     76
     77=== Namespace simplification ===
    48678
    48779See NamespaceSimplification for details.
    48880
     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
     89Instead of {{{admin=meta.Admin}}} in the {{{class META}}}, all admin options are in an inner {{{class Admin}}}.
     90
     91Old:
     92{{{
     93#!python
     94class 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
     103New:
     104{{{
     105#!python
     106class 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
     115Formerly, 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
     117Old:
     118{{{
     119#!python
     120    def some_method(self):
     121        print datetime.datetime.now()
     122        cursor = db.cursor()
     123        cursor.execute("UPDATE something;")
     124}}}
     125
     126New:
     127{{{
     128#!python
     129import datetime
     130from 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
     142All "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
     146from myproject.people.models import Person
     147p_list = Person.objects.get_list()
     148p = Person.objects.get_object()
     149}}}
     150
     151This doesn't work from an instance.
     152
     153{{{
     154#!python
     155p = Person.objects.get_object(pk=1)
     156p.objects.get_list() # Raises AttributeError
     157}}}
     158
     159=== Override default manager name ("objects") ===
     160
     161If a model already has an {{{objects}}} attribute, you'll need to specify an alternate name for the magic {{{objects}}}.
     162
     163{{{
     164#!python
     165class 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
     171p = Person(first_name='Mary', last_name='Jones', objects='Hello there.')
     172p.save()
     173p.objects == 'Hello there.'
     174Person.people.get_list()
     175}}}
     176
     177=== Custom managers, and multiple managers ===
     178
     179You 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
     181If you define at least one custom manager, it will not get the default "objects" manager.
     182
     183{{{
     184#!python
     185class 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
     194Proper 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
     198class 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
     208You can even skip saving (as requested in #1014).
     209
     210{{{
     211#!python
     212class 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
     226For any code that uses the raw database connection, use {{{django.db.connection}}} instead of {{{django.core.db.db}}}.
     227
     228Old:
     229{{{
     230#!python
     231from django.core.db import db
     232cursor = db.cursor()
     233}}}
     234
     235New:
     236{{{
     237#!python
     238from django.db import connection
     239cursor = connection.cursor()
     240}}}
     241
     242Backend-specific functions, if you should need them, are available at {{{django.db.backend}}}.
     243
     244Old:
     245{{{
     246#!python
     247from django.core import db
     248db.quote_name('foo')
     249}}}
     250
     251New:
     252{{{
     253#!python
     254from django.db import backend
     255backend.quote_name('foo')
     256}}}
     257
     258Also, 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
     262Instead of {{{people.PersonDoesNotExist}}}, it's {{{Person.DoesNotExist}}}.
     263
     264Old:
     265{{{
     266#!python
     267from django.models.myapp import people
     268try:
     269    people.get_object(pk=1)
     270except people.PersonDoesNotExist:
     271    print "Not there"
     272}}}
     273
     274New:
     275{{{
     276#!python
     277from path.to.myapp.models import Person
     278try:
     279    Person.objects.get_object(pk=1)
     280except Person.DoesNotExist:
     281    print "Not there"
     282}}}
     283
     284=== Moved admin URLconf to shorten its path ===
     285
     286You'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
     293Old:
     294{{{
     295#!python
     296get_object_or_404(polls, pk=1)
     297}}}
     298
     299New:
     300{{{
     301#!python
     302get_object_or_404(Poll, pk=1)
     303}}}
     304
     305=== Moved "auth" and "core" models to django.contrib ===
     306
     307See 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
     323The location of the session middleware has changed.
     324
     325 * Old: {{{django.middleware.sessions.SessionMiddleware}}}
     326 * New: {{{django.contrib.sessions.middleware.SessionMiddleware}}}
     327
     328Make sure to update your {{{MIDDLEWARE_CLASSES}}} setting, if you're using sessions.
     329
     330Also, 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
     334Because 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
     336These examples assume models live in {{{myproject/blog/models.py}}}.
     337
     338Old:
     339{{{
     340#!python
     341info_dict = {
     342    'app_label': 'blog',
     343    'module_name': 'entries'
     344}
     345}}}
     346
     347New:
     348{{{
     349#!python
     350from myproject.blog.models import Entry
     351info_dict = {
     352    'model': Entry
     353}
     354}}}
     355
     356=== Changed template names in generic views ===
     357
     358Because 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
     360Note that {{{app_label}}} remains the same.
     361
     362These 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
     369To 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
     371Wrappers 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
     378Old behavior: Any exception that subclasses {{{django.core.template.SilentVariableFailure}}} fails silently in the template system.
     379
     380New 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
     390Unlike before, properties are supported on models.
     391
     392{{{
     393#!python
     394from django.db import models
     395
     396class 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
     407You 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
     411from django.db import models
     412class 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
     418class Person(models.Model):
     419    first_name = models.CharField(maxlength=30)
     420    last_name = models.CharField(maxlength=30)
     421    objects = PersonManager()
     422}}}
     423
     424If a manager needs to access its associated class, it should use {{{self.klass}}}. Example:
     425
     426{{{
     427#!python
     428class 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
     447Old:
     448{{{
     449#!python
     450from django.models.myapp import people
     451m1 = people.AddManipulator()
     452m2 = people.ChangeManipulator(3)
     453}}}
     454
     455New:
     456{{{
     457#!python
     458from path.to.myapp.models import Person
     459m1 = Person.AddManipulator()
     460m2 = Person.ChangeManipulator(3)
     461}}}
     462
    489463== Change subclassing syntax ==
    490464
Back to Top