Packaging a Django project using setuptools

Anmol Porwal
DeHaat
Published in
4 min readJun 21, 2021

--

In Python, a package is a directory (containing __init__.py) with source files (also known as modules) that can be imported.

Example:

my_package
|
|--__init__.py

The term packaging implies wrapping a Django project into a package that can be deployed so that others can easily integrate into their project.

Changes required in Django project

Let’s take an example of a Django project on which we can work on to package it. Assuming following is the structure of your current Django project:

my_package/
|-- manage.py
|-- receipts/
|--fixtures/
|--receipts.json
|-- migrations/
|--0001_initial.py
|--__init__.py
|-- __init__.py
|-- admin.py
|-- apps.py
|-- models.py
|-- urls.py
|-- views.py
|-- tests.py
|-- blog/
|--fixtures/
|--blog.json
|-- migrations/
|--0001_initial.py
|--__init__.py
|-- __init__.py
|-- admin.py
|-- apps.py
|-- models.py
|-- urls.py
|-- views.py
|-- tests.py
|--my_package/
|--__init__.py
|--asgi.py
|--settings.py
|--urls.py
|--wsgi.py
|--README.rst

These are few files which we have to create for making our project installable:

MANIFEST.in
LICENSE
README.rst (if not already there)
pyproject.toml
setup.py
boot_django/
|--__init__.py
|--boot_django.py
  • MANIFEST.in will contain text files and static files which should be included in the package, for example:
include README.rst
include LICENSE
recursive-include blog/static * (if present)
  • Example LICENSE file (Update it according to your use case, refer: LICENSES):
MIT License

Copyright (c) [year] [fullname]

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
  • pyproject.toml file contains the build dependencies, which will be required while building the package, for example:
[build-system]
requires = [
"setuptools >= 50.0.0",
"wheel",
]
build-backend = "setuptools.build_meta"
  • setup.py , which has the instructions of how to package your project.
    We will use setuptools to package our application, it helps in easily build and distribute packages.
import setuptools

with open("README.md", "r", encoding="utf-8") as fh:
long_description = fh.read()

setuptools.setup(
name="my_package",
version="v0.0.1",
install_requires=[
"Django==3.2.4",
"django-extensions==3.0.3",
"django-filter==2.3.0",
"djangorestframework==3.11.2",
"django-storages==1.9.1",
"django-environ==0.4.5",
"django-cors-headers==3.3.0",
"django-summernote==0.8.11.6",
"django-model-utils==4.0.0",
"django-redis==4.12.1",
.
.
.
],
author="<your-name>",
author_email="<your-email>",
description="Django project",
long_description=long_description,
long_description_content_type="text/markdown",
url="https://github.com/<your-github-handle>/<your-repo>",
project_urls={
"Bug Tracker": "https://github.com/<your-github-handle>/<your-repo>/issues",
},
classifiers=[
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
],
package_dir={"": "."},
packages=setuptools.find_packages(where="."),
python_requires=">=3.9",
scripts=["manage.py"],
)
  1. install_requirescontains the minimal project dependencies that are required to run the project. When the project is installed with pip, this specification will be used to install its dependencies.
  2. scripts contains the scripts that are outside the django applications and need to be packaged with the project, for example if we add manage.py in the scripts, this will be added in the path and you’ll be able to run any manage.py commands directly, likemanage.py runserver
  3. classifiers is the data written for users, who can get useful information from it

Refer: writing the setup script to know more about how to write setup.py

  • And finally boot_django.py which will be used to load Django settings and setup Django before using the package:
import os
import django


BASE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "../my_package"))


def boot_django():
settings.configure(
BASE_DIR=BASE_DIR,
DEBUG=True,
DATABASES={
"default":{
"ENGINE":"django.db.backends.sqlite3",
"NAME": os.path.join(BASE_DIR, "db.sqlite3"),
}
},
INSTALLED_APPS=[
"blog",
"receipts",
]
)

django.setup()

Above example is a stripped down configuration — you can add the mandatory settings here or directly import settings of your project like:

import os
import django


BASE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "../my_package"))


def boot_django():
import my_package.settings

django.setup()

Packaging and Installing the Django project

As we have finalised creation of our application followed by making necessary amendments to the file structure to prepare it for a flawless distribution build, we can begin with going through the packaging operations.

Creating distribution file

$ cd my_package
$ python setup.py sdist
$ python setup.py bdist_wheel

Second and third commands with build the source and binary distributions of the package, it’ll create dist/ and build/ directories in the Django project itself where the source distribution (my_package-<version>.tar.gz) and binary distribution (my_package-<version>-py3-none-any.whl) are stored.

Installing the application

In order to install the application, run the following:

$ python setup.py install

The source or binary distributions created in above section can also be used to install the package, using following command:

$ pip install <source_distribution>
or
$ pip install <binary_distribution>

You can publish this to the internal pip server (if your organization has it) so that internal users can install the package or PyPi using appropriate credentials so that it’s available to everyone.

Testing the installable Django project

Now that we have packaged our project, it’s time to test it. For using modules from my_package we have to run boot_django() (which will configure the Django setting and do the setup) before running any command.

Below snippet shows how to use my_package :

from boot_django.boot_django import boot_django
from blog.models import Blog
boot_django()
blogs.objects.all()

NOTE: You will have to set all the environment variables needed to run the Django project before using my_package as a library.

Conclusion

In this tutorial we learned:

  • Creating files (MANIFEST.in , LICENSE , README.rst ) for making the Django project installable.
  • Building setup.py file to define the package.
  • Writing a script boot_django.py to configure settings and setup Django while using the package.
  • Installing and testing the packaged Django project.

--

--