I think it is time to add a new model classmethod from_db() to Django.

The idea is to allow customization of object initialization when loading 
from database. Instead of calling directly model.__init__ from the queryset 
iterators, Django calls model_cls.from_db(). The default implementation 
calls just model.__init__, but by overriding from_db() it will be possible 
to do interesting things. For example:
  1) It allows for faster loading of models. See 
https://code.djangoproject.com/ticket/19501 for some benchmarks.
  2) Possibility to return polymorphic classes from querysets. For example 
for STI:
    def from_db(cls, using, fields, values):
        # Assume database has type column, and the class contains a 
_type_map pointing to the wanted class for each different type.
        data = dict(zip(fields, values))
        model_cls = cls._type_map[data['type'])
        new = model_cls(**data)
        new._state = ModelState(using, adding=False)
        return new

  3) Allow differentiating database loading from initialization in user 
code. For example (pseudo-codeish) automatic update_fields on save():

    class AutoTracking(models.Model):
        fields...

        @classmethod
        def from_db(cls, using, fields, values):
            new = super().from_db(using, fields, values)
            # This step is surprisingly hard to do correctly at the moment!
            # Can't use overridden __init__ or signals as they don't know 
if the model is loaded from the
            # database or not.
            new._old_data = dict(zip(fields, values))
            return new

        def save(...):
            if update_fields is None:
                update_fields = set()
                for attr_name, v in self._old_data.items():
                    if getattr(self, attr_name) != v:
                        update_fields.add(attr_name)
            super().save(...)


So, there are several advantages to adding from_db(). The only problem I 
can see with this approach is that the default model initialization code 
path will be around 5%-10% slower due to the extra from_db() call for each 
row. To me that isn't big enough slowdown to worry about. In addition, as 
shown in #19501, usage of from_db() allows for significantly faster model 
loading for special cases.

The patches in #19501 are IMO too complex. The patches try to automatically 
detect when we can skip calling model.__init__. Instead we should just add 
the from_db() hook without any fast-path automation.

Any thoughts on this idea?

 - Anssi

On Friday, May 16, 2014 5:06:18 PM UTC+3, Carl Meyer wrote:
>
> On 05/16/2014 04:46 AM, Shai Berger wrote: 
> > On Monday 12 May 2014 12:27:01 Thomas Güttler wrote: 
> >> Single Table Inheritance is used by ruby-on-rails and SQLAlchemy. 
> >> 
> >> Are there reasons why it is used in django? 
> >> 
> > 
> > Essentially, STI is a form of database denormalization. I think Django 
> should 
> > not encourage this. 
>
> I agree. 
>
> >> I would love to see a polymorphic inheritance solution in django. 
> >> 
> > 
> > Just to spell things out: You want to have models A, B(A), C(A) and 
> D(B), so 
> > that list(A.objects.all()) returns a list of objects of different types. 
> > 
> > This sounds like a good idea in general, but there are devils in the 
> > implementation details. In particular, I'd like to separate this issue 
> from 
> > the issue of STI -- polymorphism is an issue of processing the retrieved 
> > records, and need not be tightly coupled to the database layout. The 
> > polymorphism solution should work whether the records are fetched by the 
> > equivalent of A.objects.all() or 
> > A.objects.select_related(child_classes).all(). 
> > 
> > I think you can sort-of achieve STI by doing it the other way around: 
> Define 
> > all your hierarchy as abstract models, with one concrete model 
> inheriting all 
> > of them (I suspect any STI implementation in Django would have to do 
> something 
> > very similar "behind the scenes"). Pros and cons (as well as testing if 
> this 
> > actually works) are left as an exercise to the reader. 
> > 
> >> I know that there are third party apps which provide this, but 
> >> something like this should be in the core. 
> >> 
> > 
> > If you ignore STI, I think it is quite straightforward to solve this 
> with a 
> > parent model class which adds a type field, and manager methods to add 
> the 
> > select_related calls and "interpret" the type field properly; so I don't 
> see an 
> > immediate need for inclusion in core. 
>
> You don't even need the "type" field, you can just select_related all 
> the subclasses and then test when iterating over the queryset which one 
> exists for each record. This is what InheritanceManager in 
> django-model-utils does. 
>
> I don't see a need to have this in core either. It seems to me almost a 
> perfect example of the occasionally-useful-but-not-essential 
> functionality that is well served by third-party packages. (IMO concrete 
> model inheritance is more often than not a questionable model-design in 
> the first place, so Django shouldn't be adding more support around it to 
> encourage its use.) 
>
> Carl 
>
>

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-developers+unsubscr...@googlegroups.com.
To post to this group, send email to django-developers@googlegroups.com.
Visit this group at http://groups.google.com/group/django-developers.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-developers/a7fdc34c-2a9c-4c58-b849-8a5e3c8a076c%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to