Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Resolving circular imports in celery and django

Tags:

I have a Django app that uses Celery to offload some tasks. Mainly, it defers the computation of some fields in a database table.

So, I have a tasks.py:

from models import MyModel from celery import shared_task  @shared_task def my_task(id):     qs = MyModel.objects.filter(some_field=id)     for record in qs:         my_value = #do some computations         record.my_field = my_value         record.save() 

And in models.py

 from django.db import models  from tasks import my_task   class MyModel(models.Model):       field1 = models.IntegerField()       #more fields       my_field = models.FloatField(null=True)        @staticmethod       def load_from_file(file):           #parse file, set fields from file           my_task.delay(id) 

Now obviously, this won't work because of a circular import (models imports tasks and tasks imports models).

I've resolved this for the moment by calling my_task.delay() from views.py, but it seems to make sense to keep the model logic within the model class. Is there a better way of doing this?

like image 598
Chinmay Kanchi Avatar asked Oct 15 '14 09:10

Chinmay Kanchi


People also ask

Are circular imports allowed in Python?

also just as a reference, it seems circular imports are allowed on python 3.5 (and probably beyond) but not 3.4 (and probably bellow).


1 Answers

The solution posted by joshua is very good, but when I first tried it, I found that my @receiver decorators had no effect. That was because the tasks module wasn't imported anywhere, which was expected as I used task auto-discovery.

There is, however, another way to decouple tasks.py from modules.py. Namely, tasks can be sent by name and they don't have to be evaluated (imported) in the process that sends them:

from django.db import models #from tasks import my_task import celery  class MyModel(models.Model):     field1 = models.IntegerField()     #more fields     my_field = models.FloatField(null=True)      @staticmethod     def load_from_file(file):         #parse file, set fields from file         #my_task.delay(id)         celery.current_app.send_task('myapp.tasks.my_task', (id,)) 

send_task() is a method on Celery app objects.

In this solution it is important to take care of correct, predictable names for your tasks.

like image 93
Piotr Ćwiek Avatar answered Oct 27 '22 13:10

Piotr Ćwiek