| 1 | ============================================= |
| 2 | Advanced tutorial: How to write reusable apps |
| 3 | ============================================= |
| 4 | |
| 5 | This advanced tutorial begins where :doc:`Tutorial 4 </intro/tutorial04>` left |
| 6 | off. We'll be turning our Web-poll into a standalone Python package you can |
| 7 | reuse in new projects and share with other people. |
| 8 | |
| 9 | If you haven't recently completed Tutorials 1–4, we encourage you to review |
| 10 | these so that your example project matches the one described below. |
| 11 | |
| 12 | .. Outline: |
| 13 | |
| 14 | .. * motivation |
| 15 | .. * what is a python package? |
| 16 | .. * what is a django app? |
| 17 | .. * what is a reusable app? |
| 18 | |
| 19 | .. * preparing |
| 20 | .. * moving templates into your app |
| 21 | .. * parent directory |
| 22 | .. * adding package boilerplate |
| 23 | .. * link to packaging docs |
| 24 | .. * package builder |
| 25 | |
| 26 | .. * using the package |
| 27 | .. * how to install |
| 28 | |
| 29 | .. * publishing |
| 30 | .. * options for publishing |
| 31 | .. * link to docs on PyPI |
| 32 | |
| 33 | Reusability matters |
| 34 | =================== |
| 35 | |
| 36 | It's a lot of work to design, build, test and maintain a web application. Many |
| 37 | Python and Django projects share common problems. Wouldn't it be great if we |
| 38 | could save some of this repeated work? |
| 39 | |
| 40 | Reusability is the way of life in Python. `The Python Package Index (PyPI) |
| 41 | <http://guide.python-distribute.org/contributing.html#pypi-info>`_ has a vast |
| 42 | range of packages you can use in your own Python programs. Check out `Django |
| 43 | Packages <http://www.djangopackages.com>`_ for existing reusable apps you could |
| 44 | incorporate in your project. Django itself is also just a Python package. This |
| 45 | means that you can take existing Python packages or Django apps and compose |
| 46 | them into your own web project. You only need to write the parts that make |
| 47 | your project unique. |
| 48 | |
| 49 | Let's say you were starting a new project that needed a polls app like the one |
| 50 | we've been working on. How do you make this app reusable? Luckily, you're well |
| 51 | on the way already. In :doc:`Tutorial 3 </intro/tutorial03>`, we saw how we |
| 52 | could decouple polls from the project-level URLconf using an ``include``. |
| 53 | In this tutorial, we'll take further steps to make the app easy to use in new |
| 54 | projects and ready to publish for others to install and use. |
| 55 | |
| 56 | .. admonition:: Package? App? |
| 57 | |
| 58 | A Python `package <http://docs.python.org/tutorial/modules.html#packages>`_ |
| 59 | provides a way of grouping related Python code for easy reuse. A package |
| 60 | contains one or more files of Python code (also known as "modules"). |
| 61 | |
| 62 | A package can be imported with ``import foo.bar`` or ``from foo import |
| 63 | bar``. For a directory (like ``polls``) to form a package, it must contain |
| 64 | a special file ``__init__.py``, even if this file is empty. |
| 65 | |
| 66 | A Django *app* is just a Python package that is specifically intended for |
| 67 | use in a Django project. An app may also use common Django conventions, |
| 68 | such as having a ``models.py`` file. |
| 69 | |
| 70 | Later on we use the term *packaging* to describe the process of making a |
| 71 | Python package easy for others to install. It can be a little confusing, we |
| 72 | know. |
| 73 | |
| 74 | Completing your reusable app |
| 75 | ============================ |
| 76 | |
| 77 | After the previous tutorials, our project should look like this:: |
| 78 | |
| 79 | mysite/ |
| 80 | manage.py |
| 81 | mysite/ |
| 82 | __init__.py |
| 83 | settings.py |
| 84 | urls.py |
| 85 | wsgi.py |
| 86 | polls/ |
| 87 | admin.py |
| 88 | __init__.py |
| 89 | models.py |
| 90 | tests.py |
| 91 | urls.py |
| 92 | views.py |
| 93 | |
| 94 | You also have a directory somewhere called ``mytemplates`` which you created in |
| 95 | :doc:`Tutorial 2 </intro/tutorial02>`. You specified its location in the |
| 96 | TEMPLATE_DIRS setting. This directory should look like this:: |
| 97 | |
| 98 | mytemplates/ |
| 99 | admin/ |
| 100 | base_site.html |
| 101 | polls/ |
| 102 | detail.html |
| 103 | index.html |
| 104 | results.html |
| 105 | |
| 106 | The polls app is already a Python package, thanks to the ``polls/__init__.py`` |
| 107 | file. That's a great start, but we can't just pick up this package and drop it |
| 108 | into a new project. The polls templates are currently stored in the |
| 109 | project-wide ``mytemplates`` directory. To make the app self-contained, it |
| 110 | should also contain the necessary templates. |
| 111 | |
| 112 | Inside the ``polls`` app, create a new ``templates`` directory. Now move the |
| 113 | ``polls`` template directory from ``mytemplates`` into the new |
| 114 | ``templates``. Your project should now look like this:: |
| 115 | |
| 116 | mysite/ |
| 117 | manage.py |
| 118 | mysite/ |
| 119 | __init__.py |
| 120 | settings.py |
| 121 | urls.py |
| 122 | wsgi.py |
| 123 | polls/ |
| 124 | admin.py |
| 125 | __init__.py |
| 126 | models.py |
| 127 | templates/ |
| 128 | polls/ |
| 129 | detail.html |
| 130 | index.html |
| 131 | results.html |
| 132 | tests.py |
| 133 | urls.py |
| 134 | views.py |
| 135 | |
| 136 | Your project-wide templates directory should now look like this:: |
| 137 | |
| 138 | mytemplates/ |
| 139 | admin/ |
| 140 | base_site.html |
| 141 | |
| 142 | Looking good! Now would be a good time to confirm that your polls application |
| 143 | still works correctly. How does Django know how to find the new location of |
| 144 | the polls templates even though we didn't modify :setting:`TEMPLATE_DIRS`? |
| 145 | Django has a :setting:`TEMPLATE_LOADERS` setting which contains a list |
| 146 | of callables that know how to import templates from various sources. One of |
| 147 | the defaults is :class:`django.template.loaders.app_directories.Loader` which |
| 148 | looks for a "templates" subdirectory in each of the :setting:`INSTALLED_APPS`. |
| 149 | |
| 150 | The ``polls`` directory could now be copied into a new Django project and |
| 151 | immediately reused. It's not quite ready to be published though. For that, we |
| 152 | need to package the app to make it easy for others to install. |
| 153 | |
| 154 | .. admonition:: Why nested? |
| 155 | |
| 156 | Why create a ``polls`` directory under ``templates`` when we're |
| 157 | already inside the polls app? This directory is needed to avoid conflicts in |
| 158 | Django's ``app_directories`` template loader. For example, if two |
| 159 | apps had a template called ``base.html``, without the extra directory, it |
| 160 | wouldn't be possible to distinguish between the two. It's a good convention |
| 161 | to use the name of your app for this directory. |
| 162 | |
| 163 | .. _installing-reuseable-apps-prerequisites: |
| 164 | |
| 165 | Installing some prerequisites |
| 166 | ============================= |
| 167 | |
| 168 | The current state of Python packaging is a bit muddled with various tools. For |
| 169 | this tutorial, we're going to use distribute_ to build our package. It's a |
| 170 | community-maintained fork of the older ``setuptools`` project. We'll also be |
| 171 | using `pip`_ to uninstall it after we're finished. You should install these |
| 172 | two packages now. If you need help, you can refer to :ref:`how to install |
| 173 | Django with pip<installing-official-release>`. You can install ``distribute`` |
| 174 | the same way. |
| 175 | |
| 176 | .. _distribute: http://pypi.python.org/pypi/distribute |
| 177 | .. _pip: http://pypi.python.org/pypi/pip |
| 178 | |
| 179 | Packaging your app |
| 180 | ================== |
| 181 | |
| 182 | Python *packaging* refers to preparing your app in a specific format that can |
| 183 | be easily installed and used. Django itself is packaged very much like |
| 184 | this. For a small app like polls, this process isn't too difficult. |
| 185 | |
| 186 | 1. First, create a parent directory for ``polls``, outside of your Django |
| 187 | project. Call this directory ``django-polls``. |
| 188 | |
| 189 | .. admonition:: Choosing a name for your app |
| 190 | |
| 191 | When choosing a name for your package, check resources like PyPI to avoid |
| 192 | naming conflicts with existing packages. It's often useful to prepend |
| 193 | ``django-`` to your module name when creating a package to distribute. |
| 194 | This helps others looking for Django apps identify your app as Django |
| 195 | specific. |
| 196 | |
| 197 | 2. Move the ``polls`` directory into the ``django-polls`` directory. |
| 198 | |
| 199 | 3. Create a file ``django-polls/README.txt`` with the following contents:: |
| 200 | |
| 201 | ===== |
| 202 | Polls |
| 203 | ===== |
| 204 | |
| 205 | Polls is a simple Django app to conduct Web-based polls. For each |
| 206 | question, visitors can choose between a fixed number of answers. |
| 207 | |
| 208 | Detailed documentation is in the "docs" directory. |
| 209 | |
| 210 | Quick start |
| 211 | ----------- |
| 212 | |
| 213 | 1. Add "polls" to your INSTALLED_APPS setting like this:: |
| 214 | |
| 215 | INSTALLED_APPS = ( |
| 216 | ... |
| 217 | 'polls', |
| 218 | ) |
| 219 | |
| 220 | 2. Include the polls URLconf in your project urls.py like this:: |
| 221 | |
| 222 | url(r'^polls/', include('polls.urls')), |
| 223 | |
| 224 | 3. Run `python manage.py syncdb` to create the polls models. |
| 225 | |
| 226 | 4. Start the development server and visit http://127.0.0.1:8000/admin/ |
| 227 | to create a poll (you'll need the Admin app enabled). |
| 228 | |
| 229 | 5. Visit http://127.0.0.1:8000/polls/ to participate in the poll. |
| 230 | |
| 231 | 4. Create a ``django-polls/LICENSE`` file. Choosing a license is beyond the |
| 232 | scope of this tutorial, but suffice it to say that code released publicly |
| 233 | without a license is *useless*. Django and many Django-compatible apps are |
| 234 | distributed under the BSD license; however, you're free to pick your own |
| 235 | license. Just be aware that your licensing choice will affect who is able |
| 236 | to use your code. |
| 237 | |
| 238 | 5. Next we'll create a ``setup.py`` file which provides details about how to |
| 239 | build and install the app. A full explanation of this file is beyond the |
| 240 | scope of this tutorial, but the `distribute docs |
| 241 | <http://packages.python.org/distribute/setuptools.html>`_ have a good explanation. |
| 242 | Create a file ``django-polls/setup.py`` with the following contents:: |
| 243 | |
| 244 | import os |
| 245 | from setuptools import setup |
| 246 | |
| 247 | README = open(os.path.join(os.path.dirname(__file__), 'README.txt')).read() |
| 248 | |
| 249 | # allow setup.py to be run from any path |
| 250 | os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir))) |
| 251 | |
| 252 | setup( |
| 253 | name = 'django-polls', |
| 254 | version = '0.1', |
| 255 | packages = ['polls'], |
| 256 | include_package_data = True, |
| 257 | license = 'BSD License', # example license |
| 258 | description = 'A simple Django app to conduct Web-based polls.', |
| 259 | long_description = README, |
| 260 | url = 'http://www.example.com/', |
| 261 | author = 'Your Name', |
| 262 | author_email = 'yourname@example.com', |
| 263 | classifiers = [ |
| 264 | 'Environment :: Web Environment', |
| 265 | 'Framework :: Django', |
| 266 | 'Intended Audience :: Developers', |
| 267 | 'License :: OSI Approved :: BSD License', # example license |
| 268 | 'Operating System :: OS Independent', |
| 269 | 'Programming Language :: Python', |
| 270 | 'Programming Language :: Python :: 2.6', |
| 271 | 'Programming Language :: Python :: 2.7', |
| 272 | 'Topic :: Internet :: WWW/HTTP', |
| 273 | 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', |
| 274 | ], |
| 275 | ) |
| 276 | |
| 277 | .. admonition:: I thought you said we were going to use ``distibute``? |
| 278 | |
| 279 | Distribute is a drop-in replacement for ``setuptools``. Even though we |
| 280 | appear to import from ``setuptools``, since we have ``distribute`` |
| 281 | installed, it will override the import. |
| 282 | |
| 283 | 6. Only Python modules and packages are included in the package by default. To |
| 284 | include additional files, we'll need to create a ``MANIFEST.in`` file. The |
| 285 | distribute docs referred to in the previous step discuss this file in more |
| 286 | details. To include the templates and our LICENSE file, create a file |
| 287 | ``django-polls/MANIFEST.in`` with the following contents:: |
| 288 | |
| 289 | include LICENSE |
| 290 | recursive-include polls/templates * |
| 291 | |
| 292 | 7. It's optional, but recommended, to include detailed documentation with your |
| 293 | app. Create an empty directory ``django-polls/docs`` for future |
| 294 | documentation. Add an additional line to ``django-polls/MANIFEST.in``:: |
| 295 | |
| 296 | recursive-include docs * |
| 297 | |
| 298 | Note that the ``docs`` directory won't be included in your package unless |
| 299 | you add some files to it. Many Django apps also provide their documentation |
| 300 | online through sites like `readthedocs.org <http://readthedocs.org>`_. |
| 301 | |
| 302 | 8. Try building your package with ``python setup.py sdist`` (run from inside |
| 303 | ``django-polls``). This creates a directory called ``dist`` and builds your |
| 304 | new package, ``django-polls-0.1.tar.gz``. |
| 305 | |
| 306 | For more information on packaging, see `The Hitchhiker's Guide to Packaging |
| 307 | <http://guide.python-distribute.org/quickstart.html>`_. |
| 308 | |
| 309 | Using your own package |
| 310 | ====================== |
| 311 | |
| 312 | Since we moved the ``polls`` directory out of the project, it's no longer |
| 313 | working. We'll now fix this by installing our new ``django-polls`` package. |
| 314 | |
| 315 | .. admonition:: Installing as a system library |
| 316 | |
| 317 | The following steps install ``django-polls`` as a system library. In |
| 318 | general, it's best to avoid messing with your system libraries to avoid |
| 319 | breaking things. For this simple example though, the risk is low and it will |
| 320 | help with understanding packaging. We'll explain how to uninstall in |
| 321 | step 4. |
| 322 | |
| 323 | For experienced users, a neater way to manage your packages is to use |
| 324 | "virtualenv" (see below). |
| 325 | |
| 326 | 1. Inside ``django-polls/dist``, untar the new package |
| 327 | ``django-polls-0.1.tar.gz`` (e.g. ``tar xzvf django-polls-0.1.tar.gz``). If |
| 328 | you're using Windows, you can download the command-line tool bsdtar_ to do |
| 329 | this, or you can use a GUI-based tool such as 7-zip_. |
| 330 | |
| 331 | 2. Change into the directory created in step 1 (e.g. ``cd django-polls-0.1``). |
| 332 | |
| 333 | 3. If you're using GNU/Linux, Mac OS X or some other flavor of Unix, enter the |
| 334 | command ``sudo python setup.py install`` at the shell prompt. If you're |
| 335 | using Windows, start up a command shell with administrator privileges and |
| 336 | run the command ``setup.py install``. |
| 337 | |
| 338 | With luck, your Django project should now work correctly again. Run the |
| 339 | server again to confirm this. |
| 340 | |
| 341 | 4. To uninstall the package, use pip (you already :ref:`installed it |
| 342 | <installing-reuseable-apps-prerequisites>`, right?):: |
| 343 | |
| 344 | sudo pip uninstall django-polls |
| 345 | |
| 346 | .. _bsdtar: http://gnuwin32.sourceforge.net/packages/bsdtar.htm |
| 347 | .. _7-zip: http://www.7-zip.org/ |
| 348 | .. _pip: http://pypi.python.org/pypi/pip |
| 349 | |
| 350 | Publishing your app |
| 351 | =================== |
| 352 | |
| 353 | Now that we've packaged and tested ``django-polls``, it's ready to share with |
| 354 | the world! If this wasn't just an example, you could now: |
| 355 | |
| 356 | * Email the package to a friend. |
| 357 | |
| 358 | * Upload the package on your Web site. |
| 359 | |
| 360 | * Post the package on a public repository, such as `The Python Package Index |
| 361 | (PyPI) <http://guide.python-distribute.org/contributing.html#pypi-info>`_. |
| 362 | |
| 363 | For more information on PyPI, see the `Quickstart |
| 364 | <http://guide.python-distribute.org/quickstart.html#register-your-package-with-the-python-package-index-pypi>`_ |
| 365 | section of The Hitchhiker's Guide to Packaging. One detail this guide mentions |
| 366 | is choosing the license under which your code is distributed. |
| 367 | |
| 368 | Installing Python packages with virtualenv |
| 369 | ========================================== |
| 370 | |
| 371 | Earlier, we installed the polls app as a system library. This has some |
| 372 | disadvantages: |
| 373 | |
| 374 | * Modifying the system libraries can affect other Python software on your |
| 375 | system. |
| 376 | |
| 377 | * You won't be able to run multiple versions of this package (or others with |
| 378 | the same name). |
| 379 | |
| 380 | Typically, these situations only arise once you're maintaining several Django |
| 381 | projects. When they do, the best solution is to use `virtualenv |
| 382 | <http://www.virtualenv.org/>`_. This tool allows you to maintain multiple |
| 383 | isolated Python environments, each with its own copy of the libraries and |
| 384 | package namespace. |