Можно ли передавать OrderedDict в качестве аргумента задачи Celery?

У меня есть внутри сериализатора Django REST Framework переопределенный метод update.
В этом update, поскольку пользователь может отправлять много дочерних элементов, у меня есть асинхронная задача Celery process_children, заниматься с детьми.

class MyModelSerializer(serializers.ModelSerializer):
    ....

    @transaction.atomic
    def update(self, mymodel, validated_data):
        try:
            children_data = validated_data.pop('children')
            transaction.on_commit(lambda: process_children.apply_async(
                countdown=1,
                args=[mymodel.id, children_data]))
        except KeyError:
            pass
        ...

В args есть один аргумент, который является не объектом json, а OrderedDict: children_data.

Задача выглядит так:

@app.task
def process_children(mymodel_id, children_data):
    mymodel = MyModel.objects.get(pk=mymodel_id)
    children = mymodel.children.all()
    for child_data in children_data:
        try:
            child = children.get(start=child_data['start'])
            child = populate_child(child, child_data)
            child.save()
        except Child.DoesNotExist:
            create_child(mymodel, child_data)

Я читал, что мы должны отправлять только json (или рассола, yaml, что угодно...) args.

  • Но эта установка, кажется, работает
  • Я даже могу отправить объект datetime (то есть атрибут start, который я использую в задаче, чтобы сопоставить сохраненный дочерний элемент с новыми значениями, отправленными через API).

Так что же здесь происходит?

  • Все ли в порядке, сельдерей сериализует и десериализует OrderedDict как босс.
  • Или я сумасшедший и должен сериализовать перед вызовом задачи и десериализовать внутри задачи?

[ОБНОВЛЕНИЕ, добавление настроек CELERY]

CELERY_BROKER_URL = get_env_variable('REDIS_URL')
CELERY_BROKER_POOL_LIMIT = 0
CELERY_REDIS_MAX_CONNECTIONS = 10
CELERY_ACCEPT_CONTENT = ['application/json']
CELERY_TASK_SERIALIZER = 'json'
CELERY_TIMEZONE = 'Europe/London'

person Sifnos    schedule 06.11.2018    source источник
comment
Вы пытались dict(children_data) преобразовать его в обычный дикт перед отправкой на задание?   -  person Nour Wolf    schedule 06.11.2018
comment
Было бы лучше использовать dict, чем OrderedDict? В чем смысл, пока вроде все работает в тесте и продакшене? Мой вопрос больше в том, правильно ли это или я должен перейти к сериализации и десериализации себя в json.   -  person Sifnos    schedule 07.11.2018


Ответы (2)


Вы используете сериализатор pickle, который будет относительно хорошо обрабатывать объекты, но с ним есть проблемы. Вот запись в блоге о концепции сериализации и celery.

person schillingt    schedule 07.11.2018
comment
Извините, я забыл показать свои настройки CELERY, я добавлю это, но нет, я использую json и никогда не получал ошибку времени выполнения, как описано в этой статье в блоге. - person Sifnos; 07.11.2018
comment
Странный. Упорядоченные словари могут быть сериализованы нормально, но дата и время должны сломаться. - person schillingt; 07.11.2018
comment
Да, и у меня также есть десятичные поля:/ - person Sifnos; 07.11.2018
comment
В документе Kombu прямо сказано, что Date и Decimal не принимаются. Я дважды проверю, что рассол не используется. - person Sifnos; 07.11.2018

Да, вы делаете это правильно.

Как указано в документе.

Данные, передаваемые между клиентами и работниками, должны быть сериализованы, поэтому каждое сообщение в Celery имеет заголовок content_type, описывающий метод сериализации, используемый для его кодирования.

Кроме того, в celery 4.0 сериализатором по умолчанию является JSON (ранее он был pickle). Поэтому всякий раз, когда вы вызываете эту задачу, сельдерей по умолчанию сериализует и десериализует ее. Если вы хотите использовать любой другой сериализатор, то при вызове задачи нужно указать content-type (если вы используете .delay, то по умолчанию сериализатор будет json.

process_children.apply_async((model_id, children_data), serializer='pickle')
person bak2trak    schedule 08.11.2018
comment
Правильно, пока в дикторе не используются ключи int/etc. Сериализация в json принуждает их к строкам, потому что json имеет только строки в качестве ключей, что может быть довольно шокирующим в первый раз, когда это происходит. - person zachaysan; 16.07.2021