I have the following code in my index view.
latest_entry_list = Entry.objects.filter(is_published=True).order_by('-date_published')[:10] for entry in latest_entry_list: entry.views = entry.views + 1 entry.save()
If there are ten (the limit) rows returned from the initial query, will the save issue 10 seperate updated calls to the database, or is Django "smart" enough to issue just one update call?
Is there a more efficient method to achieve this result?
This is because a Django QuerySet is a lazy object. It contains all of the information it needs to populate itself from the database, but will not actually do so until the information is needed.
One of the most powerful features of Django is its Object-Relational Mapper (ORM), which enables you to interact with your database, like you would with SQL. In fact, Django's ORM is just a pythonical way to create SQL to query and manipulate your database and get results in a pythonic fashion.
You can either use Python's len() or use the count() method on any queryset depending on your requirements. Also note, using len() will evaluate the queryset so it's always feasible to use the provided count() method. You should also go through the QuerySet API Documentation for more information.
You can use F()
objects for this.
Here is how you import F
: from django.db.models import F
New in Django 1.1.
Calls to update can also use F() objects to update one field based on the value of another field in the model. This is especially useful for incrementing counters based upon their current value.
Entry.objects.filter(is_published=True).update(views=F('views')+1)
Although you can't do an update on a sliced query set... edit: actually you can...
This can be done completely in django ORM. You need two SQL queries:
Getting the non-sliced query set is the hard bit. I wondered about using in_bulk
but that returns a dictionary, not a query set. One would usually use Q objects
to do complex OR type queries and that will work, but pk__in
does the job much more simply.
latest_entry_ids = Entry.objects.filter(is_published=True)\ .order_by('-date_published') .values_list('id', flat=True)[:10] non_sliced_query_set = Entry.objects.filter(pk__in=latest_entry_ids) n = non_sliced_query_set.update(views=F('views')+1) print n or 0, 'items updated'
Due to the way that django executes queries lazily, this results in just 2 database hits, no matter how many items are updated.
You could handle the updates in a single transaction, which could improve performance significantly. Use a separate function, decorated with @transaction.commit_manually.
@transaction.commit_manually def update_latest_entries(latest_entry_list): for entry in latest_entry_list: entry.views += 1 entry.save() transaction.commit()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With