Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django - Executing a task through celery from a model

In my 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)

In my 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

This is returning the following:

  • Exception Type: ImportError
  • Exception Value: cannot import name tasks

The error disappears if I comment out from core.models import Image in tasks.py, however this obviously will cause a problem since Image has no meaning in here. I have tried to import it inside create_thumbnail however it still won't recognize Image.

I have read somewhere that usually the object itself can be passed as an argument to a task and that would solve my problem. However, a friend once told me that it is considered best practice to send as little data as possible in a RabbitMQ message, so to achieve that I'm trying to only pass the image ID and then retrieve it again in the task.

1) Is what I'm trying to do considered a best practice? If yes, how do I work it out?

2) I have noticed in all the examples I found around the web, they execute the task from a view and never from a model. I'm trying to create a thumbnail whenever a new image is uploaded, I don't want to call create_thumbnail in every form/view I have. Any idea about that? Is executing a task from a model not recommended or a common practice?

like image 526
Aziz Alfoudari Avatar asked Nov 12 '11 19:11

Aziz Alfoudari


3 Answers

1) Is what I'm trying to do considered a best practice? If yes, how do I work it out?

Yes, passing only a little information to the task is generally a good thing like you mentioned.

2) I have noticed in all the examples I found around the web, they execute the task from a view and never from a model. I'm trying to create a thumbnail whenever a new image is uploaded, I don't want to call create_thumbnail in every form/view I have. Any idea about that? Is executing a task from a model not recommended or a common practice?

I've noticed the same thing, and feel that tutorials and documentation call tasks from their views because it is easier to demonstrate how things work using simple views than with models or forms.

To eliminate circular imports, you should think about which way the imports should happen. Generally, tasks.py will need to import many things from models.py whereas models.py rarely needs to know anything about tasks.py. The standard should be that models.py does not import from tasks.py. Thus, if you do need to do this and are calling a task from a model method, make the import in the method as so:

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)
like image 143
Spike Avatar answered Nov 15 '22 08:11

Spike


i wonder if the problem might be a circular import (models and tasks importing each other at the top level). try moving "from core.models import Image" into create_thumbnail, i.e. changing tasks to

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
like image 35
second Avatar answered Nov 15 '22 10:11

second


You don't need to import the task itself. Try using the following

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)
like image 4
Mo J. Mughrabi Avatar answered Nov 15 '22 10:11

Mo J. Mughrabi