1

I want to start a python script in a different process when a certain request arrives at my django server. I can successfully start it, but its imports fail.

In my django app, I have a views.py. In this I have a ViewSet with an update() method that looks like this (reduced snippet to relevant code):

from . import models # notice this import, which works

def update(self, request, pk=None):
    dirname = os.path.dirname(__file__)
    filename = os.path.join(dirname, 'Process.py')
    p = subprocess.Popen(["python", filename],
                         stdout=sys.stdout, 
                         stdin=subprocess.PIPE)

This works as expected: When performing the appropriate request that causes update() to be called, it executes Process.py, which is a file in the same directory as views.py.

In Process.py, I want to access the models of the django backend, so I do as I did before:

from . import models

# more code here

But I get the error:

ImportError: cannot import name 'models'

Why does this import fail in Process.py even though it works for views.py?

I tried

  • a suggestion on the internet that the environment should be setup to be the same for the child process, like so:

    p = subprocess.Popen(["python", filename],
                         env = {'PYTHONPATH': os.pathsep.join(sys.path)}, # this is new
                         stdout=sys.stdout, 
                         stdin=subprocess.PIPE)
    

    But that failed to start python at all:

    Fatal Python error: failed to get random numbers to initialize Python
    
  • to write the import statement in Process.py in a different way:

    from models import stuff
    

    Oddly enough, this does import models.py (yay!) but fails deeper down the rabbit hole with:

    ModuleNotFoundError: No module named '<django project name>'
    

    Said rabbit hole starts at the first model class in models.py and then dives into django code, ending up at some bootstrapper:

    Traceback (most recent call last):
    File "<path to django app>\Process.py", line 1, in <module>
      from models import stuff
    File "<path to django app>\models.py", line 3, in <module>
      class <some model class>(models.Model):
    File "<path to conda environment>\lib\site-packages\django\db\models\base.py", line 108, in __new__
      app_config = apps.get_containing_app_config(module)
    File "<path to conda environment>\lib\site-packages\django\apps\registry.py", line 253, in get_containing_app_config
      self.check_apps_ready()
    File "<path to conda environment>\lib\site-packages\django\apps\registry.py", line 135, in check_apps_ready
      settings.INSTALLED_APPS
    File "<path to conda environment>\lib\site-packages\django\conf\__init__.py", line 82, in __getattr__
      self._setup(name)
    File "<path to conda environment>\lib\site-packages\django\conf\__init__.py", line 69, in _setup
      self._wrapped = Settings(settings_module)
    File "<path to conda environment>\lib\site-packages\django\conf\__init__.py", line 170, in __init__
      mod = importlib.import_module(self.SETTINGS_MODULE)
    File "<path to conda environment>\lib\importlib\__init__.py", line 126, in import_module
      return _bootstrap._gcd_import(name[level:], package, level)
    

    It looks like django interferes with my imports somehow, but I'm not sure how and how to correct them.

How do I successfully import (and use) models.py of a app from a python script that's started with subprocess.Popen(...) from views.py of ?


from the comments

Is Process.py a regular Python script, or is it written as a Django Management Command?

It's a regular script. I modified it for debugging purposes like so:

import sys
for p in sys.path:
    print(p)

from . import models

What happens if you run the script manually from the shell? Same error?

from shell django views.py via Popen()
windows cmd: django-project>python manage.py shell then In [1]: from django-app import Process see above call detail
success ImportError: cannot import name 'models' result
includes django-project path and empty path includes django-project/django-app differences of sys.path
user11398730
  • 49
  • 1
  • 11
  • Is `Process.py` a regular Python script, or is it written as a [Django Management Command](https://docs.djangoproject.com/en/3.1/howto/custom-management-commands/)? What happens if you run the script manually from the shell? Same error? – C14L Mar 08 '21 at 16:28
  • @C14L just a regular python script (the `import` statement alone reproduces the error). Thank you for the suggestion. I am in the process of doing more research about django's commands, [based on this anser](https://stackoverflow.com/a/16853755/11398730). I managed to create a custom command and execute it via `manage.py` with working imports, however: how would I go about executing that as a separate process? – user11398730 Mar 08 '21 at 16:36
  • I'm just guessing: leave the `PYTHONPATH` like in your example above and change the `["python", filename]` to `["python", "manage.py", filename]`. – C14L Mar 08 '21 at 17:10
  • @C14L You got me on the right track: With your suggestion I was able to invoke the Django-Management-Comment version of `Process.py` I mentioned earlier and solved my concern "_however: how would I go about executing that as a separate process?_" - simply by doing `while True:` in the handle method that never returns, which is fine, because the call is still wrapped in `Popen()`. Thanks. Feel free to post as an answer. – user11398730 Mar 08 '21 at 18:02
  • Glad it worked :) I added it as answer. – C14L Mar 10 '21 at 12:54

2 Answers2

1

Process.py should be a Django Management Command, so that the Django Framework is loaded and model.py used by Django.

The PYTHONPATH is still required so that Django knows where the base directory is.

Instad of ["python", filename], the process can be started with ["python", "manage.py", filename].

C14L
  • 12,153
  • 4
  • 39
  • 52
0

Probably you are missing the reference to your Django app, to import all models from it, you have to do in the python script:

from <django-app-name>.models import *
Patrick Freitas
  • 681
  • 1
  • 5
  • 18
  • nope: `ModuleNotFoundError: No module named 'django-app-name'` – user11398730 Mar 08 '21 at 17:45
  • And the question remains: why would it take a different import compared to the sibling module `views.py`? – user11398730 Mar 08 '21 at 17:47
  • If you process script is located on the same level that views.py and models.py, only "from . import models" should work fine. Can you share a print of your project folder structure? – Patrick Freitas Mar 08 '21 at 18:00
  • I thought that as well. The thing is that Django needs a bit of initialisation/setup/bootstrapping for the API etc. to be available. A regular script lives outside that environment. Nevermind, I solved the problem via django commands. Thank you for your answer. – user11398730 Mar 08 '21 at 18:07