8

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?

2 Answers 2

13

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

Sign up to request clarification or add additional context in comments.

2 Comments

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?
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: stackoverflow.com/questions/100003/… - and once you've mastered metaclasses, look at the django source code.
1

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.