mirror of
				https://github.com/django/django.git
				synced 2025-10-29 16:46:11 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			336 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			336 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| =============================================
 | ||
| Advanced tutorial: How to write reusable apps
 | ||
| =============================================
 | ||
| 
 | ||
| This advanced tutorial begins where :doc:`Tutorial 7 </intro/tutorial07>`
 | ||
| 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–7, 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::
 | ||
| 
 | ||
|     mysite/
 | ||
|         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.gif
 | ||
|                     style.css
 | ||
|             templates/
 | ||
|                 polls/
 | ||
|                     detail.html
 | ||
|                     index.html
 | ||
|                     results.html
 | ||
|             tests.py
 | ||
|             urls.py
 | ||
|             views.py
 | ||
|         templates/
 | ||
|             admin/
 | ||
|                 base_site.html
 | ||
| 
 | ||
| You created ``mysite/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 setuptools_ to build our package. It's the
 | ||
| recommended packaging tool (merged with the ``distribute`` fork). We'll also be
 | ||
| using `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.
 | ||
| 
 | ||
| .. _setuptools: https://pypi.org/project/setuptools/
 | ||
| .. _pip: https://pypi.org/project/pip/
 | ||
| 
 | ||
| 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 ``polls``, 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 resources like PyPI to avoid
 | ||
|        naming conflicts with existing packages. It's often useful to prepend
 | ||
|        ``django-`` to your module name when creating a package to distribute.
 | ||
|        This helps others looking for Django apps identify your app as Django
 | ||
|        specific.
 | ||
| 
 | ||
|        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 the ``django-polls`` directory.
 | ||
| 
 | ||
| #. Create a file ``django-polls/README.rst`` with the following contents:
 | ||
| 
 | ||
|    .. code-block:: rst
 | ||
|        :caption: django-polls/README.rst
 | ||
| 
 | ||
|        =====
 | ||
|        Polls
 | ||
|        =====
 | ||
| 
 | ||
|        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 = [
 | ||
|                ...
 | ||
|                'polls',
 | ||
|            ]
 | ||
| 
 | ||
|        2. Include the polls URLconf in your project urls.py like this::
 | ||
| 
 | ||
|            path('polls/', include('polls.urls')),
 | ||
| 
 | ||
|        3. Run ``python manage.py migrate`` to create the polls models.
 | ||
| 
 | ||
|        4. Start the development server and visit http://127.0.0.1:8000/admin/
 | ||
|           to create a poll (you'll need the Admin app enabled).
 | ||
| 
 | ||
|        5. Visit http://127.0.0.1:8000/polls/ 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 ``pyproject.toml``, ``setup.cfg``, and ``setup.py`` files
 | ||
|    which detail how to build and install the app. A full explanation of these
 | ||
|    files is beyond the scope of this tutorial, but the `setuptools
 | ||
|    documentation <https://setuptools.readthedocs.io/en/latest/>`_ has a good
 | ||
|    explanation. Create the ``django-polls/pyproject.toml``,
 | ||
|    ``django-polls/setup.cfg``, and ``django-polls/setup.py`` files with the
 | ||
|    following contents:
 | ||
| 
 | ||
|    .. code-block:: toml
 | ||
|         :caption: django-polls/pyproject.toml
 | ||
| 
 | ||
|         [build-system]
 | ||
|         requires = ['setuptools>=40.8.0', 'wheel']
 | ||
|         build-backend = 'setuptools.build_meta:__legacy__'
 | ||
| 
 | ||
|    .. code-block:: ini
 | ||
|         :caption: django-polls/setup.cfg
 | ||
| 
 | ||
|         [metadata]
 | ||
|         name = django-polls
 | ||
|         version = 0.1
 | ||
|         description = A Django app to conduct web-based polls.
 | ||
|         long_description = file: README.rst
 | ||
|         url = https://www.example.com/
 | ||
|         author = Your Name
 | ||
|         author_email = yourname@example.com
 | ||
|         license = BSD-3-Clause  # Example license
 | ||
|         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.8
 | ||
|             Programming Language :: Python :: 3.9
 | ||
|             Topic :: Internet :: WWW/HTTP
 | ||
|             Topic :: Internet :: WWW/HTTP :: Dynamic Content
 | ||
| 
 | ||
|         [options]
 | ||
|         include_package_data = true
 | ||
|         packages = find:
 | ||
|         python_requires = >=3.8
 | ||
|         install_requires =
 | ||
|             Django >= X.Y  # Replace "X.Y" as appropriate
 | ||
| 
 | ||
|    .. code-block:: python
 | ||
|         :caption: django-polls/setup.py
 | ||
| 
 | ||
|         from setuptools import setup
 | ||
| 
 | ||
|         setup()
 | ||
| 
 | ||
| #. Only Python modules and packages are included in the package by default. To
 | ||
|    include additional files, we'll need to create a ``MANIFEST.in`` file. The
 | ||
|    setuptools docs referred to in the previous step discuss this file in more
 | ||
|    detail. To include the templates, the ``README.rst`` and our ``LICENSE``
 | ||
|    file, create a file ``django-polls/MANIFEST.in`` with the following
 | ||
|    contents:
 | ||
| 
 | ||
|    .. code-block:: text
 | ||
|        :caption: django-polls/MANIFEST.in
 | ||
| 
 | ||
|        include LICENSE
 | ||
|        include README.rst
 | ||
|        recursive-include polls/static *
 | ||
|        recursive-include polls/templates *
 | ||
| 
 | ||
| #. It's optional, but recommended, to include detailed documentation with your
 | ||
|    app. Create an empty directory ``django-polls/docs`` for future
 | ||
|    documentation. Add an additional line to ``django-polls/MANIFEST.in``::
 | ||
| 
 | ||
|     recursive-include docs *
 | ||
| 
 | ||
|    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>`_.
 | ||
| 
 | ||
| #. Try building your package with ``python setup.py sdist`` (run from inside
 | ||
|    ``django-polls``). This creates a directory called ``dist`` and builds your
 | ||
|    new package, ``django-polls-0.1.tar.gz``.
 | ||
| 
 | ||
| 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?)::
 | ||
| 
 | ||
|     python -m pip install --user django-polls/dist/django-polls-0.1.tar.gz
 | ||
| 
 | ||
| #. With luck, your Django project should now work correctly again. Run the
 | ||
|    server again to confirm this.
 | ||
| 
 | ||
| #. To uninstall the package, use pip::
 | ||
| 
 | ||
|     python -m pip uninstall django-polls
 | ||
| 
 | ||
| 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 the polls app 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.
 |