Search code examples
pythondjangodjango-modelsmodels

Incrementing IntegerField counter in a database


As beginner at Django, i tried to make a simple application that would give Http response of how many times content was viewed. I have created a new Counter model, and inside, added IntegerField model count.

class Counter(models.Model):
    count = models.IntegerField(default=0)
    def __int__(self):
        return count

In views, i made a variable counter out of Counter() class, and tried adding +1 to counter.count integer, but when i tried to save, it would give me an error that integer couldn't be saved.

so i tried saving class instead:

def IndexView(response):
    counter = Counter()
    counter.count = counter.count + 1
    counter.save()
    return HttpResponse(counter.count)

This method, would keep showing 1 and could not change after reload.


How would i change IntegerField model properly, so it could be updated after every view, and would be saved even if server was reloaded?


Solution

  • The problem

    Yes but you are creating a new Counter object on each request, which starts again at 0, that's your problem

    def IndexView(response):
        counter = Counter() # This creates a new counter each time
        counter.count = counter.count + 1
        counter.save()
        return HttpResponse(counter.count)
    

    What you were doing above would result in a bunch of Counter objects with count = 1 in the database.

    The Solution

    My example below shows you how to get an existing Counter object, and increment it, or create it if it doesn't already exist, with get_or_create()

    First we need to associate a Counter to e.g. a page (or anything, but we need someway to identify it and grab it from the DB)

    class Counter(models.Model):
        count = models.IntegerField(default=0)
        page = models.IntegerField() # or any other way to identify
                                     # what this counter belongs to
    

    then:

    def IndexView(response):
        # Get an existing page counter, or create one if not found (first page hit)
        # Example below is for page 1
    
        counter, created = Counter.objects.get_or_create(page=1) 
    
        counter.count = counter.count + 1
        counter.save()
        return HttpResponse(counter.count)
    

    Avoid race conditions that can happen with count = count + 1

    And to avoid race conditions use an F expression

    # When you have many requests coming in,
    # this may have outdated value of counter.count:
    # counter.count = counter.count + 1
    
    # Using an F expression makes the +1 happen on the database
    from django.db.models import F
    counter.count = F('count') + 1