|Version 19 (modified by adrian, 10 years ago) (diff)|
Fields as descriptors
This proposal changes the Django database API substantially.
|Lookup type||Old syntax||New syntax|
|Foreign key field||article.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 objects||reporter.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-many (adding)||article.set_sites([s1.id, s2.id])||article.site_set.clear(); article.site_set.add(s1); article.site_set.add(s2);|
- 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].
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).
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")