Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sphinx and re-usable Django apps

Problem

While trying to generate sphinx documentation for a re-usable Django application I struck upon the following snafoo. When sphinx parses the model.py code it is thwarted by the code therein trying to access the Django project settings. As this is a standalone/reusable application there is not a primary project providing these settings i.e. there is no ROOT/PROJECT/PROJECT/settings.py file.

Setup

In the interest of clarity here is what I've done. Traverse to what would be the usual project folder cd ROOT/PROJECT and create an application django-admin startapp APPLICATION which produces the following structure

/ROOT/
  /PROJECT/
    /APPLICATION/ 
      admin.py
      apps.py
      models.py
      tests.py
      views.py

Note : There are no /ROOT/PROJECT/PROJECT/*.py files because I did not navigate to the root folder cd root and create a project using django-admin createproject as one normally might.

Next one creates the sphinx documentation spinx-quickstart docs producing the following additional structure.

/ROOT/
  /PROJECT/
    /docs/
      /source/ 
        ...
        conf.py
      make.bat

That is the docs are built next to the APPLICATION.

Question

What do I place within conf.py to properly load the application without there being a settings.py file ?

Homework

In trying to resolve this I have perused a number of SO Questions, Blogs and the Django Docs and not found a succinct solution. As this has been asked a few times before on SO I'd like to motivate that it not be closed as a duplicate, if the answer in the proposed duplicate uses one of these snippets as it's solution.

  • Fails with AppRegistryNotReady

    from django.conf import settings
    settings.configure()
    
  • Similar to the first method of failure i.e. emits AppRegistryNotReady

    from django.conf import settings
    settings.configure()
    settings.INSTALLED_APPS += ['fintech']
    
  • Fails with ImproperlyConfigured

    import django
    django.setup()
    
  • There is a really old solution mentioning the deprecated setup_environ

    from django.core.management import setup_environ
    from django.conf import settings
    settings.configure()
    setup_environ(settings)
    
  • This is also a favourite answer but fails if there is no settings.py file.

    import django
    os.environ['DJANGO_SETTINGS_MODULE'] = 'PROJECT.settings'
    django.setup()
    

I've also made this question rather verbose as the related questions on SO are rather terse and not especially helpful. If it's any help I'm using Django 1.10.

Update

I have since found that if one imports their setup function from setuptools versus distutils.core that one may invoke the setup script to compile their documentation as in python setup.py build_sphinx -b BUILDER It's probably best to re-ask this when invoking setup.py over docs/conf.py via either the make.bat or the MakeFile provided by Sphinx.

I suspect the outcome would be similar though, namely include the provided answers within docs/conf.py or alternatively within setup.py, both must be called within the same Python session after all.

like image 503
Carel Avatar asked Nov 07 '16 20:11

Carel


3 Answers

If you don't want to make your documentation dependent on a "demo" project, then you can manually build the settings in your conf.py. In the path setup section:

import django
from django.conf import settings
import os
import sys

# add path to sys.path (this might be different in your project)
sys.path.insert(0, os.path.abspath('..'))

# pass settings into configure
settings.configure(
    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'my_reusable_app',
        'any_other_dependencies',
    ]
)

# call django.setup to load installed apps and other stuff
django.setup()

# ... continue with rest of conf.py

Now Sphinx can import the app modules without having a project settings.py. Other settings can be passed to settings.configure().

like image 95
Greg Schmit Avatar answered Nov 11 '22 01:11

Greg Schmit


It's not an answer to the question

What do I place within conf.py to properly load the application without there being a settings.py file ?

but an alternative approach: Put a minimal, viable Django project in the reusable package. It can be used for implementation examples, testing (including code coverage), development and doc generation. We do it that way in our company's private repositories and I have seen a few django packages on github that do it too.

A typical structure of my packages looks like this:

docs
├── conf.py
├── ... rst files
mypackage          # the actual package
mypackage_demo     # the Django project
├── .coveragerc
├── manage.py
├── settings.py
README.rst
requirements.txt
setup.py

In docs/conf.py I check for a settings environment variable and use the demo project if non is given:

if not os.environ.get('DJANGO_SETTINGS_MODULE'):
    os.environ['DJANGO_SETTINGS_MODULE'] = 'mypackage_demo.settings'
django.setup()

That way you could build the docs using another project by setting the DJANGO_SETTINGS_MODULE environment variable before calling sphinx.

settings.py just contains the minimal configuration necessary to run the application. The absolute minimum for Django to work are these:

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
SECRET_KEY = ''
INSTALLED_APPS = [
    'mypackage'
]
like image 4
masterfloda Avatar answered Nov 11 '22 02:11

masterfloda


I did have the same issue with Django 1.11 and Sphinx 1.5.5. I did not manage to get it working properly and all the solutions you mentioned above did not work either. In the end I solved it by adding this to my Sphinx conf.py:

import sys, os

project_path = os.path.abspath('.')
# For Django to know where to find stuff.
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "my_project.settings")
sys.path.append(project_path)

# For settings.py to load.
os.chdir(project_path)

# For the models to load.
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()
like image 1
Maxime Deuse Avatar answered Nov 11 '22 01:11

Maxime Deuse