How to run "SELECT FOR UPDATE" instead of "SELECT" when adding data in Django Admin?

122 views Asked by At

In PersonAdmin(): below, I overrode response_add() with select_for_update() so that write skew doesn't occur then only 2 persons can be added on Add person and overrode save_model() so that obj.save() works only when changing a person on Change person:

# "store/admin.py"

from django.contrib import admin
from .models import Person

@admin.register(Person)
class PersonAdmin(admin.ModelAdmin):

    def response_add(self, request, obj, post_url_continue=None):
                                                 # Here
        obj_count = super().get_queryset(request).select_for_update().all().count()
        
        if obj_count < 2:
            obj.save()

        return super().response_add(request, obj, post_url_continue)

    def save_model(self, request, obj, form, change):
        last_part_of_path = request.path.split('/')[-2]

        if last_part_of_path == "change":
            obj.save() # Here

But, when adding a person on Add person as shown below:

enter image description here

SELECT is run instead of SELECT FOR UPDATE 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

So, how can I run SELECT FOR UPDATE instead of SELECT when adding a person on Add person?

1

There are 1 answers

0
Super Kai - Kazuya Ito On

select_for_update() doesn't work with count():

                                         # Here                    # Here
obj_count = super().get_queryset(request).select_for_update().all().count()

But, select_for_update() works with len() as shown below:

# "store/admin.py"

from django.contrib import admin
from .models import Person

@admin.register(Person)
class PersonAdmin(admin.ModelAdmin):

    def response_add(self, request, obj, post_url_continue=None):
                    # Here                           # Here
        obj_count = len(super().get_queryset(request).select_for_update().all())
        
        if obj_count < 2:
            obj.save()

        return super().response_add(request, obj, post_url_continue)

    def save_model(self, request, obj, form, change): # Here
        last_part_of_path = request.path.split('/')[-2]

        if last_part_of_path == "change":
            obj.save() # Here

So, when adding a person on Add person as shown below:

enter image description here

SELECT FOR UPDATE is run instead of SELECT as shown below:

enter image description here