4

I have the following script and it's meant to be a standalone Django script so I can run python my_script.py from the command line. It used to work with Django 1.8, after I upgrade to Django 1.11, I'm getting the following error:

Traceback (most recent call last):
  File "app.py", line 8, in <module>
    django.setup()
  File "C:\python27\lib\site-packages\django-1.11.5-py2.7.egg\django\__init__.py", line 22, in setup
    configure_logging(settings.LOGGING_CONFIG, settings.LOGGING)
  File "C:\python27\lib\site-packages\django-1.11.5-py2.7.egg\django\conf\__init__.py", line 56, in __getattr__
    self._setup(name)
  File "C:\python27\lib\site-packages\django-1.11.5-py2.7.egg\django\conf\__init__.py", line 41, in _setup
    self._wrapped = Settings(settings_module)
  File "C:\python27\lib\site-packages\django-1.11.5-py2.7.egg\django\conf\__init__.py", line 110, in __init__
    mod = importlib.import_module(self.SETTINGS_MODULE)
  File "C:\python27\lib\importlib\__init__.py", line 37, in import_module
    __import__(name)
ImportError: Import by filename is not supported.

This is my python script

# standalone django setup
import os, sys, logging, django
prj_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
logging.basicConfig(level=logging.INFO)
logging.info("PRJ_DIR: %s" % prj_dir)
sys.path.append(prj_dir)
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "%s.settings" % prj_dir.split("/")[-1])
django.setup()

...
...
Josh Kelley
  • 56,064
  • 19
  • 146
  • 246
user1187968
  • 7,154
  • 16
  • 81
  • 152
  • 1
    Could you please share your `settings.py`? Because there is not way I can reproduce your issue, and I suspect it could be related with the settings file. Thanks in advance – Vladir Parrado Cruz Sep 12 '17 at 16:19
  • 1
    you are using this file in the same folder as manage.py? – zero Sep 14 '17 at 03:33

6 Answers6

8

It seems you're trying to split a file path by the forward slash / while running your script on Windows where path separator is the backslash \. Use os.path.basename instead of manually dealing with the stings (.split("/")[-1]):

>>> import os
>>> os.path.basename(r'/home/user/project')
'project'
>>> os.path.basename(r'c:\users\user\project')
'project'

In comparison:

>>> r'/home/user/project'.split("/")[-1]
'project'  # works
>>> r'c:\users\user\project'.split("/")[-1]
'c:\\users\\user\\project'  # error
Igonato
  • 10,175
  • 3
  • 35
  • 64
4

Don't reinvent the wheel. There is a cool manage command on django extensions that lets you run scripts on the django context.

https://django-extensions.readthedocs.io/en/latest/runscript.html

Julio
  • 170
  • 3
  • That could be off topic for OP, but very interesting. (It does not do what is usually expected in questions about a [standalone Django script](https://stackoverflow.com/questions/8047204/django-script-to-access-model-objects-without-using-manage-py-shell).) Your link is inspirative in the combination with Burhan Khalid's link to "Lightweight Django" to write a simple script for easy testing SO answers with tags django-orm and django-queryset before posting: the models, the customized settings and example in a single file, everything other common for many questions in another file :-) – hynekcer Sep 14 '17 at 18:55
1

Others have pointed out an issue with your path manipulation, however the cause of your actual import exception is that you are setting DJANGO_SETTINGS_MODULE to the name of a file.

It needs to be the name of a Python module. In other words, your settings file should end in .py and you should pass the name of it (without .py), or the importable path to it - exactly as you would type it in an import statement.

If your settings are in my_settings.py, then you would set the variable to my_settings.

Here is a simplified example of a one-page django application taken from the excellent Lightweight Django book's excerpt:

import sys


from django.conf import settings


settings.configure(
    DEBUG=True,
    SECRET_KEY='thisisthesecretkey',
    ROOT_URLCONF=__name__,
    MIDDLEWARE_CLASSES=(
        'django.middleware.common.CommonMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ),
)


from django.conf.urls import url
from django.http import HttpResponse


def index(request):
    return HttpResponse('Hello World')


urlpatterns = (
    url(r'^$', index),
)


if __name__ == "__main__":
    from django.core.management import execute_from_command_line

    execute_from_command_line(sys.argv)
Burhan Khalid
  • 169,990
  • 18
  • 245
  • 284
  • 1
    You are generally right, however it is clear that prj_dir is (should be) set correctly the parent directory of the script directory on all platforms by this code, without any ".py". If it would be a linux name it is also split correctly, the result is `"DJANGO_SETTINGS_MODULE='parentdir.settings'`. Invalid "\\" character is the problem, but an added ".py" would be "ImportError: No module named py". (Thanks for Lightweight Django.) – hynekcer Sep 14 '17 at 17:00
0

The error is in

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "%s.settings" % prj_dir.split("/")[-1])

If you run this script from the same folder as manage.py, it will return the name of the manage.py folder instead settings.py folder resulting managepy_folder.settings

when it should output settingspy_folder.settings to access the app

Test with :

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settingspy_folder.settings")

if it that was the problem, if you want use the same algorithm or something similar in multiple projects, you need a pattern to modify the current folder name to indicate the settings.py folders name, using the same folders name in both wouldn't require modification.

if that doesn't fix please post your settings.py file

zero
  • 172
  • 1
  • 6
0

If your script is in the same folder as settings.py, the code should be like this:

import os, sys, logging, django
prj_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
logging.basicConfig(level=logging.INFO)
logging.info("PRJ_DIR: %s" % prj_dir)
sys.path.append(prj_dir)
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "{}.settings".format(os.path.basename(prj_dir)))
django.setup()

else, if your script is in the parent folder of settings.py:

import os, sys, logging, django
prj_dir = os.path.dirname(os.path.abspath(__file__))
logging.basicConfig(level=logging.INFO)
logging.info("PRJ_DIR: %s" % prj_dir)
sys.path.append(prj_dir)
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "{}.settings".format(os.path.basename(prj_dir)))
django.setup()
williezh
  • 917
  • 5
  • 8
0

in the line: os.environ.setdefault( "DJANGO_SETTINGS_MODULE", "myproject.settings") # should be like in import command )

Just use python module path: like "myproject.settings" instead of path like /opt/myproject/settings" or "c:\myproject\settings.py"

Yuvi
  • 11
  • 2