【问题标题】:Can not use celery delay for saved form: object is not JSON serializable不能对保存的表单使用 celery 延迟:对象不是 JSON 可序列化的
【发布时间】:2018-11-30 22:34:23
【问题描述】:

使用 Django 1.8,我想在视图中保存表单后触发延迟 celery 功能

def new_topic(request, forum_id):
    form = TopicForm()
    uid = request.user.id
    if request.method == 'POST':
        tform = TopicForm(request.POST)
        if tform.is_valid():
            topic = tform.save(commit=False)
            topic.title = clean_title(tform.cleaned_data['title'])
            topic.description = clean_desc(tform.cleaned_data['description'])
            topic.save()
            notify_new_topic.delay( uid, topic) #<--problem here
            #rest of the views

但我明白了

EncodeError at /add/topic/
<Topic: Topic object> is not JSON serializable

如果我从 celery 任务中删除 delay,我不会收到任何错误。

任务是:

@shared_task
def notify_new_topic(flwd_id, topic):
    title = topic.title
    link = topic.slug

    flwd= cached_user(flwd_id) #User.objects.get(id = flwd_id)
    print 'flwd is', flwd.username
    flwr_ids = FollowUser.objects.filter(followed=flwd).values('follower_id')
    flwrs = User.objects.filter(id__in= flwr_ids).values('id', 'username','email') 

    for f in flwrs:
        print 'flwr username:',  f['username']
        if notify_flwdp_applies(int(f['id'])):
            print 'notify flwdp applies'
            make_alerts_new_topic(flwd_id, f['id'], topic)
            print 'back from make_alerts_new_topic'

我想知道如何调试/修复这个问题?

【问题讨论】:

    标签: django django-celery


    【解决方案1】:

    既然已经提供了解决方案,我将尝试解释为什么我们不能将不可序列化的对象传递给celery 任务

    为什么我们需要将可序列化的对象传递给 celery 任务?

    对于 celery,我们使用 消息代理(如 RedisRabbitMQ)。假设我们使用 Redis。当调用 celery 任务 时,会将参数传递给 Redis,以便代理可以读取它们。为了实现这一点,这些参数的数据类型应该由 Redis 支持。

    解决方法

    假设您想将 python dictionary 作为参数传递给 celery 任务,请将这些值添加到 celery 配置中:

    task_serializer = "json"  
    result_serializer = "json"
    accept_content = ["json"]
    

    或者你可能想这样做

    celery.conf.update(
        task_serializer="json",
        result_serializer="json",
        accept_content=["json"]
    )
    

    对于其他情况,将上面的json替换为picklexml等。

    典型的基于文本的序列化格式是csvjsonxmlyamltoml等。基于二进制的格式是protobufavro。 Python 还有几个包,如picklenumpypandas,它们支持将自定义对象序列化为byte 格式。您还可以制作自定义序列化程序。

    这些配置有什么作用?

    1. 指示 celery 先序列化 python 对象,然后将它们传递给消息代理
    2. 消息代理反序列化对象,然后将它们提供给celery worker

    参考文献

    【讨论】:

      【解决方案2】:

      Task 的参数应该是可序列化的(即 string、int 等)。要修复错误,您可以传递 topic_id 作为参数并在任务方法中获取主题对象:

      notify_new_topic.delay( uid, topic.id)
      
      @shared_task
      def notify_new_topic(flwd_id, topic_id):
          topic = Topic.objects.get(pk=topic_id)
          title = topic.title
          link = topic.slug
      
          flwd= cached_user(flwd_id) #User.objects.get(id = flwd_id)
          print 'flwd is', flwd.username
          flwr_ids = FollowUser.objects.filter(followed=flwd).values('follower_id')
          flwrs = User.objects.filter(id__in= flwr_ids).values('id', 'username','email') 
      
          for f in flwrs:
              print 'flwr username:',  f['username']
              if notify_flwdp_applies(int(f['id'])):
                  print 'notify flwdp applies'
                  make_alerts_new_topic(flwd_id, f['id'], topic)
                  print 'back from make_alerts_new_topic'
      

      【讨论】:

      • 感谢您的提示。这消除了错误,但现在没有生成通知。难道是因为我在延迟函数内部调用了一个函数make_alerts_new_topic
      • @Babr 很难说没有访问您的代码。但我认为这更有可能是您的程序逻辑中的错误。例如检查flwrs 变量的值。如果它是空的,则永远不会发送通知。 if notify_flwdp_applies(int(f['id'])) 条件相同。看看是真是假。并且至少确保make_alerts_new_topic 在延迟函数之外按预期工作。
      • 问题是延迟函数中的打印语句没有在终端中打印出来。那么我该如何检查它们呢?但是,如果没有 delay,这些功能就可以正常工作。
      • @Babr 您可以启用日志记录。在此处查看详细信息stackoverflow.com/questions/13366312/…
      猜你喜欢
      • 2012-07-02
      • 1970-01-01
      • 2021-12-24
      • 2013-05-23
      • 2015-02-24
      • 2014-10-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多