Celery should only execute tasks when its assumptions about state and message format are guaranteed.

Apply these rules:

1) Django: trigger tasks after the DB commit

Example (safe timing):

# views.py
from django.db import transaction

def create_user(request):
    user = User.objects.create(username=request.POST['username'])
    transaction.on_commit(lambda: send_email.delay(user.pk))
    return HttpResponse('User created')

Optional Celery shortcut (Django task class):

from celery import Celery

app = Celery('proj', task_cls='celery.contrib.django.task:Task')

2) Kafka (and other transports): configure required serializers precisely

Example:

# celeryconfig.py
task_serializer = 'json'      # required
# result_serializer = ...     # only set if you have a need