123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348 |
- =============================================
- Advanced tutorial: How to write reusable apps
- =============================================
- This advanced tutorial begins where :doc:`Tutorial 8 </intro/tutorial08>`
- left off. We'll be turning our web-poll into a standalone Python package
- you can reuse in new projects and share with other people.
- If you haven't recently completed Tutorials 1–8, we encourage you to review
- these so that your example project matches the one described below.
- Reusability matters
- ===================
- It's a lot of work to design, build, test and maintain a web application. Many
- Python and Django projects share common problems. Wouldn't it be great if we
- could save some of this repeated work?
- Reusability is the way of life in Python. `The Python Package Index (PyPI)
- <https://pypi.org/>`_ has a vast range of packages you can use in your own
- Python programs. Check out `Django Packages <https://djangopackages.org>`_ for
- existing reusable apps you could incorporate in your project. Django itself is
- also a normal Python package. This means that you can take existing Python
- packages or Django apps and compose them into your own web project. You only
- need to write the parts that make your project unique.
- Let's say you were starting a new project that needed a polls app like the one
- we've been working on. How do you make this app reusable? Luckily, you're well
- on the way already. In :doc:`Tutorial 1 </intro/tutorial01>`, we saw how we
- could decouple polls from the project-level URLconf using an ``include``.
- In this tutorial, we'll take further steps to make the app easy to use in new
- projects and ready to publish for others to install and use.
- .. admonition:: Package? App?
- A Python :term:`package` provides a way of grouping related Python code for
- easy reuse. A package contains one or more files of Python code (also known
- as "modules").
- A package can be imported with ``import foo.bar`` or ``from foo import
- bar``. For a directory (like ``polls``) to form a package, it must contain
- a special file ``__init__.py``, even if this file is empty.
- A Django *application* is a Python package that is specifically intended
- for use in a Django project. An application may use common Django
- conventions, such as having ``models``, ``tests``, ``urls``, and ``views``
- submodules.
- Later on we use the term *packaging* to describe the process of making a
- Python package easy for others to install. It can be a little confusing, we
- know.
- Your project and your reusable app
- ==================================
- After the previous tutorials, our project should look like this:
- .. code-block:: text
- djangotutorial/
- manage.py
- mysite/
- __init__.py
- settings.py
- urls.py
- asgi.py
- wsgi.py
- polls/
- __init__.py
- admin.py
- apps.py
- migrations/
- __init__.py
- 0001_initial.py
- models.py
- static/
- polls/
- images/
- background.png
- style.css
- templates/
- polls/
- detail.html
- index.html
- results.html
- tests.py
- urls.py
- views.py
- templates/
- admin/
- base_site.html
- You created ``djangotutorial/templates`` in :doc:`Tutorial 7
- </intro/tutorial07>`, and ``polls/templates`` in
- :doc:`Tutorial 3 </intro/tutorial03>`. Now perhaps it is clearer why we chose
- to have separate template directories for the project and application:
- everything that is part of the polls application is in ``polls``. It makes the
- application self-contained and easier to drop into a new project.
- The ``polls`` directory could now be copied into a new Django project and
- immediately reused. It's not quite ready to be published though. For that, we
- need to package the app to make it easy for others to install.
- .. _installing-reusable-apps-prerequisites:
- Installing some prerequisites
- =============================
- The current state of Python packaging is a bit muddled with various tools. For
- this tutorial, we're going to use :pypi:`setuptools` to build our package. It's
- the recommended packaging tool (merged with the ``distribute`` fork). We'll
- also be using :pypi:`pip` to install and uninstall it. You should install these
- two packages now. If you need help, you can refer to :ref:`how to install
- Django with pip<installing-official-release>`. You can install ``setuptools``
- the same way.
- Packaging your app
- ==================
- Python *packaging* refers to preparing your app in a specific format that can
- be easily installed and used. Django itself is packaged very much like
- this. For a small app like polls, this process isn't too difficult.
- #. First, create a parent directory for the package, outside of your Django
- project. Call this directory ``django-polls``.
- .. admonition:: Choosing a name for your app
- When choosing a name for your package, check PyPI to avoid naming
- conflicts with existing packages. We recommend using a ``django-``
- prefix for package names, to identify your package as specific to
- Django, and a corresponding ``django_`` prefix for your module name. For
- example, the ``django-ratelimit`` package contains the
- ``django_ratelimit`` module.
- Application labels (that is, the final part of the dotted path to
- application packages) *must* be unique in :setting:`INSTALLED_APPS`.
- Avoid using the same label as any of the Django :doc:`contrib packages
- </ref/contrib/index>`, for example ``auth``, ``admin``, or
- ``messages``.
- #. Move the ``polls`` directory into ``django-polls`` directory, and rename it
- to ``django_polls``.
- #. Edit ``django_polls/apps.py`` so that :attr:`~.AppConfig.name` refers to the
- new module name and add :attr:`~.AppConfig.label` to give a short name for
- the app:
- .. code-block:: python
- :caption: ``django-polls/django_polls/apps.py``
- from django.apps import AppConfig
- class PollsConfig(AppConfig):
- default_auto_field = "django.db.models.BigAutoField"
- name = "django_polls"
- label = "polls"
- #. Create a file ``django-polls/README.rst`` with the following contents:
- .. code-block:: rst
- :caption: ``django-polls/README.rst``
- ============
- django-polls
- ============
- django-polls is a Django app to conduct web-based polls. For each
- question, visitors can choose between a fixed number of answers.
- Detailed documentation is in the "docs" directory.
- Quick start
- -----------
- 1. Add "polls" to your INSTALLED_APPS setting like this::
- INSTALLED_APPS = [
- ...,
- "django_polls",
- ]
- 2. Include the polls URLconf in your project urls.py like this::
- path("polls/", include("django_polls.urls")),
- 3. Run ``python manage.py migrate`` to create the models.
- 4. Start the development server and visit the admin to create a poll.
- 5. Visit the ``/polls/`` URL to participate in the poll.
- #. Create a ``django-polls/LICENSE`` file. Choosing a license is beyond the
- scope of this tutorial, but suffice it to say that code released publicly
- without a license is *useless*. Django and many Django-compatible apps are
- distributed under the BSD license; however, you're free to pick your own
- license. Just be aware that your licensing choice will affect who is able
- to use your code.
- #. Next we'll create the ``pyproject.toml`` file which details how to build and
- install the app. A full explanation of this file is beyond the scope of this
- tutorial, but the `Python Packaging User Guide
- <https://packaging.python.org/guides/writing-pyproject-toml/>`_ has a good
- explanation. Create the ``django-polls/pyproject.toml`` file with the
- following contents:
- .. code-block:: toml
- :caption: ``django-polls/pyproject.toml``
- [build-system]
- requires = ["setuptools>=61.0"]
- build-backend = "setuptools.build_meta"
- [project]
- name = "django-polls"
- version = "0.1"
- dependencies = [
- "django>=X.Y", # Replace "X.Y" as appropriate
- ]
- description = "A Django app to conduct web-based polls."
- readme = "README.rst"
- requires-python = ">= 3.10"
- authors = [
- {name = "Your Name", email = "yourname@example.com"},
- ]
- classifiers = [
- "Environment :: Web Environment",
- "Framework :: Django",
- "Framework :: Django :: X.Y", # Replace "X.Y" as appropriate
- "Intended Audience :: Developers",
- "License :: OSI Approved :: BSD License",
- "Operating System :: OS Independent",
- "Programming Language :: Python",
- "Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3 :: Only",
- "Programming Language :: Python :: 3.10",
- "Programming Language :: Python :: 3.11",
- "Programming Language :: Python :: 3.12",
- "Programming Language :: Python :: 3.13",
- "Topic :: Internet :: WWW/HTTP",
- "Topic :: Internet :: WWW/HTTP :: Dynamic Content",
- ]
- [project.urls]
- Homepage = "https://www.example.com/"
- #. Many common files and Python modules and packages are included in the
- package by default. To include additional files, we'll need to create a
- ``MANIFEST.in`` file. To include the templates and static files, create a
- file ``django-polls/MANIFEST.in`` with the following contents:
- .. code-block:: text
- :caption: ``django-polls/MANIFEST.in``
- recursive-include django_polls/static *
- recursive-include django_polls/templates *
- #. It's optional, but recommended, to include detailed documentation with your
- app. Create an empty directory ``django-polls/docs`` for future
- documentation.
- Note that the ``docs`` directory won't be included in your package unless
- you add some files to it. Many Django apps also provide their documentation
- online through sites like `readthedocs.org <https://readthedocs.org>`_.
- #. Check that the :pypi:`build` package is installed (``python -m pip install
- build``) and try building your package by running ``python -m build`` inside
- ``django-polls``. This creates a directory called ``dist`` and builds your
- new package into source and binary formats, ``django-polls-0.1.tar.gz`` and
- ``django_polls-0.1-py3-none-any.whl``.
- For more information on packaging, see Python's `Tutorial on Packaging and
- Distributing Projects
- <https://packaging.python.org/tutorials/packaging-projects/>`_.
- Using your own package
- ======================
- Since we moved the ``polls`` directory out of the project, it's no longer
- working. We'll now fix this by installing our new ``django-polls`` package.
- .. admonition:: Installing as a user library
- The following steps install ``django-polls`` as a user library. Per-user
- installs have a lot of advantages over installing the package system-wide,
- such as being usable on systems where you don't have administrator access
- as well as preventing the package from affecting system services and other
- users of the machine.
- Note that per-user installations can still affect the behavior of system
- tools that run as that user, so using a virtual environment is a more robust
- solution (see below).
- #. To install the package, use pip (you already :ref:`installed it
- <installing-reusable-apps-prerequisites>`, right?):
- .. code-block:: shell
- python -m pip install --user django-polls/dist/django-polls-0.1.tar.gz
- #. Update ``mysite/settings.py`` to point to the new module name::
- INSTALLED_APPS = [
- "django_polls.apps.PollsConfig",
- ...,
- ]
- #. Update ``mysite/urls.py`` to point to the new module name::
- urlpatterns = [
- path("polls/", include("django_polls.urls")),
- ...,
- ]
- #. Run the development server to confirm the project continues to work.
- Publishing your app
- ===================
- Now that we've packaged and tested ``django-polls``, it's ready to share with
- the world! If this wasn't just an example, you could now:
- * Email the package to a friend.
- * Upload the package on your website.
- * Post the package on a public repository, such as `the Python Package Index
- (PyPI)`_. `packaging.python.org <https://packaging.python.org>`_ has `a good
- tutorial <https://packaging.python.org/tutorials/packaging-projects/#uploading-the-distribution-archives>`_
- for doing this.
- Installing Python packages with a virtual environment
- =====================================================
- Earlier, we installed ``django-polls`` as a user library. This has some
- disadvantages:
- * Modifying the user libraries can affect other Python software on your system.
- * You won't be able to run multiple versions of this package (or others with
- the same name).
- Typically, these situations only arise once you're maintaining several Django
- projects. When they do, the best solution is to use :doc:`venv
- <python:tutorial/venv>`. This tool allows you to maintain multiple isolated
- Python environments, each with its own copy of the libraries and package
- namespace.
|