Version 13 (modified by Adrian Holovaty, 18 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.

Models support properties

Status: Done

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

Status: Done

Difference: Import is from django.db.models instead of django.core.meta. This is easier to remember. However, models may not be the best name for it.

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

Status: Not yet done

This is easier to remember, clearer and more consistent.

Old:

from django.core.db import db
cursor = db.cursor()

New:

from django.db import connection
cursor = connection.cursor()

Interact directly with model classes, not with magic modules

Status: Done

Import the model class directly from the module in which it was defined. No more django.models.* magic.

from myproject.people.models import Person
p = Person(first_name='John', last_name='Smith')
p.save()

This also removes the need for the module_name parameter.

Access table-level DB API functions via model classes, not with magic modules

Status: Done

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")

Status: Not yet done

If a model already has an objects attribute, you'll need to specify an alternate name for the magic objects.

class Person(models.Model):
    first_name = models.CharField(maxlength=30)
    last_name = models.CharField(maxlength=30)
    objects = models.TextField()
    class META:
        manager = Manager(name='more_objects')

p = Person(first_name='Mary', last_name='Jones', objects='Hello there.')
p.save()
p.objects == 'Hello there.'
Person.more_objects.get_list()

API usage: Overriding model methods (and pre- and post-save hooks)

Status: Done

Proper subclassing of methods will now work, so you can subclass the automatic save() and delete() methods. This removes the need for the _pre_save() and _post_save() hooks. 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

API usage: Overriding table-level functions

Status: Not yet done

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. 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 super(PersonManager, self).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)
    class META:
        manager = PersonManager()

Other "module"-level members: Automatic manipulators and ObjectDoesNotExist exception

Status: Not yet done

Person.get_add_manipulator()
Person.get_change_manipulator()
Person.DoesNotExist

Database lookup API changes

See DescriptorFields, rjwittams' proposal on the API changes.

Note: See TracWiki for help on using the wiki.
Back to Top