1

When I use select_for_update() and update() of a queryset together as shown below:

# "store/views.py"

from django.db import transaction
from .models import Person
from django.http import HttpResponse

@transaction.atomic
def test(request):
                        # Here                           # Here
    print(Person.objects.select_for_update().filter(id=1).update(name="Tom"))
                                                  
    return HttpResponse("Test")

Only UPDATE query is run without SELECT FOR UPDATE query as shown below. *I use PostgreSQL and these logs below are the queries of PostgreSQL and you can check on PostgreSQL, how to log queries with transaction queries such as "BEGIN" and "COMMIT":

enter image description here

But, when I use select_for_update() and update() of a queryset separately then put print(qs) between them as shown below:

# "store/views.py"

from django.db import transaction
from .models import Person
from django.http import HttpResponse

@transaction.atomic
def test(request):

    qs = Person.objects.select_for_update().filter(id=1)
    print(qs) # Here
    qs.update(name="Tom")
                                                  
    return HttpResponse("Test")

SELECT FOR UPDATE and UPDATE queries are run as shown below:

enter image description here

Actually, this example above occurs because QuerySets are lazy according to the Django documentation below:

QuerySets are lazy – the act of creating a QuerySet doesn’t involve any database activity. You can stack filters together all day long, and Django won’t actually run the query until the QuerySet is evaluated.

But, this is not simple for me. I just want normal database behavior.

Now, is there non-lazy mode or strict mode for querysets in Django?

Super Kai - Kazuya Ito
  • 22,221
  • 10
  • 124
  • 129

1 Answers1

0

But, this is not simple for me. I just want normal database behavior.

Non-lazy querysets would not be easy either. Imagine a QuerySet that looks like:

Person.objects.all().filter(id=1)

If the queryset was eager it would fetch first all Persons, because of the Person.objects.all(), and then fetch the filtered ones, and perhaps update the records, even if fetching the items was not necessary at all. If the number of Persons is huge, it could even take that much memory that the server crashes.

You can force evaluation for example with bool(…), len(…), list(…), so:

from django.db import transaction
from django.http import HttpResponse
from .models import Person


@transaction.atomic
def test(request):
    qs = Person.objects.filter(id=1).select_for_update()
    bool(qs)  # noqa: evaluate the queryset
    qs.update(name='Tom')       
    return HttpResponse('Test')
Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555