views:

193

answers:

3

Hi

I've recently been having some problems with my imports in Django (Python)... It's better to explain using a file diagram:

- project/
    - application/
        - file.py
    - application2/
        - file2.py

In project/application/file.py I have the following:

def test_method():
    return "Working"

The problem occurs in project/application2/file2.py, when I try to import the method from above:

from application.file import test_method

Usually works, but sometimes not.

from project.application.file import test_method

Does work, but it goes against Django's portability guidelines as the project folder must always be called the same.

I wouldn't mind, but it's the fact that this issue is occurring inconsistently, most of the time omitting project is fine, but occasionally not (and as far as I can see, with no reason).

I can pretty much guarantee I'm doing something stupid, but has anyone experienced this? Would I be better just putting the project in front of all relevant imports to keep things consistent? Honestly, it's unlikely the project folder name will ever change, I just want things to stick with guidelines where possible.

+3  A: 

For import to find a module, it needs to either be in sys.path. Usually, this includes "", so it searches the current directory. If you load "application" from project, it'll find it, since it's in the current directory.

Okay, that's the obvious stuff. A confusing bit is that Python remembers which modules are loaded. If you load application, then you load application2 which imports application, the module "application" is already loaded. It doesn't need to find it on disk; it just uses the one that's already loaded. On the other hand, if you didn't happen to load application yet, it'll search for it--and not find it, since it's not in the same directory as what's loading it ("."), or anywhere else in the path.

That can lead to the weird case where importing sometimes works and sometimes doesn't; it only works if it's already loaded.

If you want to be able to load these modules as just "application", then you need to arrange for project/ to be appended to sys.path.

(Relative imports sound related, but it seems like application and application2 are separate packages--relative imports are used for importing within the same package.)

Finally, be sure to consistently treat the whole thing as a package, or to consistently treat each application as their own package. Do not mix and match. If package/ is in the path (eg. sys.path includes package/..), then you can indeed do "from package.application import foo", but if you then also do "from application import foo", it's possible for Python to not realize these are the same thing--their names are different, and they're in different paths--and end up loading two distinct copies of it, which you definitely don't want.

Glenn Maynard
+1  A: 

It is better to always import using the same way - say, using project.app.models - because otherwise, you may find your module is imported twice, which sometimes leads to obscure errors as discussed in this question.

Vinay Sajip
+2  A: 

If you dig into the django philosophy, you will find, that a project is a collection of apps. Some of these apps could depend on other apps, which is just fine. However, what you always want is to make your apps plug-able so you can move them to a different project and use them there as well. To do this, you need to strip all things in your code that's related to your project, so when doing imports you would do.

from aplication.file import test_method

This would be the django way of doing this. Glenn answered why you are getting your errors so I wont go into that part. When you run the command to start a new project:

django-admin.py startproject myproject
This will create a folder with a bunch of files that django needs, manage.py settings,py ect, but it will do another thing for you. It will place the folder "myproject" on your python path. In short this means that what ever application you put in that folder, you would be able to import like shown above. You don't need to use django-admin.py to start a project as nothing magical happens, it's just a shortcut. So you can place you application folders anywhere really, you just need to have them on a python path, so you can import from them directly and make your code project independent so it easily can be used in any future project, abiding to the DRY principle that django is built upon.

googletorp