In pre-1.4 the directory containing the settings.py had to be on sys.path in order to be able to have custom apps installed there and to be able to reference them without using the project name. When manage.py is run Django did a trick of temporarily adding the parent directory to sys.path, importing the project.settings, then removing the parent from the sys.path. In order for this to work, there had to be an __init__.py in the settings directory, making it a package. It works, because once a package is imported, it no longer needs to be on the path to import its contents, enabling, for example, importing app.urls among other things, but without littering the top level namespace with any other modules or packages that happen to be in the parent directory
In 1.4+ an extra directory exists, by default, duplicating the project name, and manage.py is moved up to the higher level. As this directory is already on the path (if manage.py is run from there), no fiddling with a temporary add of the parent is needed, nor done. The outer directory is not a package (no __init__.py). The project package from which settings, urls, and wsgi can be imported, is just on the path, so the project name quilified modules load fine. If apps shall be not being qualified by the project name (so they can override ones in site-packages or so they are portable) the have are started in the outer directory. In contrast, apps that have to be qualified with the project name are started in the inner directory.
There are three different important information regarding the project path. The name of a project (PROJECT_NAME), the path to the directory containing manage.py (PROJECT_ROOT) and the path to the directory containing settings.py (SETTINGS_DIR).
import os, django SETTINGS_DIR = os.path.abspath(__file__) PROJECT_ROOT, PROJECT_NAME = os.path.split(SETTINGS_DIR) if django.VERSION[:2]<(1, 4): PROJECT_ROOT = SETTINGS_DIR