7

I've found many similar posts on the web about this topic but no one state clearly which is the problem.

Code

class Item(models.Model):
    @classmethod
    def get_next_item_number(cls):
        return cls.objects.count() + 1

    number = models.IntegerField(default=get_next_item_number)

Problem

When I access Django admin panel the text field related to 'number' field contains

<classmethod object at 0x7fab049d7c50>

So I tried to change the code above

class Item(models.Model):
    @classmethod
    def get_next_item_number(cls):
        return cls.objects.count() + 1

    number = models.IntegerField(default=get_next_item_number())

but when I run django server I get:

number = models.IntegerField(default=get_next_item_number())
TypeError: 'classmethod' object is not callable

I know all this could be prevented by declaring get_next_item_number() as an external function but this solution is not elegant to me because get_next_item_number() refer only to Item class.

Is there a solution I'm missing?

Sirion
  • 804
  • 1
  • 11
  • 33
  • have a look : http://stackoverflow.com/questions/3786987/class-based-default-value-for-field-in-django-model-inheritance-hierarchy – xecgr Sep 02 '14 at 12:07
  • Those solutions do not provide a default value when I build the ModelForm associated to Item model. I'd rather use an external function. – Sirion Sep 02 '14 at 12:16

2 Answers2

6

I found this solution:

Code

class Item(models.Model):
    @staticmethod
    def get_next_item_number():
        return Item.objects.count() + 1

    number = models.IntegerField(default=get_next_item_number.__func__)

I'm not completely aware of the possible consequences, but it works.

Sirion
  • 804
  • 1
  • 11
  • 33
  • staticmethod and classmethod are not the same thing in python. A staticmethod is not aware about the class at all, so you will not get the class as an argument. In the op's case it might do the job, though – Martin Taleski Mar 22 '23 at 07:25
0

Overwrite __init__ method of your Item class and assing the default value there, have a look to this code:

from django.db import models
class Item(models.Model):
    number = models.IntegerField()
    @classmethod
    def get_next_item_number(cls):
        return cls.objects.count() + 1
    def __init__(self, *args, **kwargs):
        super(Item, self).__init__(*args, **kwargs)
        if not self.pk and not self.number:
            self.number = Item.get_next_item_number()

And here's the flow output

>>> from your_app import models
>>> models.Item().save()
>>> models.Item().number
>>> 2
>>> models.Item().save()
>>> models.Item().number
>>> 3
>>> models.Item.objects.filter(id=1)[0].number #number only get its value from classmethod on new items
>>> 1
xecgr
  • 5,095
  • 3
  • 19
  • 28
  • Thank you, but it still doesn't show the default number value in ModelForm linked to Item model. E.g., in admin panel when I add a new Item the input text of the number field is empty. Isn't really there a way to pass a classmethod to 'default' keyword inside field definition? – Sirion Sep 02 '14 at 14:00