124👍
Use an F expression:
from django.db.models import F
either in update()
:
Counter.objects.get_or_create(name=name)
Counter.objects.filter(name=name).update(count=F("count") + 1)
or on the object instance:
counter, _ = Counter.objects.get_or_create(name=name)
counter.count = F("count") + 1
counter.save(update_fields=["count"])
Remember to specify update_fields
, or you might encounter race conditions on other fields of the model.
A note on the race condition avoided by using F expressions has been added to the official documentation.
19👍
If you don’t need to know the value of the counter when you set it, the top answer is definitely your best bet:
counter, _ = Counter.objects.get_or_create(name = name)
counter.count = F('count') + 1
counter.save()
This tells your database to add 1 to the value of count
, which it can do perfectly well without blocking other operations. The drawback is that you have no way of knowing what count
you just set. If two threads simultaneously hit this function, they would both see the same value, and would both tell the db to add 1. The db would end up adding 2 as expected, but you won’t know which one went first.
If you do care about the count right now, you can use the select_for_update
option referenced by Emil Stenstrom. Here’s what that looks like:
from models import Counter
from django.db import transaction
@transaction.atomic
def increment_counter(name):
counter = (Counter.objects
.select_for_update()
.get_or_create(name=name)[0]
counter.count += 1
counter.save()
This reads the current value and locks matching rows until the end of the transaction. Now only one worker can read at a time. See the docs for more on select_for_update.
- [Django]-Django – why is the request.POST object immutable?
- [Django]-What are the limitations of Django's ORM?
- [Django]-How can I restrict Django's GenericForeignKey to a list of models?
18👍
In Django 1.4 there is support for SELECT … FOR UPDATE clauses, using database locks to make sure no data is accesses concurrently by mistake.
- [Django]-Error trying to install Postgres for python (psycopg2)
- [Django]-Django: Redirect logged in users from login page
- [Django]-Serving Media files during deployment in django 1.8
8👍
Keeping it simple and building on @Oduvan’s answer:
counter, created = Counter.objects.get_or_create(name = name,
defaults={'count':1})
if not created:
counter.count = F('count') +1
counter.save()
The advantage here is that if the object was created in the first statement, you don’t have to do any further updates.
- [Django]-Django 1.5b1: executing django-admin.py causes "No module named settings" error
- [Django]-How to paginate Django with other get variables?
- [Django]-Django: Safely Remove Old Migrations?
7👍
Django 1.7
from django.db.models import F
counter, created = Counter.objects.get_or_create(name = name)
counter.count = F('count') +1
counter.save()
- [Django]-How can I set two primary key fields for my models in Django?
- [Django]-POST jQuery array to Django
- [Django]-Django Admin: Using a custom widget for only one model field
-2👍
Or if you just want a counter and not a persistent object you can use itertools counter which is implemented in C. The GIL will provide the safety needed.
–Sai
- [Django]-List_display – boolean icons for methods
- [Django]-What's the best Django search app?
- [Django]-How to query Case-insensitive data in Django ORM?