5

I'm building a Django project with Python 3.6. I have created this directory structure ...

project
- manage.py
- scripts
  - run_commands.py
- commons
  - util
    - __init__.py
    - my_class.py

The contents of init.py are

from . import my_class

In another class, I attempt to import my MyClass like this

from commons.util import MyClass

but I'm getting this error

ModuleNotFoundError: No module named 'commons'

Am I creating my init.py properly?

satish
  • 703
  • 5
  • 23
  • 52
  • From which module are you attempting to import `MyClass`? Can you show the directory structure for that? – Lord Elrond Apr 20 '20 at 22:18
  • @ReinstateMonica, The directory structure has been updated. I'm trying to use the import in the "run_commands.py" file. – satish Apr 21 '20 at 14:11

2 Answers2

4

It looks like the problem is that MyClass is not located in commons.util, because you only imported the module named my_class, not the class itself.

Instead the file commons/util/__init__.py should contain the following import:

from .my_class import MyClass

I don't think this will solve your problem, because you would be getting a different error than the one shown, but you will get errors for this eventually.

Update

First, I'd recommend reading this answer for a good explanation for how imports work in python.

Basically, when you execute from commons.util import MyClass, the interpreter scans the contents of sys.path for a module named commons.
I assume you didn't set sys.path to include your project folder, hence the ModuleNotFoundError.

TLDR; you have 2 options:

  • Manually set sys.path in run_commands.py to check your project folder (Don't do this!)
  • Use Django's Command class

To use Django's Command class, you will need to adjust your project folder similar to the following:

project
- manage.py
- commons
- management
  - commands
    run_commands.py     
  - util
    - __init__.py
    - my_class.py

Now in run_commands.py:

from django.core.management.base import BaseCommand

from commons.util import MyClass

class Command(BaseCommand):
    def handle(*args, **options):
        print("handling", MyClass)

You can execute the new command with the following:

python3 manage.py run_commands
Lord Elrond
  • 13,430
  • 7
  • 40
  • 80
  • Thank you for your help. I first ran "source venv/bin/activate" to activate my virtual environment and then I ran "python scripts/run_commands.py myarg1 myarg2". I did try editing the __init__.py file to what you suggested, but as you predicted, the same error occurred. – satish Apr 22 '20 at 00:57
2

It used to be the case that yes, you need to put an __init__.py in every directory that is going to be treated as a python module as without an __init__.py python wouldn't look inside that directory for importable code.

- project
  - __init__.py
  - commons
    - __init__.py
    - util
      - __init__.py
      - my_class.py

But as Reinstate Monica points out below this is no longer true as of Python 3.3+. So, depending on your version of Python you will need to make an informed decision.

Note, you might or might not need an __init__.py in the root project directory (if you need them at all), it depends if it has definitions that are part of the source tree. But you won't need it if it's just a container, like you see in the way most Django projects are organised, for example.

jhrr
  • 1,624
  • 14
  • 22
  • Thank you. Is there a need to put any content in the __init__.py file that is in the commons directory? – satish Apr 17 '20 at 15:39
  • Nope, for just making modules importable they can all be empty. – jhrr Apr 17 '20 at 15:40
  • Thanks. Oddly, I'm still getting a "ModuleNotFoundError: No module named 'commons'" error with the empty file. I even tried making the "__init__.py" file in the util directory empty. – satish Apr 17 '20 at 15:46
  • With the empty `__init__.py` files you'll need to qualify the import in full like: `from commons.util.my_class import MyClass` but seeing as the error is firing from `commons` I suspect you have a misconfigured Django project regardless of the `__init__.py` situation. If you didn't use `startproject` you should start over with that. https://docs.djangoproject.com/en/3.0/intro/tutorial01/#creating-a-project – jhrr Apr 17 '20 at 15:48
  • Unfortunately I'm not in a position to rebuild my project. Do you think there are any adjustments I can make that would make this work or is starting over my only option? – satish Apr 17 '20 at 15:58
  • Well (and meant with total respect), if you haven't got imports working then you probably haven't gotten very far with your project! I would highly recommend starting again using `startproject` if you are this unfamiliar with the basics. Using it will assist you in learning Django best practices from the beginning. However, if you truly can't begin again, then you should open another, separate question posting your `settings.py` and all the other details of your project configuration in full so whatever is wrong elsewhere can be debugged in full. – jhrr Apr 17 '20 at 16:05
  • Thank you for your advice. I'm building a Python script within someone else's project so I think the fault is probably with me as opposed to the broader structure so I'll see what I can do. – satish Apr 17 '20 at 16:20
  • 1
    This simply is not true as of python 3.3+. [related](https://stackoverflow.com/questions/37139786/is-init-py-not-required-for-packages-in-python-3-3) – Lord Elrond Apr 20 '20 at 22:18
  • @ReinstateMonica thanks for that, i was unaware. i've updated the answer with credit to you and info to reflect the fact. – jhrr Apr 21 '20 at 13:48