[that link is going down -- hence, I'm pasting the contents below]

Django ORM does not support inheritance. Inheritance is a good thing.
Often I want some of the database models to share some common fields
and the same manager. For example consider the case of adding the
delete proxy.

class RetainDeletesMixin(object):

    class NonDeleted(models.Manager):

        def get_query_set(self):
            return super(NonDeleted, self).get_query_set().filter(
                delete_flag=0)

    delete_flag = models.IntegerField(null=True, blank=True,
default=0)

    objects = NonDeleted()

    def delete(self):
        self.delete_flag = 1
        self.save()


class Item(RetainDeletesMixin, models.Model):
    ...

Here we are extending the model Item by adding a mixin that overrides
the delete method - so that when we do item.delete() the model is not
actually deleted but merely flagged (delete_flag=1) so. This means the
get_query_set method should also be told not to return the flagged
models. In Python speak,

>>> items = Item.objects.all()
>>> item = items[0]
>>> len(items)
1
>>> item.delete_flag
0
>>> item.delete()
>>> item.delete_flag
1
>>> len(Item.objects.all())
0
>>>

But this will not work. Django does not consider delete_flag, which is
inherited from RetainDeletesMixin, as a database column at all. So I
came up with the following metaclass hack which enables you to design
the django models based on the inheritance pattern like the above,

class RetainDeletesMixin(object):

    class NonDeleted(models.Manager):

        def get_query_set(self):
            # Ideally, we should be using `super` here but we don't
because
            # the variable `NonDeleted` will not be accessible once we
            # 'copy' this class in the metaclass.
            return models.Manager.get_query_set(self).filter(
                delete_flag=0)

    delete_flag = models.IntegerField(null=True, blank=True,
default=0)

    objects = NonDeleted()

    def delete(self):
        self.delete_flag = 1
        self.save()

def django_extends(base):

    class ProxyMetaClass(models.Model.__metaclass__):

        def __new__(cls, name, bases, attrs):
            # The following attributes must be moved *prior* to
deferring
            # execution to the models.Model's metaclass, because they
            # will be manipulated by __new__
            #  - models.fields.Field instances
            #  - objects (ModelManager)
            for key in dir(base):
                if not key.startswith('_'):
                    obj = getattr(base, key)
                    if isinstance(obj, models.fields.Field) or \
                            key == 'objects':
                        attrs[key] = obj
                        delattr(base, key)
                    # Delete objects that have attribute
'contribute_to_class'
                    # for otherwise that will break in
                    # base.py:Model.add_to_class
                    # Eg: inner classes inherited from models.Manager
                    elif hasattr(obj, 'contribute_to_class'):
                        delattr(base, key)

            return super(ProxyMetaClass,
                         cls).__new__(cls, name,
                                      tuple([base]+list(bases)),
                                      attrs)

    frame = sys._getframe(1)
    frame.f_locals['__metaclass__'] = ProxyMetaClass

class Item(models.Model):
    django_extends(RetainDeletesMixin)
    ...

What this basically does is - copy the database fields and objects
from the base class (RetainDeletesMixin) to the derived class (Item)
so that Django will recognize it upon processing in ModelBase. It also
makes RetainDeletesMixin a base class of Item. I have not tested this
code extensively, but it works for the models that I have written in
our application.


On Dec 19, 1:53 pm, Sridhar Ratnakumar <[EMAIL PROTECTED]>
wrote:
> I wanted to share this little hack of mine - conceptual (not database-
> based)inheritancefordjango:http://nearfar.org/blog/django-model-inheritance.html
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Django developers" group.
To post to this group, send email to django-developers@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/django-developers?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to