C.B.
C.B.

Reputation: 8336

Any way to use a Django Model as an Interface?

I have a Base class for many Subclasses, and the only thing changing within the subclasses is a certain method (the template pattern). However I am stuck and can't get it to work.

class Base(models.Model):
    _value = models.CharField(max_length=200)
    _name = models.CharField(max_length=200)
    user = models.ForeignKey(User, related_name="some_set")

    #used as a property
    def value():
        def fget(self):
            self.refresh()
            return self._value

    def refresh(self):
        raise NotImplementedError("..")

class Subclass1(Base):
    def refresh(self):
        self._value = some_val

class Subclass2(Base):
    def refresh(self):
        self._value = some_other_val

I would love to be able to treat the entire related set as the same entity, and call the value property on each, with each deferring to its own implemented version of refresh, i.e.

for x in user.some_set.all():
    print x.value

but at this point it doesn't seem possible, even with removing refresh in the superclass. I've also thought of using the Strategy pattern and use a ForeignKey relationship to call the method, but I would still have to have a base class in the ForeignKey that the subclasses derive from.

Upvotes: 2

Views: 951

Answers (3)

C.B.
C.B.

Reputation: 8336

I ended up using the Strategy pattern with a GenericForeignKey

class Base(models.Model):
    _name = models.CharField(max_length=200)
    user = models.ForeignKey(User, related_name="some_set")
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    strategy = GenericForeignKey()

    #used as a property
    def value():
        def fget(self):
            return self.strategy.value

class Strategy1(models.Model):
    #some other definitions
    def value():
        def fget(self):
            return some_var

class Strategy2(models.Model):
    #some other definitions
    def value():
        def fget(self):
            return some_other_var

Which allowed me to do for x in user.some_set.all(): print x.value

Upvotes: 0

sax
sax

Reputation: 3806

use Proxy Models

from the doc

Sometimes, however, you only want to change the Python behavior of a model – perhaps to change the default manager, or add a new method.

This is what proxy model inheritance is for: creating a proxy for the original model. You can create, delete and update instances of the proxy model and all the data will be saved as if you were using the original (non-proxied) model. The difference is that you can change things like the default model ordering or the default manager in the proxy, without having to alter the original.

Upvotes: 4

justcompile
justcompile

Reputation: 3542

I might be missing the point, but have you tried Django Model Utils?

https://bitbucket.org/carljm/django-model-utils/src

If you look at the inheritance manager and make the relevant changes to your model, you should then be able to query as per:

entities = Base.objects.filter(user=my_user_obj).select_subclasses()
for entity in entities:
    print entity.value

Upvotes: 1

Related Questions