7

My Discount model describes common fields for all types of discounts in the system. I have some proxy models which describe concrete algorithm for culculating total. Base Discount class has a member field named type, which is a string identifing its type and its related class.

class Discount(models.Model):
  TYPE_CHOICES = (
    ('V', 'Value'),
    ('P', 'Percentage'),
  )

  name = models.CharField(max_length=32)
  code = models.CharField(max_length=32)
  quantity = models.PositiveIntegerField()
  value = models.DecimalField(max_digits=4, decimal_places=2)
  type = models.CharField(max_length=1, choices=TYPE_CHOICES)

  def __unicode__(self):
    return self.name

  def __init__(self, *args, **kwargs):
    if self.type:
      self.__class__ = getattr(sys.modules[__name__], self.type + 'Discount')
    super(Discount, self).__init__(*args, **kwargs)

class ValueDiscount(Discount):
  class Meta:
    proxy = True

  def total(self, total):
    return total - self.value

But I keep getting exception of AttributeError saying self doesnt have type. How to fix this or is there another way to achieve this?

aambrozkiewicz
  • 542
  • 3
  • 14

2 Answers2

12

Your init method needs to look like this instead:

def __init__(self, *args, **kwargs):
    super(Discount, self).__init__(*args, **kwargs)
    if self.type:
        self.__class__ = getattr(sys.modules[__name__], self.type + 'Discount')

You need to call super's __init__ before you will be able to access self.type.

Becareful with calling your field type since type is also a python built-in function, though you might not run into any problems.

See: http://docs.python.org/library/functions.html#type

Derek Kwok
  • 12,768
  • 6
  • 38
  • 58
  • Thank you. One more question, why do I need to call super __init__ before accessing objects properties? I thought that it's a python which makes all members declared in object accessable in constructor, not super class (models.Model from Django). How come? – aambrozkiewicz Sep 23 '11 at 09:16
  • 4
    This is because django.db.models.Model has `__metaclass__ = ModelBase`. This means that django uses `ModelBase` to create your Model class instead of the regular `type`. I highly suggest reading: http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python - and once you've mastered metaclasses, look at the django source code. – Derek Kwok Sep 23 '11 at 09:35
0

call super(Discount, self).__init__(*args, **kwargs) before referencing self.type.

erikvw
  • 416
  • 3
  • 11