简体   繁体   中英

Django queryset caching with exists() and values_list()

Let's say, I'm trying to change the name of all profiles whose name is John to Mike

queryset = Profile.objects.filter(name='John')

Method 1:

if queryset:
    queryset.update(name='Mike')
    logger.info(f'Updated the following profiles: {queryset.values_list('id', flat=True)}')

Method 2:

if queryset.exists():
    queryset.update(name='Mike')
    logger.info(f'Updated the following profiles: {queryset.values_list('id', flat=True)}')

QUESTION: What's the most efficient way to update all the fields with the name John , and log the records' ids that were updated?

In Method 1 , calling if queryset evaluates the entire queryset .

In Method 2 , using queryset.exists() makes a query, as does the queryset.update(...) call.

The most efficient method would be to skip the conditional if statement entirely, and just call queryset.update(...) . The return value of the call will include the amount of rows that were updated, which may be 0 .

To test the differences, we can use django.db.connection which will list all of the executed queries, and how long they took.

from django.db import connection, reset_queries

from django.contrib.models import User


def method_1(queryset):
    if queryset:
        queryset.update(first_name="Mike")

def method_2(queryset):
    if queryset.exists():
        queryset.update(first_name="Mike")

def method_3(queryset):
    queryset.update(first_name="Mike")


>>> connection.queries == []  # empty to start
True

>>> method_1(User.objects.all())
>>> [query["time"] for query in connection.queries]
['0.051', '0.126']

>>> reset_queries()

>>> method_2(User.objects.all())
>>> [query["time"] for query in connection.queries]
['0.001', '0.125']

>>> reset_queries()

>>> method_3(User.objects.all())
>>> [query["time"] for query in connection.queries]
['0.122']

As you can see, method_3 only uses a single query to perform the update, and is faster than the other methods.

If you must get the ID of every updated Profile you should query this first as after you update the rows you will not be able to query this again. You can use the result of this to determine if you should run the update.

profile_ids_to_update = list(queryset.values_list('id', flat=True))
if profile_ids_to_update:
    queryset.update(name='Mike')
    logger.info(f'Updated the following profiles: {profile_ids_to_update}')

This will result in at most 2 queries, if there are no rows to update there will only be 1

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM