views:

243

answers:

3

I have a Django model:

class Foo(models.Model):
    name = models.CharField(unique=True)
    attribute1 = models.FloatField(null=True, blank=True)
    attribute2 = models.FloatField(null=True, blank=True)
    attribute3 = models.BooleanField(null=True, blank=True)
    attribute4 = models.CharField(null=True, blank=True)
    inherit = models.ForeignKey('self', related_name='children', null=True, blank=True)

I'd like that when inherit is not null/blank, that attribute1 and attribute2 etc. are inherited from the parent object inherit so that when I access the attributes I get the values of the parent. I dont care about setting values in the child.

I thought about using model methods eg.:

_attribute1 = models.FloatField(null=True, blank=True)
get_attribute1(self):
    if self.inherit:
        return self.inherit._attribute1
    else:
        return self._attribute1
set_attribute1(self, value):
    if not self.inherit:
        self._attribute1 = value
attribute1 = property(get_attribute1, set_attribute1)

But it seems ugly since I have about 10 attributes. Is there a better way to do this?

+2  A: 

Perhaps the __getattr__ and __setattr__ are a good choice here.

class Foo(models.Model):
    name = models.CharField(unique=True)
    inherit = models.ForeignKey('self', related_name='children', null=True, blank=True)

    _attribute1 = models.FloatField(null=True, blank=True)
    _attribute2 = models.FloatField(null=True, blank=True)
    _attribute3 = models.BooleanField(null=True, blank=True)
    _attribute4 = models.CharField(null=True, blank=True)

    def __getattr__(self, name):
        if self.inherit and hasattr(self.inherit, name):
            return getattr(self.inherit, name, None)
        elif hasattr(self, '_'+name):
            return getattr(self, '_'+name, None)
        return super(Foo, self).__getattr__(name)

    def __setattr__(self, name, value):
        if self.inherit and hasattr(self.inherit, name):
            return setattr(self.inherit, name, value)
        elif hasattr(self, '_'+name):
            return self.__dict__[name] = value
        return super(Foo, self).__setattr__(name, value)

Disclaimer: I didn't try running this

T. Stone
A: 

You could use a Descriptor:

class InheritedAttribute(object):
    def __init__(self, name):
        self.attname = '_' + name

    def __get__(self, instance, owner):
        if instance.inherit:
            return getattr(instance.inherit, self.attname)
        else:
            return getattr(instance, self.attname)

    def __set__(self, instance, value):
        setattr(instance, self.attname, value)

the Model would look like this:

class Foo(models.Model)
    name = models.CharField(unique=True)
    _attribute1 = models.FloatField(null=True, blank=True)
    _attribute2 = models.FloatField(null=True, blank=True)
    _attribute3 = models.BooleanField(null=True, blank=True)
    _attribute4 = models.CharField(null=True, blank=True)
    inherit = models.ForeignKey('self', related_name='children', null=True, blank=True)
    attribute1 = InheritedAttribute('attribute1')
    attribute2 = InheritedAttribute('attribute2')
    attribute3 = InheritedAttribute('attribute3')
    attribute1 = InheritedAttribute('attribute4')

This could probably be enhanced with a Metaclass that automatically hides the model fields behind a descriptor.

piquadrat
A: 

You could use Signals to generate accessor-method, as shown here

vikingosegundo