Creating a Reusable Django app
Tags: django, python
Published: 2022-10-02 17:57:26

Django Versatility does not stop at creating APIs or Impressive Web apps, We can also create packages from the apps within our Django Projects. In this article, we’ll be creating a package, uploading it to test.pypi and looking at how to use that package with other projects.

![](https://cdn-images-1.medium.com/max/2000/1*xdk_hsbIJPcVVtuxz_DeHw.png)

## How Reusable?

Before We begin, it is crucial to understand how the package we intend to create will work in the projects of others. Will our package gives functions to the project, or will it work as a standalone and interact with the database? Is my package going to need data from the project? Will my package pass data to the template? Etc. Answering these questions can help you better grasp how to structure your package.

## Creating your Package

We’ll be using [This repo](https://github.com/gr1nch3/django_memo.git) (also below) to create our package. It’s a simple memo app that allows users to save and check memos if it’s a task.
[**GitHub - gr1nch3/django_memo: A simple Django memo application**](https://github.com/gr1nch3/django_memo.git)

The project structure should be like this:

    django_memo
      |_django_memo
      |_memo
      |_theme
      |_manage.py

The first step is to move the app we want to turn into a package to another folder, you can copy or cut it to the folder. You should pick a meaningful name for the folder, since it’s what your package will be referenced by. I’ll name mine django-memo. Outside that folder, we will create a virtual environment.(make sure your package and its virtual environment are in a separate workspace or folder

    # first
    python3 -m venv my-env
    # second
    source my-env/bin/activate # macos/linux
    my-env/scripts/activate.bat # windows

Next is to install twine and setuptools, we’ll use them to build our package and test it.

    pip install twine setuptools

### Extras

In the folder you moved your app to(django-memo in my case), we are going to add the following files that will configure and build your package: **setup.cfg, LICENSE, pyproject.toml, README.md **and **MANIFEST.in **.

**pyproject.toml**

Add the following to your **pyproject.toml **file, and make sure the setuptools version matches the one you’ve installed.

    [build-system]
    requires = ['setuptools >= 65.3.0', 'wheel']
    build-backend = 'setuptools.build_meta:__legacy__'

**setup.cfg**

This file contains the metadata for **PyPI **and other requirements for your package. Make sure to check the requirements for your package version and set accordingly.

    [metadata]
    name = django-memo
    version = 1.2
    description = A Django app to take and manage memo
    readme = "README.md"
    long_description = file: README.md 
    long_description_content_type=text/markdown
    url = [https://www.example.com/](https://www.example.com/)
    author = gr1nch3
    author_email = example[@gmail.com](mailto:lloydm763@gmail.com)
    license = MIT License
    classifiers =
        Environment :: Web Environment
        Framework :: Django
        Framework :: Django :: 4.0 # set your version
        Intended Audience :: Developers
        License :: OSI Approved :: MIT License
        Operating System :: OS Independent
        Programming Language :: Python
        Programming Language :: Python :: 3
        Programming Language :: Python :: 3 :: Only
        Programming Language :: Python :: 3.9
        Programming Language :: Python :: 3.10
        Topic :: Communications :: Chat

    [options]
    include_package_data = true
    packages = find:
    python_requires = >=3.9
    install_requires =
        Django >= 4.0 # set your version

**Note: **the **long_description **by default is .rst, but .md works for me. The **long_description **and **long_description_content_type **needed to be configured like that to pass the **twine** check.

**README**

Your README file needs to be properly written in order to upload to pypi. It should also tell how to set up your package in a Django project.

    =====
    Memo
    =====

    memo is a Django app to create memos and manage them

    Detailed documentation is in the "docs" directory.

    Quick start
    -----------

    1. Add "memo" to your INSTALLED_APPS setting like this::

    INSTALLED_APPS = [
            ...
            'memo',
      ]

    2. Include the memo URLconf in your project urls.py like this::

    path('memo/', include('memo.urls')),

    3. Run ``python manage.py migrate`` to create the memo models.

    4. Visit '[http://127.0.0.1:8000/memo/'](http://127.0.0.1:8000/memo/') to check out your memos

**LICENSE**

The License will contain the license for your package. We’ll use a MIT License

    # MIT License

    Copyright (c) [year] [your full name]

    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.

**MANIFEST.in**

The manifest will include other files, folders and templates that your package needs.

    include LICENSE
    include README.md
    recursive-include docs *

**setup.py**

required to build the package

    from setuptools import setup

    setup()

After all the configurations, your package should look like this:

    ...(workspace or folder)
      |_my-env
      |_django-memo
        |_memo
        |_LICENSE
        |_MANIFEST.in
        |_pyproject.toml
        |_README.md
        |_setup.cfg
        |_setup.py

## Configuring our model

In this section, we will configure our memo model to work with the projects it will be installed in. Since the memo model requires an abstract user. We’ll set it so that the package gets the user model from the settings. Just passing the user model to the settings (with AUTH_USER_MODEL) and accessing it as settings.AUTH_USER_MODEL for our memo model would pass the model as a string. So we’ll use the get_user_model() from django.contrib.auth. Our memo model structure will change to the following:

    from django.db import models

    from django.contrib.auth import get_user_model # new

    MemoUser = get_user_model() # new
    # Create your models here.

    # ---------------------------------------------------------------------------- #
    #                           model of the memo detail                           #
    # ---------------------------------------------------------------------------- #

    class Memo(models.Model):
                                 # new
        user = models.ForeignKey(MemoUser, on_delete=models.CASCADE)
        title = models.CharField(max_length=100)
        memo = models.TextField(blank=True)
        created_at = models.DateTimeField(auto_now_add=True)
        updated_at = models.DateTimeField(auto_now=True)
        is_completed = models.BooleanField(default=False)

    def __str__(self):
            return self.title

    class Meta:
            db_table = 'memo'
            verbose_name = 'Memo'
            verbose_name_plural = 'Memos'
            ordering = ['-created_at']

Note: If you were to pass a model to the package and use it as a foreign key, you can check out the code for the get_user_model() function, it works for passing other models as well, and you would only need to set it up in the settings.py.

## Testing our Package

At this point, we’ve built and structured our app and have all files needed to turn it to a package.

### Build

There are two ways we could create the dist folder that contains the package.

 1. We could use setuptools and run the command python setup.py sdist in the directory that contains our app and the packaging files, and we would get a build that would still pass the twine check .

 2. We could also use the python build module’s python3 -m build command and get a similar result, and we would also get the wheel for the package

### Test

After you have created your dist folder, run the command twine check dist/* in the folder that contains the dist and you should see a message like this:

    # if you used the first method
    Checking dist/django-memo-1.2.tar.gz: PASSED

    # if you used the second method
    Checking dist/django_memo-1.2-py3-none-any.whl: PASSED
    Checking dist/django-memo-1.2.tar.gz: PASSED

### Installing our Package

To install our package locally in a project, we can run the following command: python -m pip install -U ./dist/django-memo-1.2.tar.gz .

## Uploading our Package

We are going to upload our package to [test.PyPI](https://test.pypi.org/). Make sure to create an account. When done, go to account settings and add API token and create a token. Remember to copy your token someplace safe because you’ll be using it for uploads to test.PyPI. To upload to the real PyPI, you can use your credentials.

After creating the token, in your terminal, run the following command:

    python3 -m twine upload --repository testpypi dist/*

You’ll be prompted for a username, and you should set the username value to __token__ . When you receive the prompt for a password, you add the token value you copied for the password. Results should be something similar:

    Uploading django_memo-1.2-py3-none-any.whl
    100% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 10.7/10.7 kB • 00:05 • 14.2 MB/s
    Uploading django-memo-1.2.tar.gz
    100% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9.6/9.6 kB • 00:00 • 9.6 MB/s

    View at:
    [https://test.pypi.org/project/django-memo/1.2/](https://test.pypi.org/project/django-memo/1.2/)

Congratulations! You Just created your first working package! If you follow the link from the result in your terminal, you should see your package on test.PyPI.

![](https://cdn-images-1.medium.com/max/3200/1*qgHT6KbYWGPdatiTKM4XSw.png)

To view the package, check it out in this repo:
[**GitHub - gr1nch3/django-memo**](https://github.com/gr1nch3/django-memo)