【问题标题】:Django - Executing a task through celery from a modelDjango - 通过模型中的芹菜执行任务
【发布时间】:2011-12-27 18:29:01
【问题描述】:

在我的models.py中:

from django.db import models
from core import tasks

class Image(models.Model):
    image     = models.ImageField(upload_to='images/orig')
    thumbnail = models.ImageField(upload_to='images/thumbnails', editable=False)

    def save(self, *args, **kwargs):
       super(Image, self).save(*args, **kwargs)
       tasks.create_thumbnail.delay(self.id)

在我的 tasks.py 中:

from celery.decorators import task
from core.models import Image

@task()
def create_thumbnail(image_id):
    ImageObj = Image.objects.get(id=image_id)
    # other stuff here

这将返回以下内容:

  • 异常类型: ImportError
  • 异常值:无法导入名称任务

如果我在tasks.py 中注释掉from core.models import Image,错误就会消失,但这显然会导致问题,因为Image 在这里没有任何意义。我尝试将它导入到 create_thumbnail 中,但它仍然无法识别 Image

我在某处读到,通常对象本身可以作为参数传递给任务,这样可以解决我的问题。但是,一位朋友曾经告诉我,在 RabbitMQ 消息中发送尽可能少的数据被认为是最佳实践,因此我试图仅传递图像 ID,然后在任务中再次检索它。

1) 我正在尝试做的事情是否被视为最佳做法?如果是,我该如何解决?

2) 我注意到在网上找到的所有示例中,它们从视图执行任务,而不是从模型执行。每当上传新图像时,我都会尝试创建缩略图,我不想在我拥有的每个表单/视图中调用 create_thumbnail。有什么想法吗?从模型中执行任务是不推荐的还是常见的做法?

【问题讨论】:

    标签: django rabbitmq celery


    【解决方案1】:

    我想知道问题是否可能是循环导入(modelstasks 在顶层相互导入)。尝试将“from core.models import Image”移动到create_thumbnail,即将tasks改为

    from celery.decorators import task
    
    @task()
    def create_thumbnail(image_id):
        from core.models import Image
        ImageObj = Image.objects.get(id=image_id)
        # other stuff here
    

    【讨论】:

    • 我已经尝试过了,在这种情况下消息发送正确但是 create_thumbnail 会产生错误:File "/home/ubuntu/project/core/tasks.py", line 5, in create_thumbnailfrom core.models import ImageNameError: global name 'Image' is not defined
    • 我认为core 是您的应用程序的名称。您是否尝试过在项目名称前添加(即from project.app.models import Model)? django 做了一些路径魔法,有时没有什么帮助
    • 刚刚做了,同样的错误返回:NameError: global name 'Image' is not defined。我已经在django.wsgi 中附加了我的项目路径。
    • 我对芹菜的唯一设置是settings.py中的以下内容:# Celery Settingsimport djcelerydjcelery.setup_loader()djcelery.setup_loader()BROKER_HOST = "localhost"BROKER_PORT = 5672BROKER_USER = "guest"BROKER_PASSWORD = "guest"@987654342>2更新:django.wsgi中还有以下内容:os.environ['CELERY_LOADER'] = 'django'
    • 好的,我刚刚检查了管理界面,显然我有 0 个工人!不过不确定这是否相关,因为我已经在终端中运行 celeryd 并且可以看到消息通过。
    【解决方案2】:

    1) 我正在尝试做的事情是否被视为最佳做法?如果是,我该如何解决?

    是的,像您提到的那样,只向任务传递少量信息通常是一件好事。

    2) 我注意到在网上找到的所有示例中,它们从视图执行任务,而不是从模型执行。每当上传新图像时,我都会尝试创建缩略图,我不想在我拥有的每个表单/视图中调用 create_thumbnail。有什么想法吗?从模型中执行任务是不推荐还是常见做法?

    我注意到了同样的事情,并且觉得教程和文档从他们的视图中调用任务,因为使用简单的视图比使用模型或表单更容易演示事物的工作原理。

    要消除循环导入,您应该考虑导入应该以哪种方式进行。一般来说,tasks.py 需要从models.py 中导入很多东西,而models.py 很少需要知道tasks.py 的任何信息。标准应该是models.py不从tasks.py导入。因此,如果您确实需要这样做并且正在从模型方法调用任务,请在方法中进行导入:

    from django.db import models
    
    class Image(models.Model):
        image     = models.ImageField(upload_to='images/orig')
        thumbnail = models.ImageField(upload_to='images/thumbnails', editable=False)
    
        def save(self, *args, **kwargs):
           super(Image, self).save(*args, **kwargs)
           from core.tasks import create_thumbnail
           create_thumbnail.delay(self.id)
    

    【讨论】:

    • 我真的很欣赏“为了消除循环导入,您应该考虑导入应该以哪种方式进行......”这一段。谢谢。
    【解决方案3】:

    您不需要导入任务本身。尝试使用以下

    from django.db import models
    from celery.execute import send_task, delay_task
    
    class Image(models.Model):
        image     = models.ImageField(upload_to='images/orig')
        thumbnail = models.ImageField(upload_to='images/thumbnails', editable=False)
    
        def save(self, *args, **kwargs):
           super(Image, self).save(*args, **kwargs)
           result = delay_task("task_prefix.create_thumbnail", post.id)
    

    【讨论】:

    • celery.execute 在 celery 3.x 中似乎不再存在
    猜你喜欢
    • 2022-01-21
    • 1970-01-01
    • 2021-12-16
    • 2018-03-07
    • 1970-01-01
    • 2017-04-11
    • 1970-01-01
    • 1970-01-01
    • 2020-01-21
    相关资源
    最近更新 更多