python-3.xdjangocelery

Django model object as parameter for celery task raises EncodeError - 'object of type someModelName is not JSON serializable'


Im working with a django project(im pretty new to django) and running into an issue passing a model object between my view and a celery task.

I am taking input from a form which contains several ModelChoiceField fields and using the selected object in a celery task. When I queue the task(from the post method in the view) using someTask.delay(x, y, z) where x, y and z are various objects from the form ModelChoiceFields I get the error object of type <someModelName> is not JSON serializable.

That said, if I create a simple test function and pass any of the same objects from the form into the function I get the expected behavior and the name of the object selected in the form is logged.

def test(object):
    logger.debug(object.name)

I have done some poking based on the above error and found django serializers which allows for a workaround by serializing the object using serializers.serialize('json', [template]), in the view before passing it to the celery task.

I can then access the object in the celery task by using template = json.loads(template)[0].get('fields') to access its required bits as a dictionary -- while this works, it does seem a bit inelegant and I wanted to see if there is something I am missing here.

Im obviously open to any feedback/guidance here however my main questions are:

Any suggestions would be greatly appreciated.

Traceback: I tried to post the full traceback here as well however including that caused the post to get flagged as 'this looks like spam'

Internal Server Error: /build/
Traceback (most recent call last):
  File "/home/tech/sandbox_project/venv/lib/python3.8/site-packages/kombu/serialization.py", line 49, in _reraise_errors
    yield
  File "/home/tech/sandbox_project/venv/lib/python3.8/site-packages/kombu/serialization.py", line 220, in dumps
    payload = encoder(data)
  File "/home/tech/sandbox_project/venv/lib/python3.8/site-packages/kombu/utils/json.py", line 65, in dumps
    return _dumps(s, cls=cls or _default_encoder,
  File "/usr/lib/python3.8/json/__init__.py", line 234, in dumps
    return cls(
  File "/usr/lib/python3.8/json/encoder.py", line 199, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/usr/lib/python3.8/json/encoder.py", line 257, in iterencode
    return _iterencode(o, 0)
  File "/home/tech/sandbox_project/venv/lib/python3.8/site-packages/kombu/utils/json.py", line 55, in default
    return super().default(o)
  File "/usr/lib/python3.8/json/encoder.py", line 179, in default
    raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type Template is not JSON serializable

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/tech/sandbox_project/venv/lib/python3.8/site-packages/django/core/handlers/exception.py", line 47, in inner
    response = get_response(request)

Solution

  • Add this lines to settings.py

    # Project/settings.py
    CELERY_ACCEPT_CONTENT = ['json']
    CELERY_TASK_SERIALIZER = 'json'
    CELERY_RESULT_SERIALIZER = 'json'
    

    Then instead of passing object, send JSON with id/pk if you're using a model instance call the task like this..

    test.delay({'pk': 1})
    

    Django model instance is not available in celery environment, as it runs in a different process

    How you can get the model instance inside task then? Well, you can do something like below -

    def import_django_instance():
        """
        Makes django environment available 
        to tasks!!
        """
        import django
        import os
        os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Project.settings')
        django.setup()
    
    
    # task
    @shared_task(name="simple_task")
    def simple_task(data):
        import_django_instance()
        from app.models import AppModel
    
        pk = data.get('pk')
        instance = AppModel.objects.get(pk=pk)
        # your operation