|Version 35 (modified by 11 years ago) (diff),|
Removing the magic
The "magic-removal" branch aims to make several sweeping changes to the Django codebase. Most changes involve the database API and removing some of its unneeded magic, which confuses newbies and is a bit of a wart.
This document explains the changes in the branch.
These changes will be integrated into the next Django release (either 0.91 or 0.92, depending on whether we decide to release a 0.91 as a "middle" release for people who want new features currently in trunk but don't want to wait for magic-removal).
Models support properties
Unlike before, properties are supported on models.
from django.db import models class Person(models.Model): first_name = models.CharField(maxlength=30) last_name = models.CharField(maxlength=30) def _get_full_name(self): return "%s %s" % (self.first_name, self.last_name) full_name = property(_get_full_name)
Model class and Field classes renamed/relocated
Difference: Import is from
django.db.models instead of
django.core.meta. This is easier to remember.
from django.db import models class Person(models.Model): first_name = models.CharField(maxlength=30) last_name = models.CharField(maxlength=30)
Database connection relocated/renamed
The connection is now available at
django.db.connection. This is easier to remember, clearer and more consistent.
from django.core.db import db cursor = db.cursor()
from django.db import connection cursor = connection.cursor()
Backend-specific functions, if you should need them, are available at
from django.core import db db.quote_name('foo')
from django.db import backend backend.quote_name('foo')
Also, the various backend functionality has been split into three separate modules for each backend --
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.
Interact directly with model classes, not with magic modules
Import the model class directly from the module in which it was defined. No more
from myproject.people.models import Person p = Person(first_name='John', last_name='Smith') p.save()
This also removes the need for the
Access table-level DB API functions via model classes, not with magic modules
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.
from myproject.people.models import Person p_list = Person.objects.get_list() p = Person.objects.get_object()
This doesn't work from an instance.
p = Person.objects.get_object(pk=1) p.objects.get_list() # Raises AttributeError
Override default manager name ("objects")
If a model already has an
objects attribute, you'll need to specify an alternate name for the magic
class Person(models.Model): first_name = models.CharField(maxlength=30) last_name = models.CharField(maxlength=30) objects = models.TextField() people = models.Manager() p = Person(first_name='Mary', last_name='Jones', objects='Hello there.') p.save() p.objects == 'Hello there.' Person.people.get_list()
Custom managers, and multiple managers
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.
If you define at least one custom manager, it will not get the default "objects" manager.
class Person(models.Model): first_name = models.CharField(maxlength=30) last_name = models.CharField(maxlength=30) people = models.Manager() fun_people = SomeOtherManager()
Added a more powerful way of overriding model methods, removed hard-coded _pre_save(), _post_save(), etc.
Proper subclassing of methods now works, so you can subclass the automatic
delete() methods. This removes the need for the
_post_delete() hooks -- all of which have been removed. Example:
class Person(models.Model): first_name = models.CharField(maxlength=30) last_name = models.CharField(maxlength=30) def save(self): self.do_something() super(Person, self).save(self) # Call the "real" save() method. self.do_something_else()
You can even skip saving (as requested in #1014).
class Person(models.Model): first_name = models.CharField(maxlength=30) last_name = models.CharField(maxlength=30) def save(self): if datetime.date.today() > datetime.date(2005, 1, 1): super(Person, self).save(self) # Call the "real" save() method. else: # Don't save. pass
New API functionality: Overriding table-level functions
You can override any table-level functions, such as
get_object(). Do this by creating a custom
models.Manager subclass and passing it to your model. The term "manager" could be replaced with some other word.
from django.db import models class PersonManager(models.Manager): def get_list(self, **kwargs): # Changes get_list() to hard-code a limit=10. kwargs['limit'] = 10 return models.Manager.get_list(self, **kwargs) # Call the "real" get_list() method. class Person(models.Model): first_name = models.CharField(maxlength=30) last_name = models.CharField(maxlength=30) objects = PersonManager()
If a manager needs to access its associated class, it should use
class PersonManager(models.Manager): def get_fun_person(self): try: return self.get_object(fun__exact=True) except self.klass.DoesNotExist: print "Doesn't exist."
Renamed DoesNotExist exception
from django.models.myapp import people try: people.get_object(pk=1) except people.PersonDoesNotExist: print "Not there"
from path.to.myapp.models import Person try: Person.objects.get_object(pk=1) except Person.DoesNotExist: print "Not there"
Removed SilentVariableFailure exception
Old behavior: Any exception that subclasses
django.core.template.SilentVariableFailure fails silently in the template system.
New behavior: Any exception that has a
silent_variable_failure attribute fails silently in the template system.
django.core.template.SilentVariableFailure no longer exists.
Status: Mostly done, with some quirks left
from django.models.myapp import people m1 = people.AddManipulator() m2 = people.ChangeManipulator(3)
from path.to.myapp.models import Person m1 = Person.AddManipulator() m2 = Person.ChangeManipulator(3)
Renamed 'class META' to 'class Meta'
class META within models should now be
class Meta. The latter is nicer on the eyes.
get_object_or_404 and get_list_or_404 now take model classes, not modules
Model methods no longer automatically have access to datetime and db modules
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.
def some_method(self): print datetime.datetime.now() cursor = db.cursor() cursor.execute("UPDATE something;")
import datetime from django.db import connection # ... def some_method(self): print datetime.datetime.now() cursor = connection.cursor() cursor.execute("UPDATE something;")
Move "auth" and "core" models to django.contrib
django.contrib.contenttypes.models(This will most likely be removed in the future.)
Moved Session model and middleware from core to django.contrib
The location of the session middleware has changed.
Make sure to update your
MIDDLEWARE_CLASSES setting, if you're using sessions.
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.
Change subclassing syntax
Status: Not done yet
Move admin options to 'class Admin'
Status: Done in 
admin=meta.Admin in the
class META, we'll move all admin options into an inner
Database lookup API changes
Status: Not done yet
See DescriptorFields, rjwittams' proposal on the API changes.
Status: Not done yet
See NamespaceSimplification for details.