2

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.

C.B.
  • 8,096
  • 5
  • 20
  • 34

3 Answers3

4

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.

Community
  • 1
  • 1
sax
  • 3,708
  • 19
  • 22
  • It's on the right path, but still doesn't allow me to call the new defined method `refresh` when getting the related set. – C.B. Dec 17 '14 at 15:13
1

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
justcompile
  • 3,362
  • 1
  • 29
  • 37
  • This is close, but I can't get it to resolve to the deepest level of subclass. When I place the InteritanceManager on a subclass( I actually have 2 levels deep) it doesn't resolve to the subclasses – C.B. Dec 17 '14 at 16:19
  • How deep is your model inheritance? – justcompile Dec 17 '14 at 16:51
  • One extra level than above, sorry should have included that. – C.B. Dec 17 '14 at 16:57
  • The docs state that if using >=django 1.6 then it should cater for grandchild inheritance. Judging by the tags of your question this is the case? – justcompile Dec 17 '14 at 17:43
  • yes - I noticed that myself. However if I stick the `objects = InheritanceManager()` on the root class, it only goes one level deep when I call `select_subclasses`, and if I stick it on the next class down, it resolves to itself. – C.B. Dec 17 '14 at 17:52
  • Hmm, frustrating. One last ditched attempt, what if you stick the inheritancemanager on both the root AND next one down? Or is that what you tried? – justcompile Dec 17 '14 at 17:56
0

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

C.B.
  • 8,096
  • 5
  • 20
  • 34