Version 19 (modified by adrian, 10 years ago) (diff)

Added example table so people can easily see what's changing.

Fields as descriptors

This proposal changes the Django database API substantially.

Lookup typeOld syntaxNew syntax
Foreign keyarticle.get_reporter()article.reporter
Foreign key fieldarticle.reporter_id (Doesn't do a DB query)article.reporter.id (Doesn't do a DB query)
Foreign key (other end)reporter.get_article_list()reporter.article_set
Foreign key (other end)reporter.get_article_list(headline__startswith="This")reporter.article_set.filter(headline__startswith="This")
Adding related objectsreporter.add_article(headline="John's second story", pub_date=datetime(2005, 7, 29))reporter.article_set.add(headline="John's second story", pub_date=datetime(2005, 7, 29))
Many-to-manyarticle.get_site_list()article.site_set
Many-to-many (adding)article.set_sites([s1.id, s2.id])article.site_set.clear(); article.site_set.add(s1); article.site_set.add(s2);
Many-to-many (deleting)article.set_sites([s1.id])article.site_set.remove(s2)
Orderingreporter.get_article_list(order_by=['headline'])reporter.article_set.order_by('headline')

Implementation details:

  • Fields would be left as members by the metaclass.
  • The real 'data' would end up either in name-mangled instance members, or in a private dictionary.
  • When accessed via the class, they would return themselves. eg Reporter.name would be a field class you can inspect. This would make introspection as in the admin easier.
  • When accessed via the instance, they would return a representation of the value they hold. This could be
    • the simple data instance from the private dict that was fetched from the db.
    • a lazily computed value, eg a BLOB field which would be fetched from the db on first access, not at initial load time, or a ForeignKey relationship that hasn't been loaded yet.
  • ForeignKeys and other related fields would place another descriptor on the related class. When accessed via the instance, this would return a lazy collection object. It will act like a set (an ordered one if there is an ordering involved).

This would also support other methods in favour of the get_related_list things.

Methods which change the meaning of the lazy collection are as follows:

  • .filter(): adds query parameters using Django's normal DB query syntax.
  • .order_by(): adds/changes ordering parameters.

The collection supports the appropriate collection protocols : It acts as a set, with ordering if there is an ordering in the underlying query. The query is only done when a collection protocol is used. Sometimes, multiple queries will be used to save traffic, eg if you access reporter.article_set[10000:10020] and then reporter.article_set[:20].

Query syntax

In further discussions about this idea, it seemed consistent that both the manager ( Person.objects ) and relationship end points, (reporter.articles) would act as lazy sets of instances (base class QuerySet?) , with these 'refinement' methods on them (filter, order_by, etc).

eg

Person.objects.get_list() becomes Person.objects

Person.objects.get_list(name__startswith="R") becomes Person.objects.filter(name__startswith="R")

Query sets would be combinable with the standard set operators, which would be less clumsy than the current complex= query syntax. eg ar_list = Person.objects.get_list(complex=Q(name_startswith="R")|Q(name_startswith="A") ) goes to (idiosyncratically)

people = Person.objects
ar_set = people.filter(name_startswith="R") | people.filter(name_startswith="A")
Back to Top