0
0
mirror of https://github.com/django/django.git synced 2024-11-29 22:56:46 +01:00
django/docs/intro/reusable-apps.txt

322 lines
12 KiB
Plaintext
Raw Normal View History

=============================================
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 17, 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 just a 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 3 </intro/tutorial03>`, 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 just 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
wsgi.py
polls/
__init__.py
Simplified default project template. Squashed commit of: commit 508ec9144b35c50794708225b496bde1eb5e60aa Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Tue Jan 29 22:50:55 2013 +0100 Tweaked default settings file. * Explained why BASE_DIR exists. * Added a link to the database configuration options, and put it in its own section. * Moved sensitive settings that must be changed for production at the top. commit 6515fd2f1aa73a86dc8dbd2ccf512ddb6b140d57 Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Tue Jan 29 14:35:21 2013 +0100 Documented the simplified app & project templates in the changelog. commit 2c5b576c2ea91d84273a019b3d0b3b8b4da72f23 Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Tue Jan 29 13:59:27 2013 +0100 Minor fixes in tutorials 5 and 6. commit 55a51531be8104f21b3cca3f6bf70b0a7139a041 Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Tue Jan 29 13:51:11 2013 +0100 Updated tutorial 2 for the new project template. commit 29ddae87bdaecff12dd31b16b000c01efbde9e20 Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Tue Jan 29 11:58:54 2013 +0100 Updated tutorial 1 for the new project template. commit 0ecb9f6e2514cfd26a678a280d471433375101a3 Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Tue Jan 29 11:29:13 2013 +0100 Adjusted the default URLconf detection to account for the admin. It's now enabled by default. commit 5fb4da0d3d09dac28dd94e3fde92b9d4335c0565 Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Tue Jan 29 10:36:55 2013 +0100 Added security warnings for the most sensitive settings. commit 718d84bd8ac4a42fb4b28ec93965de32680f091e Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Mon Jan 28 23:24:06 2013 +0100 Used an absolute path for the SQLite database. This ensures the settings file works regardless of which directory django-admin.py / manage.py is invoked from. BASE_DIR got a +1 from a BDFL and another core dev. It doesn't involve the concept of a "Django project"; it's just a convenient way to express relative paths within the source code repository for non-Python files. Thanks Jacob Kaplan-Moss for the suggestion. commit 1b559b4bcda622e10909b68fe5cab90db6727dd9 Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Mon Jan 28 23:22:40 2013 +0100 Removed STATIC_ROOT from the default settings template. It isn't necessary in development, and it confuses beginners to no end. Thanks Carl Meyer for the suggestion. commit a55f141a500bb7c9a1bc259bbe1954c13b199671 Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Mon Jan 28 23:21:43 2013 +0100 Removed MEDIA_ROOT/URL from default settings template. Many sites will never deal with user-uploaded files, and MEDIA_ROOT is complicated to explain. Thanks Carl Meyer for the suggestion. commit 44bf2f2441420fd9429ee9fe1f7207f92dd87e70 Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Mon Jan 28 22:22:09 2013 +0100 Removed logging config. This configuration is applied regardless of the value of LOGGING; duplicating it in LOGGING is confusing. commit eac747e848eaed65fd5f6f254f0a7559d856f88f Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Mon Jan 28 22:05:31 2013 +0100 Enabled the locale middleware by default. USE_I18N is True by default, and doesn't work well without LocaleMiddleware. commit d806c62b2d00826dc2688c84b092627b8d571cab Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Mon Jan 28 22:03:16 2013 +0100 Enabled clickjacking protection by default. commit 99152c30e6a15003f0b6737dc78e87adf462aacb Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Mon Jan 28 22:01:48 2013 +0100 Reorganized settings in logical sections, and trimmed comments. commit d37ffdfcb24b7e0ec7cc113d07190f65fb12fb8a Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Mon Jan 28 16:54:11 2013 +0100 Avoided misleading TEMPLATE_DEBUG = DEBUG. According to the docs TEMPLATE_DEBUG works only when DEBUG = True. commit 15d9478d3a9850e85841e7cf09cf83050371c6bf Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Mon Jan 28 16:46:25 2013 +0100 Removed STATICFILES_FINDERS/TEMPLATE_LOADERS from default settings file. Only developers with special needs ever need to change these settings. commit 574da0eb5bfb4570883756914b4dbd7e20e1f61e Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Mon Jan 28 16:45:01 2013 +0100 Removed STATICFILES/TEMPLATES_DIRS from default settings file. The current best practice is to put static files and templates in applications, for easier testing and deployment. commit 8cb18dbe56629aa1be74718a07e7cc66b4f9c9f0 Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Mon Jan 28 16:24:16 2013 +0100 Removed settings related to email reporting from default settings file. While handy for small scale projects, it isn't exactly a best practice. commit 8ecbfcb3638058f0c49922540f874a7d802d864f Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Tue Jan 29 18:54:43 2013 +0100 Documented how to enable the sites framework. commit 23fc91a6fa67d91ddd9d71b1c3e0dc26bdad9841 Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Mon Jan 28 16:28:59 2013 +0100 Disabled the sites framework by default. RequestSite does the job for single-domain websites. commit c4d82eb8afc0eb8568bf9c4d12644272415e3960 Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Tue Jan 29 00:08:33 2013 +0100 Added a default admin.py to the application template. Thanks Ryan D Hiebert for the suggestion. commit 4071dc771e5c44b1c5ebb9beecefb164ae465e22 Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Mon Jan 28 10:59:49 2013 +0100 Enabled the admin by default. Everyone uses the admin. commit c807a31f8d89e7e7fd97380e3023f7983a8b6fcb Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Mon Jan 28 10:57:05 2013 +0100 Removed admindocs from default project template. commit 09e4ce0e652a97da1a9e285046a91c8ad7a9189c Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Mon Jan 28 16:32:52 2013 +0100 Added links to the settings documentation. commit 5b8f5eaef364eb790fcde6f9e86f7d266074cca8 Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Mon Jan 28 11:06:54 2013 +0100 Used a significant example for URLconf includes. commit 908e91d6fcee2a3cb51ca26ecdf12a6a24e69ef8 Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Mon Jan 28 16:22:31 2013 +0100 Moved code comments about WSGI to docs, and rewrote said docs. commit 50417e51996146f891d08ca8b74dcc736a581932 Author: Aymeric Augustin <aymeric.augustin@m4x.org> Date: Mon Jan 28 15:51:50 2013 +0100 Normalized the default application template. Removed the default test that 1 + 1 = 2, because it's been committed way too many times, in too many projects. Added an import of `render` for views, because the first view will often be: def home(request): return render(request, "mysite/home.html")
2013-01-28 15:51:50 +01:00
admin.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.
1. 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``.
2. Move the ``polls`` directory into the ``django-polls`` directory.
3. Create a file ``django-polls/README.rst`` with the following contents:
.. code-block:: rst
:caption: django-polls/README.rst
=====
Polls
=====
Polls is a simple 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.
4. 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.
5. Next we'll create a ``setup.py`` file which provides details about how to
build and install the app. A full explanation of this file is beyond the
scope of this tutorial, but the `setuptools docs
<https://setuptools.readthedocs.io/en/latest/>`_ have a good
explanation. Create a file ``django-polls/setup.py`` with the following
contents:
.. code-block:: python
:caption: django-polls/setup.py
import os
from setuptools import find_packages, setup
with open(os.path.join(os.path.dirname(__file__), 'README.rst')) as readme:
README = readme.read()
# allow setup.py to be run from any path
os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir)))
setup(
name='django-polls',
version='0.1',
packages=find_packages(),
include_package_data=True,
license='BSD License', # example license
description='A simple Django app to conduct Web-based polls.',
long_description=README,
url='https://www.example.com/',
author='Your Name',
author_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', # example license
'Operating System :: OS Independent',
'Programming Language :: Python',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Topic :: Internet :: WWW/HTTP',
'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
],
)
6. 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
details. 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 *
7. 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>`_.
8. 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/distributing/>`_.
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 ``virtualenv`` is a more robust solution
(see below).
1. To install the package, use pip (you already :ref:`installed it
<installing-reusable-apps-prerequisites>`, right?)::
pip install --user django-polls/dist/django-polls-0.1.tar.gz
2. With luck, your Django project should now work correctly again. Run the
server again to confirm this.
3. To uninstall the package, use pip::
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/distributing/#uploading-your-project-to-pypi>`_
for doing this.
Installing Python packages with virtualenv
==========================================
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 `virtualenv
<https://virtualenv.pypa.io/>`_. This tool allows you to maintain multiple
isolated Python environments, each with its own copy of the libraries and
package namespace.