3

I have a django app called customer. Inside customer.models I have some model classes one of them is Tooth. I also created a new python file inside my app's directory called callbacks.py to store some callback functions for some signlas. What I do is the following

from customer.models import Tooth

def callback(sender, **kwargs)
    #using Tooth here

and on models.py

from customer.callbacks import callback
#.....
post_save.connect(callback, sender=Customer)

But when I try to run sqlall I get an import error

    from customer.models import Tooth
ImportError: cannot import name Tooth

Everything else all other imports work normally.

EDIT: Using Django 1.6 version

Apostolos
  • 7,763
  • 17
  • 80
  • 150
  • 1
    Does `customer/models.py` import `callbacks.py`? You might have a circular import here. – Thomas Orozco Dec 16 '14 at 13:37
  • Yes...but how can I use my callback on models.py file? – Apostolos Dec 16 '14 at 13:51
  • There are various ways to do so, but the best way depends on the Django version you're using. Which version are you using? Do you mind upgrading if necessary? – Thomas Orozco Dec 16 '14 at 13:54
  • 1
    possible duplicate of [the right place to keep my signals.py files in django](http://stackoverflow.com/questions/7115097/the-right-place-to-keep-my-signals-py-files-in-django). You'll also need to **move your signal registration to the `callbacks.py` file** (i.e. out of your `models.py` file, and remove the circular import). – Thomas Orozco Dec 16 '14 at 13:56
  • @ThomasOrozco but in django docs says that registering is better to be done in models.py: "Where should this code live? You can put signal handling and registration code anywhere you like. However, you’ll need to make sure that the module it’s in gets imported early on so that the signal handling gets registered before any signals need to be sent. This makes your app’s models.py a good place to put registration of signal handlers." – Apostolos Dec 16 '14 at 14:02
  • Then read the question I linked, read the `django-developers` thread that is linked from there, read what Django core developers are saying there, and **then** make up your own opinion. The reason why it's recommended to use `models.py` there is because using `__init__.py` causes code coverage to be improperly calculated. The best solution in your case is **upgrade to Django 1.7 and put this import in your `ready()` code** (this is one of the reasons why this was added) — however, you didn't answer my question about whether you minded upgrading, so I don't know if this is feasible for you. – Thomas Orozco Dec 16 '14 at 14:06
  • Sorry just didn't see your question. No it is not an option right now upgrading to django 1.7. But thank you for the post, I used one of the solutions there. – Apostolos Dec 16 '14 at 14:14

1 Answers1

1

This is a circular import.

Here's what happens:

  • Django loads up the models
  • Therefore, django imports customer.models
  • Python execute the content of customer/models.py to compute the attributes of the module
  • customers/models.py imports customer/callbacks.py
  • Python set aside the execution of customer/models.py and starts executing customer/callbacks.py
  • callbacks.py try to import models.py which is being imported. Python prevents double importation of the module and raises an ImportError.

Usually these kind of situation show a poor design. But sometimes (which seems to be your case) tight coupling is required. One quick and dirty way to fix this is to delay the circular import, in your case:

In models.py:

from customer.callbacks import callback

# Define Tooth

post_save.connect(callback, sender=Customer)

In callbacks.py:

def callback(sender, **kwargs)
    from customer.models import Tooth
    # Use Tooth
Antoine Catton
  • 380
  • 3
  • 16