Code


Version 29 (modified by adrian, 8 years ago) (diff)

Updated page to reflect newest thinking

Fields as descriptors

This proposal changes the Django database API substantially.

The examples on this page use the following models:

class Reporter(models.Model):
    fname = models.CharField(maxlength=30)
    lname = models.CharField(maxlength=30)

class Site(models.Model):
    name = models.CharField(maxlength=20)

class Article(models.Model):
    headline = models.CharField(maxlength=50)
    reporter = models.ForeignKey(Reporter)
    pub_date = models.DateField()
    sites = models.ManyToManyField(Site)

Overview

This proposal changes the way foreign-key and many-to-many related objects are accessed via the database-object API. It also changes the functionality of get_list(), get_object(), etc.

Under this proposal, each manager gets the following methods:

class Manager:
    def all(self):
        # returns a Query instance -- old get_list()

    def filter(self, **kwargs):
        # returns a Query instance -- old get_list(**kwargs)

    def get(self, **kwargs):
        # returns a Query instance -- old get_object()

    def values(self, **kwargs):
        # returns a ValuesQuery instance -- old get_values()

    def in_bulk(self, **kwargs):
        # return an InBulkQuery instance -- old get_in_bulk()

class Query:
    def filter(self, **kwargs):
        # returns a Query instance

    def unique(self, true_or_false):
        # returns a Query instance

    def order_by(self, *field_names):
        # returns a Query instance

    def count(self):
        # returns a Query instance

class ValuesQuery(Query):
    # Only difference is the output format.

class InBulkQuery(Query):
    # Only difference is the output format.

Any of these Manager methods can be overridden by custom managers, of course.

Related-object lookup uses the default manager of the related object, which means the API for accessing related objects is completely consistent with the API for accessing objects via a manager.

Another improvement is that the lookup type "__exact" can be left off.

Examples

Old syntax New syntax
reporters.get_list() Reporter.objects.all()
reporters.get_list(fname__exact='John') Reporter.objects.filter(fname='John')
reporters.get_list(order_by=('-lname', 'fname')) Reporter.objects.order_by('-lname', 'fname')
reporters.get_list(fname__exact='John', order_by=('lname',)) Reporter.objects.filter(fname='John').order_by('lname')
reporters.get_object(pk=3) Reporter.objects.get(pk=3)
reporters.get_object(fname__contains='John') Reporter.objects.get(fname__contains='John')
reporters.get_list(distinct=True) Reporter.objects.distinct()
reporters.get_values() Reporter.objects.values()
reporters.get_in_bulk([1, 2]) Reporter.objects.in_bulk([1, 2])
reporters.get_in_bulk([1, 2], fname__exact='John') Reporter.objects.filter(fname='John').in_bulk([1, 2])
Date lookup
articles.get_pub_date_list('year') {{{Article.objects.dates('pub_date', 'year')
Many-to-one related lookup
article_obj.reporter_id article_obj.reporter.id
article_obj.get_reporter() article_obj.reporter
reporter_obj.get_article_list() reporter_obj.article_set.all()
reporter_obj.get_article_list(headline__exact='Hello') reporter_obj.article_set.filter(headline='Hello')
reporter_obj.get_article_count() reporter_obj.article_set.count()
reporter_obj.add_article(headline='Foo') reporter_obj.article_set.add(headline='Foo')
(Alternate syntax) reporter_obj.article_set.add(article_obj)
("values" lookup, etc., not previously possible) reporter_obj.article_set.values()
Many-to-many related lookup
article_obj.get_site_list() article_obj.sites.all()
article_obj.set_sites([s1.id, s2.id]) article_obj.sites.clear(); article_obj.sites.add(s1); article_obj.sites.add(s2)
article_obj.set_sites([s1.id]) # deletion article_obj.sites.remove(s2)
site_obj.get_reporter_list() site_obj.reporter_set.all()