user3667832
user3667832

Reputation: 377

Django - counting objects in database inside model declaraton

I have model with field named "number". It's not the same as id, it's position while displayed on website, users can sort teams around.

I need something for default property of number. I think I should just count the number of positions in the database and add one, so if there are 15 positions inside db, the new team would be last on list and have "number" 16.

But I don't know how to do it, inside models.py file.

Inside views.py I would just use

Iteam.objects.count()

But what can I use inside model declaration? How can the model check itself?

Edit:

I tried do it as Joey Wilhelm suggested:

   from django.db import models

class ItemManager(models.Manager):

    def next_number(self):
        return self.count() + 1


class Iteam(models.Model):
    name = models.CharField()

    objects = ItemManager()
    number = models.IntegerField(default=objects.next_number())

Unfortunetly i get error:

AttributeError: 'NoneType' object has no attribute '_meta'

Upvotes: 0

Views: 275

Answers (1)

Joey Wilhelm
Joey Wilhelm

Reputation: 5819

This is actually something you would want to do on a manager, rather than the model itself. The model should represent, and perform actions for, an individual instance. Whereas the manager represents, and performs actions for a collection of instances. So in this case you might want something like:

from django.db import models

class ItemManager(models.Manager):

    def next_number(self):
        return self.count() + 1

class Item(models.Model):
    number = models.IntegerField()

    objects = ItemManager()

That being said, I could see this leading to a lot of data integrity issues. Imagine the scenario:

  • 4 items are created, with numbers 1, 2, 3, 4
  • Item 2 gets deleted
  • 1 new item is created; this item now has a number of 4, giving you a duplicate

Edit:

The above approach will work only for pre-generating 'number', such as:

Iteam.objects.create(number=Iteam.objects.get_number(), name='foo')

In order to have it work as an actual default value for the field, you would need to use a less savory approach, such as:

from django.db import models

class Iteam(models.Model):
    name = models.CharField()
    number = models.IntegerField(default=lambda: Iteam.get_next_number())

    @classmethod
    def get_next_number(cls):
        return cls.objects.count() + 1

I would still warn against this approach, however, as it still could lead to the data integrity issues mentioned above.

Upvotes: 1

Related Questions