It hasn't failed me yet:

BATCH_SIZE = 100

def batch_proess(queryset) -> None:
    count = queryset.count()
    if count == 0:
        time.sleep(10)
        return
    with transaction.atomic():
        batch = list(
            queryset.select_for_update(of=("self",), skip_locked=True).values_list(
                "id", flat=True
            )[:BATCH_SIZE]
        )
        # Do whatever you need to do with the batch here.
    return

Lightning bolt
About the author

I'm Justin Duke — a software engineer, writer, and founder. I currently work as the CEO of Buttondown, the best way to start and grow your newsletter, and as a partner at Third South Capital.

Lightning bolt
Greatest hits

Lightning bolt
Elsewhere

Lightning bolt
Don't miss the next essay

Get a monthly roundup of everything I've written: no ads, no nonsense.