views:

34

answers:

1

How can one accomplish class-based default value in following scheme? I mean, I would like to inherited classes set default value for "number" differently:

class OrderDocumentBase(PdfPrintable):
    number = models.PositiveIntegerField(default=self.create_number())

    @classmethod
    def create_number(cls):
        raise NotImplementedError

class Invoice(OrderDocumentBase):
    @classmethod
    def create_number(cls):
        return 1

class CreditAdvice(OrderDocumentBase):
    @classmethod
    def create_number(cls):
        return 2

I have looked at this stackoverflow question, but it doesn't address the same problem. The only thing I thought would work was overloading OrderDocumentBase's __init__ method like this:

def __init__(self, *args, **kwargs):
    """
    Overload __init__ to enable dynamic set of default to number
    """
    super(OrderDocumentBase, self).__init__(*args, **kwargs)
    number_field = filter(lambda x: x.name == 'number', self._meta.fields)[0]
    number = self.__class__.create_number()
    number_field.default = number

This works, but only partially and behaves quite wierdly. In admin interface, I can see the default being set only after second or latter page refresh. On first try, None is being set :(

Second possibility is redefinition of number field in each class, but that doesn't seem too much pretty. Is there any other way?

Can someone help?

A: 

There are a couple of problems here. First, the self.method is not going to work. There is no self in the context of the body of the class, which is where you are declaring the PositiveIntegerField.

Second, passing a callable will not work as the callable gets bound at compile time and does not change at runtime. So if you define say,

class OrderDocumentBase(PdfPrintable):
    create_number = lambda: return 0
    number = models.PositiveIntegerField(default=create_number)

class Invoice(OrderDocumentBase):
    create_number = lambda: return 1

All Invoice instances will still get 0 as default value.

One way I can think of to tackle this is to override the save() method. You can check if the number has not been supplied and set it to a default before saving.

class OrderDocumentBase(PdfPrintable):
    number = models.PositiveIntegerField()

    def save(self, *args, **kwargs):
        if not self.number:
            self.number = self.DEFAULT
        super(OrderDocumentBase, self).save(*args, **kwargs)

class Invoice(OrderDocumentBase):
    DEFAULT = 2

class CreditAdvice(OrderDocumentBase):
    DEFAULT = 3

I tested the above with a small change (made OrderDocumentBase abstract as I did not have PdfPrintable) and it worked as expected.

Manoj Govindan
Well, I know that the snipped I used does not work, I just wanted to show the way I wish it would work :)The problem with your solution is that the default value will not be visible in Django admin when creating new object, which is necessary functionality I need. Seems like the only solution is redefinition in each child model.
xaralis