diff options
Diffstat (limited to 'parts/django/docs/intro')
31 files changed, 2722 insertions, 0 deletions
diff --git a/parts/django/docs/intro/_images/admin01.png b/parts/django/docs/intro/_images/admin01.png Binary files differnew file mode 100644 index 0000000..28f14d6 --- /dev/null +++ b/parts/django/docs/intro/_images/admin01.png diff --git a/parts/django/docs/intro/_images/admin02.png b/parts/django/docs/intro/_images/admin02.png Binary files differnew file mode 100644 index 0000000..4b49ebb --- /dev/null +++ b/parts/django/docs/intro/_images/admin02.png diff --git a/parts/django/docs/intro/_images/admin02t.png b/parts/django/docs/intro/_images/admin02t.png Binary files differnew file mode 100644 index 0000000..d7519d1 --- /dev/null +++ b/parts/django/docs/intro/_images/admin02t.png diff --git a/parts/django/docs/intro/_images/admin03.png b/parts/django/docs/intro/_images/admin03.png Binary files differnew file mode 100644 index 0000000..635226c --- /dev/null +++ b/parts/django/docs/intro/_images/admin03.png diff --git a/parts/django/docs/intro/_images/admin03t.png b/parts/django/docs/intro/_images/admin03t.png Binary files differnew file mode 100644 index 0000000..94273cb --- /dev/null +++ b/parts/django/docs/intro/_images/admin03t.png diff --git a/parts/django/docs/intro/_images/admin04.png b/parts/django/docs/intro/_images/admin04.png Binary files differnew file mode 100644 index 0000000..982420a --- /dev/null +++ b/parts/django/docs/intro/_images/admin04.png diff --git a/parts/django/docs/intro/_images/admin04t.png b/parts/django/docs/intro/_images/admin04t.png Binary files differnew file mode 100644 index 0000000..a2ec8bb --- /dev/null +++ b/parts/django/docs/intro/_images/admin04t.png diff --git a/parts/django/docs/intro/_images/admin05.png b/parts/django/docs/intro/_images/admin05.png Binary files differnew file mode 100644 index 0000000..b424393 --- /dev/null +++ b/parts/django/docs/intro/_images/admin05.png diff --git a/parts/django/docs/intro/_images/admin05t.png b/parts/django/docs/intro/_images/admin05t.png Binary files differnew file mode 100644 index 0000000..a5da950 --- /dev/null +++ b/parts/django/docs/intro/_images/admin05t.png diff --git a/parts/django/docs/intro/_images/admin06.png b/parts/django/docs/intro/_images/admin06.png Binary files differnew file mode 100644 index 0000000..5f24d4e --- /dev/null +++ b/parts/django/docs/intro/_images/admin06.png diff --git a/parts/django/docs/intro/_images/admin06t.png b/parts/django/docs/intro/_images/admin06t.png Binary files differnew file mode 100644 index 0000000..fb65e0a --- /dev/null +++ b/parts/django/docs/intro/_images/admin06t.png diff --git a/parts/django/docs/intro/_images/admin07.png b/parts/django/docs/intro/_images/admin07.png Binary files differnew file mode 100644 index 0000000..b21022f --- /dev/null +++ b/parts/django/docs/intro/_images/admin07.png diff --git a/parts/django/docs/intro/_images/admin08.png b/parts/django/docs/intro/_images/admin08.png Binary files differnew file mode 100644 index 0000000..ddac57e --- /dev/null +++ b/parts/django/docs/intro/_images/admin08.png diff --git a/parts/django/docs/intro/_images/admin08t.png b/parts/django/docs/intro/_images/admin08t.png Binary files differnew file mode 100644 index 0000000..83773bb --- /dev/null +++ b/parts/django/docs/intro/_images/admin08t.png diff --git a/parts/django/docs/intro/_images/admin09.png b/parts/django/docs/intro/_images/admin09.png Binary files differnew file mode 100644 index 0000000..ba7de1b --- /dev/null +++ b/parts/django/docs/intro/_images/admin09.png diff --git a/parts/django/docs/intro/_images/admin10.png b/parts/django/docs/intro/_images/admin10.png Binary files differnew file mode 100644 index 0000000..07a9bf3 --- /dev/null +++ b/parts/django/docs/intro/_images/admin10.png diff --git a/parts/django/docs/intro/_images/admin11.png b/parts/django/docs/intro/_images/admin11.png Binary files differnew file mode 100644 index 0000000..6c583fd --- /dev/null +++ b/parts/django/docs/intro/_images/admin11.png diff --git a/parts/django/docs/intro/_images/admin11t.png b/parts/django/docs/intro/_images/admin11t.png Binary files differnew file mode 100644 index 0000000..af792b8 --- /dev/null +++ b/parts/django/docs/intro/_images/admin11t.png diff --git a/parts/django/docs/intro/_images/admin12.png b/parts/django/docs/intro/_images/admin12.png Binary files differnew file mode 100644 index 0000000..aac5c0d --- /dev/null +++ b/parts/django/docs/intro/_images/admin12.png diff --git a/parts/django/docs/intro/_images/admin13.png b/parts/django/docs/intro/_images/admin13.png Binary files differnew file mode 100644 index 0000000..49a5950 --- /dev/null +++ b/parts/django/docs/intro/_images/admin13.png diff --git a/parts/django/docs/intro/_images/admin13t.png b/parts/django/docs/intro/_images/admin13t.png Binary files differnew file mode 100644 index 0000000..7dc01e1 --- /dev/null +++ b/parts/django/docs/intro/_images/admin13t.png diff --git a/parts/django/docs/intro/_images/admin14.png b/parts/django/docs/intro/_images/admin14.png Binary files differnew file mode 100644 index 0000000..b1f4a54 --- /dev/null +++ b/parts/django/docs/intro/_images/admin14.png diff --git a/parts/django/docs/intro/_images/admin14t.png b/parts/django/docs/intro/_images/admin14t.png Binary files differnew file mode 100644 index 0000000..86c3acc --- /dev/null +++ b/parts/django/docs/intro/_images/admin14t.png diff --git a/parts/django/docs/intro/index.txt b/parts/django/docs/intro/index.txt new file mode 100644 index 0000000..bc61be7 --- /dev/null +++ b/parts/django/docs/intro/index.txt @@ -0,0 +1,36 @@ +Getting started +=============== + +New to Django? Or to Web development in general? Well, you came to the right +place: read this material to quickly get up and running. + +.. toctree:: + :maxdepth: 1 + + overview + install + tutorial01 + tutorial02 + tutorial03 + tutorial04 + whatsnext + +.. seealso:: + + If you're new to Python_, you might want to start by getting an idea of what + the language is like. Django is 100% Python, so if you've got minimal + comfort with Python you'll probably get a lot more out of Django. + + If you're new to programming entirely, you might want to start with this + `list of Python resources for non-programmers`_ + + If you already know a few other languages and want to get up to speed with + Python quickly, we recommend `Dive Into Python`_ (also available in a + `dead-tree version`_). If that's not quite your style, there are quite + a few other `books about Python`_. + + .. _python: http://python.org/ + .. _list of Python resources for non-programmers: http://wiki.python.org/moin/BeginnersGuide/NonProgrammers + .. _dive into python: http://diveintopython.org/ + .. _dead-tree version: http://www.amazon.com/exec/obidos/ASIN/1590593561/ref=nosim/jacobian20 + .. _books about Python: http://wiki.python.org/moin/PythonBooks
\ No newline at end of file diff --git a/parts/django/docs/intro/install.txt b/parts/django/docs/intro/install.txt new file mode 100644 index 0000000..327686f --- /dev/null +++ b/parts/django/docs/intro/install.txt @@ -0,0 +1,84 @@ +Quick install guide +=================== + +Before you can use Django, you'll need to get it installed. We have a +:doc:`complete installation guide </topics/install>` that covers all the +possibilities; this guide will guide you to a simple, minimal installation +that'll work while you walk through the introduction. + +Install Python +-------------- + +Being a Python Web framework, Django requires Python. It works with any Python +version from 2.4 to 2.7 (due to backwards +incompatibilities in Python 3.0, Django does not currently work with +Python 3.0; see :doc:`the Django FAQ </faq/install>` for more +information on supported Python versions and the 3.0 transition), but we recommend installing Python 2.5 or later. If you do so, you won't need to set up a database just yet: Python 2.5 or later includes a lightweight database called SQLite_. + +.. _sqlite: http://sqlite.org/ + +Get Python at http://www.python.org. If you're running Linux or Mac OS X, you +probably already have it installed. + +.. admonition:: Django on Jython + + If you use Jython_ (a Python implementation for the Java platform), you'll + need to follow a few additional steps. See :doc:`/howto/jython` for details. + +.. _jython: http://www.jython.org/ + +You can verify that Python's installed by typing ``python`` from your shell; you should see something like:: + + Python 2.5.1 (r251:54863, Jan 17 2008, 19:35:17) + [GCC 4.0.1 (Apple Inc. build 5465)] on darwin + Type "help", "copyright", "credits" or "license" for more information. + >>> + +Set up a database +----------------- + +If you installed Python 2.5 or later, you can skip this step for now. + +If not, or if you'd like to work with a "large" database engine like PostgreSQL, +MySQL, or Oracle, consult the :ref:`database installation information +<database-installation>`. + +Remove any old versions of Django +--------------------------------- + +If you are upgrading your installation of Django from a previous version, you +will need to :ref:`uninstall the old Django version before installing the new +version <removing-old-versions-of-django>`. + +Install Django +-------------- + +You've got three easy options to install Django: + + * Install a version of Django :doc:`provided by your operating system + distribution </misc/distributions>`. This is the quickest option for those + who have operating systems that distribute Django. + + * :ref:`Install an official release <installing-official-release>`. This + is the best approach for users who want a stable version number and aren't + concerned about running a slightly older version of Django. + + * :ref:`Install the latest development version + <installing-development-version>`. This is best for users who want the + latest-and-greatest features and aren't afraid of running brand-new code. + +.. admonition:: Always refer to the documentation that corresponds to the + version of Django you're using! + + If you do either of the first two steps, keep an eye out for parts of the + documentation marked **new in development version**. That phrase flags + features that are only available in development versions of Django, and + they likely won't work with an official release. + +That's it! +---------- + +That's it -- you can now :doc:`move onto the tutorial </intro/tutorial01>`. + + + diff --git a/parts/django/docs/intro/overview.txt b/parts/django/docs/intro/overview.txt new file mode 100644 index 0000000..34572a6 --- /dev/null +++ b/parts/django/docs/intro/overview.txt @@ -0,0 +1,324 @@ +================== +Django at a glance +================== + +Because Django was developed in a fast-paced newsroom environment, it was +designed to make common Web-development tasks fast and easy. Here's an informal +overview of how to write a database-driven Web app with Django. + +The goal of this document is to give you enough technical specifics to +understand how Django works, but this isn't intended to be a tutorial or +reference -- but we've got both! When you're ready to start a project, you can +:doc:`start with the tutorial </intro/tutorial01>` or :doc:`dive right into more +detailed documentation </topics/index>`. + +Design your model +================= + +Although you can use Django without a database, it comes with an +object-relational mapper in which you describe your database layout in Python +code. + +The :doc:`data-model syntax </topics/db/models>` offers many rich ways of +representing your models -- so far, it's been solving two years' worth of +database-schema problems. Here's a quick example, which might be saved in +the file ``mysite/news/models.py``:: + + class Reporter(models.Model): + full_name = models.CharField(max_length=70) + + def __unicode__(self): + return self.full_name + + class Article(models.Model): + pub_date = models.DateTimeField() + headline = models.CharField(max_length=200) + content = models.TextField() + reporter = models.ForeignKey(Reporter) + + def __unicode__(self): + return self.headline + +Install it +========== + +Next, run the Django command-line utility to create the database tables +automatically: + +.. code-block:: bash + + manage.py syncdb + +The :djadmin:`syncdb` command looks at all your available models and creates +tables in your database for whichever tables don't already exist. + +Enjoy the free API +================== + +With that, you've got a free, and rich, :doc:`Python API </topics/db/queries>` to +access your data. The API is created on the fly, no code generation necessary:: + + # Import the models we created from our "news" app + >>> from news.models import Reporter, Article + + # No reporters are in the system yet. + >>> Reporter.objects.all() + [] + + # Create a new Reporter. + >>> r = Reporter(full_name='John Smith') + + # Save the object into the database. You have to call save() explicitly. + >>> r.save() + + # Now it has an ID. + >>> r.id + 1 + + # Now the new reporter is in the database. + >>> Reporter.objects.all() + [<Reporter: John Smith>] + + # Fields are represented as attributes on the Python object. + >>> r.full_name + 'John Smith' + + # Django provides a rich database lookup API. + >>> Reporter.objects.get(id=1) + <Reporter: John Smith> + >>> Reporter.objects.get(full_name__startswith='John') + <Reporter: John Smith> + >>> Reporter.objects.get(full_name__contains='mith') + <Reporter: John Smith> + >>> Reporter.objects.get(id=2) + Traceback (most recent call last): + ... + DoesNotExist: Reporter matching query does not exist. + + # Create an article. + >>> from datetime import datetime + >>> a = Article(pub_date=datetime.now(), headline='Django is cool', + ... content='Yeah.', reporter=r) + >>> a.save() + + # Now the article is in the database. + >>> Article.objects.all() + [<Article: Django is cool>] + + # Article objects get API access to related Reporter objects. + >>> r = a.reporter + >>> r.full_name + 'John Smith' + + # And vice versa: Reporter objects get API access to Article objects. + >>> r.article_set.all() + [<Article: Django is cool>] + + # The API follows relationships as far as you need, performing efficient + # JOINs for you behind the scenes. + # This finds all articles by a reporter whose name starts with "John". + >>> Article.objects.filter(reporter__full_name__startswith="John") + [<Article: Django is cool>] + + # Change an object by altering its attributes and calling save(). + >>> r.full_name = 'Billy Goat' + >>> r.save() + + # Delete an object with delete(). + >>> r.delete() + +A dynamic admin interface: it's not just scaffolding -- it's the whole house +============================================================================ + +Once your models are defined, Django can automatically create a professional, +production ready :doc:`administrative interface </ref/contrib/admin/index>` -- a Web +site that lets authenticated users add, change and delete objects. It's as easy +as registering your model in the admin site:: + + # In models.py... + + from django.db import models + + class Article(models.Model): + pub_date = models.DateTimeField() + headline = models.CharField(max_length=200) + content = models.TextField() + reporter = models.ForeignKey(Reporter) + + + # In admin.py in the same directory... + + import models + from django.contrib import admin + + admin.site.register(models.Article) + +The philosophy here is that your site is edited by a staff, or a client, or +maybe just you -- and you don't want to have to deal with creating backend +interfaces just to manage content. + +One typical workflow in creating Django apps is to create models and get the +admin sites up and running as fast as possible, so your staff (or clients) can +start populating data. Then, develop the way data is presented to the public. + +Design your URLs +================ + +A clean, elegant URL scheme is an important detail in a high-quality Web +application. Django encourages beautiful URL design and doesn't put any cruft +in URLs, like ``.php`` or ``.asp``. + +To design URLs for an app, you create a Python module called a :doc:`URLconf +</topics/http/urls>`. A table of contents for your app, it contains a simple mapping +between URL patterns and Python callback functions. URLconfs also serve to +decouple URLs from Python code. + +Here's what a URLconf might look like for the ``Reporter``/``Article`` +example above:: + + from django.conf.urls.defaults import * + + urlpatterns = patterns('', + (r'^articles/(\d{4})/$', 'news.views.year_archive'), + (r'^articles/(\d{4})/(\d{2})/$', 'news.views.month_archive'), + (r'^articles/(\d{4})/(\d{2})/(\d+)/$', 'news.views.article_detail'), + ) + +The code above maps URLs, as simple regular expressions, to the location of +Python callback functions ("views"). The regular expressions use parenthesis to +"capture" values from the URLs. When a user requests a page, Django runs +through each pattern, in order, and stops at the first one that matches the +requested URL. (If none of them matches, Django calls a special-case 404 view.) +This is blazingly fast, because the regular expressions are compiled at load +time. + +Once one of the regexes matches, Django imports and calls the given view, which +is a simple Python function. Each view gets passed a request object -- +which contains request metadata -- and the values captured in the regex. + +For example, if a user requested the URL "/articles/2005/05/39323/", Django +would call the function ``news.views.article_detail(request, +'2005', '05', '39323')``. + +Write your views +================ + +Each view is responsible for doing one of two things: Returning an +:class:`~django.http.HttpResponse` object containing the content for the +requested page, or raising an exception such as :class:`~django.http.Http404`. +The rest is up to you. + +Generally, a view retrieves data according to the parameters, loads a template +and renders the template with the retrieved data. Here's an example view for +``year_archive`` from above:: + + def year_archive(request, year): + a_list = Article.objects.filter(pub_date__year=year) + return render_to_response('news/year_archive.html', {'year': year, 'article_list': a_list}) + +This example uses Django's :doc:`template system </topics/templates>`, which has +several powerful features but strives to stay simple enough for non-programmers +to use. + +Design your templates +===================== + +The code above loads the ``news/year_archive.html`` template. + +Django has a template search path, which allows you to minimize redundancy among +templates. In your Django settings, you specify a list of directories to check +for templates. If a template doesn't exist in the first directory, it checks the +second, and so on. + +Let's say the ``news/article_detail.html`` template was found. Here's what that +might look like: + +.. code-block:: html+django + + {% extends "base.html" %} + + {% block title %}Articles for {{ year }}{% endblock %} + + {% block content %} + <h1>Articles for {{ year }}</h1> + + {% for article in article_list %} + <p>{{ article.headline }}</p> + <p>By {{ article.reporter.full_name }}</p> + <p>Published {{ article.pub_date|date:"F j, Y" }}</p> + {% endfor %} + {% endblock %} + +Variables are surrounded by double-curly braces. ``{{ article.headline }}`` +means "Output the value of the article's headline attribute." But dots aren't +used only for attribute lookup: They also can do dictionary-key lookup, index +lookup and function calls. + +Note ``{{ article.pub_date|date:"F j, Y" }}`` uses a Unix-style "pipe" (the "|" +character). This is called a template filter, and it's a way to filter the value +of a variable. In this case, the date filter formats a Python datetime object in +the given format (as found in PHP's date function; yes, there is one good idea +in PHP). + +You can chain together as many filters as you'd like. You can write custom +filters. You can write custom template tags, which run custom Python code behind +the scenes. + +Finally, Django uses the concept of "template inheritance": That's what the +``{% extends "base.html" %}`` does. It means "First load the template called +'base', which has defined a bunch of blocks, and fill the blocks with the +following blocks." In short, that lets you dramatically cut down on redundancy +in templates: each template has to define only what's unique to that template. + +Here's what the "base.html" template might look like: + +.. code-block:: html+django + + <html> + <head> + <title>{% block title %}{% endblock %}</title> + </head> + <body> + <img src="sitelogo.gif" alt="Logo" /> + {% block content %}{% endblock %} + </body> + </html> + +Simplistically, it defines the look-and-feel of the site (with the site's logo), +and provides "holes" for child templates to fill. This makes a site redesign as +easy as changing a single file -- the base template. + +It also lets you create multiple versions of a site, with different base +templates, while reusing child templates. Django's creators have used this +technique to create strikingly different cell-phone editions of sites -- simply +by creating a new base template. + +Note that you don't have to use Django's template system if you prefer another +system. While Django's template system is particularly well-integrated with +Django's model layer, nothing forces you to use it. For that matter, you don't +have to use Django's database API, either. You can use another database +abstraction layer, you can read XML files, you can read files off disk, or +anything you want. Each piece of Django -- models, views, templates -- is +decoupled from the next. + +This is just the surface +======================== + +This has been only a quick overview of Django's functionality. Some more useful +features: + + * A :doc:`caching framework </topics/cache>` that integrates with memcached + or other backends. + + * A :doc:`syndication framework </ref/contrib/syndication>` that makes + creating RSS and Atom feeds as easy as writing a small Python class. + + * More sexy automatically-generated admin features -- this overview barely + scratched the surface. + +The next obvious steps are for you to `download Django`_, read :doc:`the +tutorial </intro/tutorial01>` and join `the community`_. Thanks for your +interest! + +.. _download Django: http://www.djangoproject.com/download/ +.. _the community: http://www.djangoproject.com/community/ diff --git a/parts/django/docs/intro/tutorial01.txt b/parts/django/docs/intro/tutorial01.txt new file mode 100644 index 0000000..560070b --- /dev/null +++ b/parts/django/docs/intro/tutorial01.txt @@ -0,0 +1,690 @@ +===================================== +Writing your first Django app, part 1 +===================================== + +Let's learn by example. + +Throughout this tutorial, we'll walk you through the creation of a basic +poll application. + +It'll consist of two parts: + + * A public site that lets people view polls and vote in them. + * An admin site that lets you add, change and delete polls. + +We'll assume you have :doc:`Django installed </intro/install>` already. You can +tell Django is installed by running the Python interactive interpreter and +typing ``import django``. If that command runs successfully, with no errors, +Django is installed. + +.. admonition:: Where to get help: + + If you're having trouble going through this tutorial, please post a message + to `django-users`__ or drop by `#django on irc.freenode.net`__ to chat + with other Django users who might be able to help. + +__ http://groups.google.com/group/django-users +__ irc://irc.freenode.net/django + +Creating a project +================== + +If this is your first time using Django, you'll have to take care of some +initial setup. Namely, you'll need to auto-generate some code that establishes a +Django :term:`project` -- a collection of settings for an instance of Django, +including database configuration, Django-specific options and +application-specific settings. + +From the command line, ``cd`` into a directory where you'd like to store your +code, then run the command ``django-admin.py startproject mysite``. This will +create a ``mysite`` directory in your current directory. + +.. admonition:: Script name may differ in distribution packages + + If you installed Django using a Linux distribution's package manager + (e.g. apt-get or yum) ``django-admin.py`` may have been renamed to + ``django-admin``. You may continue through this documentation by omitting + ``.py`` from each command. + +.. admonition:: Mac OS X permissions + + If you're using Mac OS X, you may see the message "permission denied" when + you try to run ``django-admin.py startproject``. This is because, on + Unix-based systems like OS X, a file must be marked as "executable" before it + can be run as a program. To do this, open Terminal.app and navigate (using + the ``cd`` command) to the directory where :doc:`django-admin.py + </ref/django-admin>` is installed, then run the command + ``chmod +x django-admin.py``. + +.. note:: + + You'll need to avoid naming projects after built-in Python or Django + components. In particular, this means you should avoid using names like + ``django`` (which will conflict with Django itself) or ``test`` (which + conflicts with a built-in Python package). + +:doc:`django-admin.py </ref/django-admin>` should be on your system path if you +installed Django via ``python setup.py``. If it's not on your path, you can find +it in ``site-packages/django/bin``, where ```site-packages``` is a directory +within your Python installation. Consider symlinking to :doc:`django-admin.py +</ref/django-admin>` from some place on your path, such as +:file:`/usr/local/bin`. + +.. admonition:: Where should this code live? + + If your background is in PHP, you're probably used to putting code under the + Web server's document root (in a place such as ``/var/www``). With Django, + you don't do that. It's not a good idea to put any of this Python code + within your Web server's document root, because it risks the possibility + that people may be able to view your code over the Web. That's not good for + security. + + Put your code in some directory **outside** of the document root, such as + :file:`/home/mycode`. + +Let's look at what :djadmin:`startproject` created:: + + mysite/ + __init__.py + manage.py + settings.py + urls.py + +These files are: + + * :file:`__init__.py`: An empty file that tells Python that this directory + should be considered a Python package. (Read `more about packages`_ in the + official Python docs if you're a Python beginner.) + + * :file:`manage.py`: A command-line utility that lets you interact with this + Django project in various ways. You can read all the details about + :file:`manage.py` in :doc:`/ref/django-admin`. + + * :file:`settings.py`: Settings/configuration for this Django project. + :doc:`/topics/settings` will tell you all about how settings work. + + * :file:`urls.py`: The URL declarations for this Django project; a "table of + contents" of your Django-powered site. You can read more about URLs in + :doc:`/topics/http/urls`. + +.. _more about packages: http://docs.python.org/tutorial/modules.html#packages + +The development server +---------------------- + +Let's verify this worked. Change into the :file:`mysite` directory, if you +haven't already, and run the command ``python manage.py runserver``. You'll see +the following output on the command line:: + + Validating models... + 0 errors found. + + Django version 1.0, using settings 'mysite.settings' + Development server is running at http://127.0.0.1:8000/ + Quit the server with CONTROL-C. + +You've started the Django development server, a lightweight Web server written +purely in Python. We've included this with Django so you can develop things +rapidly, without having to deal with configuring a production server -- such as +Apache -- until you're ready for production. + +Now's a good time to note: DON'T use this server in anything resembling a +production environment. It's intended only for use while developing. (We're in +the business of making Web frameworks, not Web servers.) + +Now that the server's running, visit http://127.0.0.1:8000/ with your Web +browser. You'll see a "Welcome to Django" page, in pleasant, light-blue pastel. +It worked! + +.. admonition:: Changing the port + + By default, the :djadmin:`runserver` command starts the development server + on the internal IP at port 8000. + + If you want to change the server's port, pass + it as a command-line argument. For instance, this command starts the server + on port 8080: + + .. code-block:: bash + + python manage.py runserver 8080 + + If you want to change the server's IP, pass it along with the port. So to + listen on all public IPs (useful if you want to show off your work on other + computers), use: + + .. code-block:: bash + + python manage.py runserver 0.0.0.0:8000 + + Full docs for the development server can be found in the + :djadmin:`runserver` reference. + +Database setup +-------------- + +Now, edit :file:`settings.py`. It's a normal Python module with +module-level variables representing Django settings. Change the +following keys in the :setting:`DATABASES` ``'default'`` item to match +your databases connection settings. + + * :setting:`ENGINE` -- Either + ``'django.db.backends.postgresql_psycopg2'``, + ``'django.db.backends.mysql'`` or + ``'django.db.backends.sqlite3'``. Other backends are + :setting:`also available <ENGINE>`. + + * :setting:`NAME` -- The name of your database. If you're using + SQLite, the database will be a file on your computer; in that + case, :setting:`NAME` should be the full absolute path, + including filename, of that file. If the file doesn't exist, it + will automatically be created when you synchronize the database + for the first time (see below). + + When specifying the path, always use forward slashes, even on + Windows (e.g. ``C:/homes/user/mysite/sqlite3.db``). + + * :setting:`USER` -- Your database username (not used for SQLite). + + * :setting:`PASSWORD` -- Your database password (not used for + SQLite). + + * :setting:`HOST` -- The host your database is on. Leave this as + an empty string if your database server is on the same physical + machine (not used for SQLite). + +If you're new to databases, we recommend simply using SQLite (by +setting :setting:`ENGINE` to ``'django.db.backends.sqlite3'``). SQLite +is included as part of Python 2.5 and later, so you won't need to +install anything else. + +.. note:: + + If you're using PostgreSQL or MySQL, make sure you've created a database by + this point. Do that with "``CREATE DATABASE database_name;``" within your + database's interactive prompt. + + If you're using SQLite, you don't need to create anything beforehand - the + database file will be created automatically when it is needed. + +While you're editing :file:`settings.py`, take note of the +:setting:`INSTALLED_APPS` setting towards the bottom of the file. That variable +holds the names of all Django applications that are activated in this Django +instance. Apps can be used in multiple projects, and you can package and +distribute them for use by others in their projects. + +By default, :setting:`INSTALLED_APPS` contains the following apps, all of which +come with Django: + + * :mod:`django.contrib.auth` -- An authentication system. + + * :mod:`django.contrib.contenttypes` -- A framework for content types. + + * :mod:`django.contrib.sessions` -- A session framework. + + * :mod:`django.contrib.sites` -- A framework for managing multiple sites + with one Django installation. + + * :mod:`django.contrib.messages` -- A messaging framework. + +These applications are included by default as a convenience for the common case. + +Each of these applications makes use of at least one database table, though, +so we need to create the tables in the database before we can use them. To do +that, run the following command: + +.. code-block:: bash + + python manage.py syncdb + +The :djadmin:`syncdb` command looks at the :setting:`INSTALLED_APPS` setting and +creates any necessary database tables according to the database settings in your +:file:`settings.py` file. You'll see a message for each database table it +creates, and you'll get a prompt asking you if you'd like to create a superuser +account for the authentication system. Go ahead and do that. + +If you're interested, run the command-line client for your database and type +``\dt`` (PostgreSQL), ``SHOW TABLES;`` (MySQL), or ``.schema`` (SQLite) to +display the tables Django created. + +.. admonition:: For the minimalists + + Like we said above, the default applications are included for the common + case, but not everybody needs them. If you don't need any or all of them, + feel free to comment-out or delete the appropriate line(s) from + :setting:`INSTALLED_APPS` before running :djadmin:`syncdb`. The + :djadmin:`syncdb` command will only create tables for apps in + :setting:`INSTALLED_APPS`. + +.. _creating-models: + +Creating models +=============== + +Now that your environment -- a "project" -- is set up, you're set to start +doing work. + +Each application you write in Django consists of a Python package, somewhere +on your `Python path`_, that follows a certain convention. Django comes with a +utility that automatically generates the basic directory structure of an app, +so you can focus on writing code rather than creating directories. + +.. admonition:: Projects vs. apps + + What's the difference between a project and an app? An app is a Web + application that does something -- e.g., a Weblog system, a database of + public records or a simple poll app. A project is a collection of + configuration and apps for a particular Web site. A project can contain + multiple apps. An app can be in multiple projects. + +Your apps can live anywhere on your `Python path`_. In this tutorial, we'll +create our poll app in the :file:`mysite` directory for simplicity. + +To create your app, make sure you're in the :file:`mysite` directory and type +this command: + +.. code-block:: bash + + python manage.py startapp polls + +That'll create a directory :file:`polls`, which is laid out like this:: + + polls/ + __init__.py + models.py + tests.py + views.py + +This directory structure will house the poll application. + +The first step in writing a database Web app in Django is to define your models +-- essentially, your database layout, with additional metadata. + +.. admonition:: Philosophy + + A model is the single, definitive source of data about your data. It contains + the essential fields and behaviors of the data you're storing. Django follows + the :ref:`DRY Principle <dry>`. The goal is to define your data model in one + place and automatically derive things from it. + +In our simple poll app, we'll create two models: polls and choices. A poll has +a question and a publication date. A choice has two fields: the text of the +choice and a vote tally. Each choice is associated with a poll. + +These concepts are represented by simple Python classes. Edit the +:file:`polls/models.py` file so it looks like this:: + + from django.db import models + + class Poll(models.Model): + question = models.CharField(max_length=200) + pub_date = models.DateTimeField('date published') + + class Choice(models.Model): + poll = models.ForeignKey(Poll) + choice = models.CharField(max_length=200) + votes = models.IntegerField() + +The code is straightforward. Each model is represented by a class that +subclasses :class:`django.db.models.Model`. Each model has a number of class +variables, each of which represents a database field in the model. + +Each field is represented by an instance of a :class:`~django.db.models.Field` +class -- e.g., :class:`~django.db.models.CharField` for character fields and +:class:`~django.db.models.DateTimeField` for datetimes. This tells Django what +type of data each field holds. + +The name of each :class:`~django.db.models.Field` instance (e.g. ``question`` or +``pub_date`` ) is the field's name, in machine-friendly format. You'll use this +value in your Python code, and your database will use it as the column name. + +You can use an optional first positional argument to a +:class:`~django.db.models.Field` to designate a human-readable name. That's used +in a couple of introspective parts of Django, and it doubles as documentation. +If this field isn't provided, Django will use the machine-readable name. In this +example, we've only defined a human-readable name for ``Poll.pub_date``. For all +other fields in this model, the field's machine-readable name will suffice as +its human-readable name. + +Some :class:`~django.db.models.Field` classes have required elements. +:class:`~django.db.models.CharField`, for example, requires that you give it a +:attr:`~django.db.models.Field.max_length`. That's used not only in the database +schema, but in validation, as we'll soon see. + +Finally, note a relationship is defined, using +:class:`~django.db.models.ForeignKey`. That tells Django each Choice is related +to a single Poll. Django supports all the common database relationships: +many-to-ones, many-to-manys and one-to-ones. + +.. _`Python path`: http://docs.python.org/tutorial/modules.html#the-module-search-path + +Activating models +================= + +That small bit of model code gives Django a lot of information. With it, Django +is able to: + + * Create a database schema (``CREATE TABLE`` statements) for this app. + * Create a Python database-access API for accessing Poll and Choice objects. + +But first we need to tell our project that the ``polls`` app is installed. + +.. admonition:: Philosophy + + Django apps are "pluggable": You can use an app in multiple projects, and + you can distribute apps, because they don't have to be tied to a given + Django installation. + +Edit the :file:`settings.py` file again, and change the +:setting:`INSTALLED_APPS` setting to include the string ``'polls'``. So +it'll look like this:: + + INSTALLED_APPS = ( + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.sites', + 'polls' + ) + +Now Django knows to include the ``polls`` app. Let's run another +command: + +.. code-block:: bash + + python manage.py sql polls + +You should see something similar to the following (the ``CREATE TABLE`` SQL +statements for the polls app): + +.. code-block:: sql + + BEGIN; + CREATE TABLE "polls_poll" ( + "id" serial NOT NULL PRIMARY KEY, + "question" varchar(200) NOT NULL, + "pub_date" timestamp with time zone NOT NULL + ); + CREATE TABLE "polls_choice" ( + "id" serial NOT NULL PRIMARY KEY, + "poll_id" integer NOT NULL REFERENCES "polls_poll" ("id"), + "choice" varchar(200) NOT NULL, + "votes" integer NOT NULL + ); + COMMIT; + +Note the following: + + * The exact output will vary depending on the database you are using. + + * Table names are automatically generated by combining the name of the app + (``polls``) and the lowercase name of the model -- ``poll`` and + ``choice``. (You can override this behavior.) + + * Primary keys (IDs) are added automatically. (You can override this, too.) + + * By convention, Django appends ``"_id"`` to the foreign key field name. + Yes, you can override this, as well. + + * The foreign key relationship is made explicit by a ``REFERENCES`` + statement. + + * It's tailored to the database you're using, so database-specific field + types such as ``auto_increment`` (MySQL), ``serial`` (PostgreSQL), or + ``integer primary key`` (SQLite) are handled for you automatically. Same + goes for quoting of field names -- e.g., using double quotes or single + quotes. The author of this tutorial runs PostgreSQL, so the example + output is in PostgreSQL syntax. + + * The :djadmin:`sql` command doesn't actually run the SQL in your database - + it just prints it to the screen so that you can see what SQL Django thinks + is required. If you wanted to, you could copy and paste this SQL into your + database prompt. However, as we will see shortly, Django provides an + easier way of committing the SQL to the database. + +If you're interested, also run the following commands: + + * :djadmin:`python manage.py validate <validate>` -- Checks for any errors + in the construction of your models. + + * :djadmin:`python manage.py sqlcustom polls <sqlcustom>` -- Outputs any + :ref:`custom SQL statements <initial-sql>` (such as table modifications or + constraints) that are defined for the application. + + * :djadmin:`python manage.py sqlclear polls <sqlclear>` -- Outputs the + necessary ``DROP TABLE`` statements for this app, according to which + tables already exist in your database (if any). + + * :djadmin:`python manage.py sqlindexes polls <sqlindexes>` -- Outputs the + ``CREATE INDEX`` statements for this app. + + * :djadmin:`python manage.py sqlall polls <sqlall>` -- A combination of all + the SQL from the :djadmin:`sql`, :djadmin:`sqlcustom`, and + :djadmin:`sqlindexes` commands. + +Looking at the output of those commands can help you understand what's actually +happening under the hood. + +Now, run :djadmin:`syncdb` again to create those model tables in your database: + +.. code-block:: bash + + python manage.py syncdb + +The :djadmin:`syncdb` command runs the sql from 'sqlall' on your database for +all apps in :setting:`INSTALLED_APPS` that don't already exist in your database. +This creates all the tables, initial data and indexes for any apps you have +added to your project since the last time you ran syncdb. :djadmin:`syncdb` can +be called as often as you like, and it will only ever create the tables that +don't exist. + +Read the :doc:`django-admin.py documentation </ref/django-admin>` for full +information on what the ``manage.py`` utility can do. + +Playing with the API +==================== + +Now, let's hop into the interactive Python shell and play around with the free +API Django gives you. To invoke the Python shell, use this command: + +.. code-block:: bash + + python manage.py shell + +We're using this instead of simply typing "python", because ``manage.py`` sets +up the project's environment for you. "Setting up the environment" involves two +things: + + * Putting ``polls`` on ``sys.path``. For flexibility, several pieces of + Django refer to projects in Python dotted-path notation (e.g. + ``'polls.models'``). In order for this to work, the ``polls`` + package has to be on ``sys.path``. + + We've already seen one example of this: the :setting:`INSTALLED_APPS` + setting is a list of packages in dotted-path notation. + + * Setting the ``DJANGO_SETTINGS_MODULE`` environment variable, which gives + Django the path to your ``settings.py`` file. + +.. admonition:: Bypassing manage.py + + If you'd rather not use ``manage.py``, no problem. Just make sure ``mysite`` + and ``polls`` are at the root level on the Python path (i.e., ``import mysite`` + and ``import polls`` work) and set the ``DJANGO_SETTINGS_MODULE`` environment + variable to ``mysite.settings``. + + For more information on all of this, see the :doc:`django-admin.py + documentation </ref/django-admin>`. + +Once you're in the shell, explore the :doc:`database API </topics/db/queries>`:: + + >>> from polls.models import Poll, Choice # Import the model classes we just wrote. + + # No polls are in the system yet. + >>> Poll.objects.all() + [] + + # Create a new Poll. + >>> import datetime + >>> p = Poll(question="What's up?", pub_date=datetime.datetime.now()) + + # Save the object into the database. You have to call save() explicitly. + >>> p.save() + + # Now it has an ID. Note that this might say "1L" instead of "1", depending + # on which database you're using. That's no biggie; it just means your + # database backend prefers to return integers as Python long integer + # objects. + >>> p.id + 1 + + # Access database columns via Python attributes. + >>> p.question + "What's up?" + >>> p.pub_date + datetime.datetime(2007, 7, 15, 12, 00, 53) + + # Change values by changing the attributes, then calling save(). + >>> p.pub_date = datetime.datetime(2007, 4, 1, 0, 0) + >>> p.save() + + # objects.all() displays all the polls in the database. + >>> Poll.objects.all() + [<Poll: Poll object>] + + +Wait a minute. ``<Poll: Poll object>`` is, utterly, an unhelpful representation +of this object. Let's fix that by editing the polls model (in the +``polls/models.py`` file) and adding a +:meth:`~django.db.models.Model.__unicode__` method to both ``Poll`` and +``Choice``:: + + class Poll(models.Model): + # ... + def __unicode__(self): + return self.question + + class Choice(models.Model): + # ... + def __unicode__(self): + return self.choice + +It's important to add :meth:`~django.db.models.Model.__unicode__` methods to +your models, not only for your own sanity when dealing with the interactive +prompt, but also because objects' representations are used throughout Django's +automatically-generated admin. + +.. admonition:: Why :meth:`~django.db.models.Model.__unicode__` and not + :meth:`~django.db.models.Model.__str__`? + + If you're familiar with Python, you might be in the habit of adding + :meth:`~django.db.models.Model.__str__` methods to your classes, not + :meth:`~django.db.models.Model.__unicode__` methods. We use + :meth:`~django.db.models.Model.__unicode__` here because Django models deal + with Unicode by default. All data stored in your database is converted to + Unicode when it's returned. + + Django models have a default :meth:`~django.db.models.Model.__str__` method + that calls :meth:`~django.db.models.Model.__unicode__` and converts the + result to a UTF-8 bytestring. This means that ``unicode(p)`` will return a + Unicode string, and ``str(p)`` will return a normal string, with characters + encoded as UTF-8. + + If all of this is jibberish to you, just remember to add + :meth:`~django.db.models.Model.__unicode__` methods to your models. With any + luck, things should Just Work for you. + +Note these are normal Python methods. Let's add a custom method, just for +demonstration:: + + import datetime + # ... + class Poll(models.Model): + # ... + def was_published_today(self): + return self.pub_date.date() == datetime.date.today() + +Note the addition of ``import datetime`` to reference Python's standard +``datetime`` module. + +Save these changes and start a new Python interactive shell by running +``python manage.py shell`` again:: + + >>> from polls.models import Poll, Choice + + # Make sure our __unicode__() addition worked. + >>> Poll.objects.all() + [<Poll: What's up?>] + + # Django provides a rich database lookup API that's entirely driven by + # keyword arguments. + >>> Poll.objects.filter(id=1) + [<Poll: What's up?>] + >>> Poll.objects.filter(question__startswith='What') + [<Poll: What's up?>] + + # Get the poll whose year is 2007. + >>> Poll.objects.get(pub_date__year=2007) + <Poll: What's up?> + + >>> Poll.objects.get(id=2) + Traceback (most recent call last): + ... + DoesNotExist: Poll matching query does not exist. + + # Lookup by a primary key is the most common case, so Django provides a + # shortcut for primary-key exact lookups. + # The following is identical to Poll.objects.get(id=1). + >>> Poll.objects.get(pk=1) + <Poll: What's up?> + + # Make sure our custom method worked. + >>> p = Poll.objects.get(pk=1) + >>> p.was_published_today() + False + + # Give the Poll a couple of Choices. The create call constructs a new + # choice object, does the INSERT statement, adds the choice to the set + # of available choices and returns the new Choice object. Django creates + # a set to hold the "other side" of a ForeignKey relation + # (e.g. a poll's choices) which can be accessed via the API. + >>> p = Poll.objects.get(pk=1) + + # Display any choices from the related object set -- none so far. + >>> p.choice_set.all() + [] + + # Create three choices. + >>> p.choice_set.create(choice='Not much', votes=0) + <Choice: Not much> + >>> p.choice_set.create(choice='The sky', votes=0) + <Choice: The sky> + >>> c = p.choice_set.create(choice='Just hacking again', votes=0) + + # Choice objects have API access to their related Poll objects. + >>> c.poll + <Poll: What's up?> + + # And vice versa: Poll objects get access to Choice objects. + >>> p.choice_set.all() + [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>] + >>> p.choice_set.count() + 3 + + # The API automatically follows relationships as far as you need. + # Use double underscores to separate relationships. + # This works as many levels deep as you want; there's no limit. + # Find all Choices for any poll whose pub_date is in 2007. + >>> Choice.objects.filter(poll__pub_date__year=2007) + [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>] + + # Let's delete one of the choices. Use delete() for that. + >>> c = p.choice_set.filter(choice__startswith='Just hacking') + >>> c.delete() + +For more information on model relations, see :doc:`Accessing related objects +</ref/models/relations>`. For full details on the database API, see our +:doc:`Database API reference </topics/db/queries>`. + +When you're comfortable with the API, read :doc:`part 2 of this tutorial +</intro/tutorial02>` to get Django's automatic admin working. diff --git a/parts/django/docs/intro/tutorial02.txt b/parts/django/docs/intro/tutorial02.txt new file mode 100644 index 0000000..c80d87d --- /dev/null +++ b/parts/django/docs/intro/tutorial02.txt @@ -0,0 +1,465 @@ +===================================== +Writing your first Django app, part 2 +===================================== + +This tutorial begins where :doc:`Tutorial 1 </intro/tutorial01>` left off. We're +continuing the Web-poll application and will focus on Django's +automatically-generated admin site. + +.. admonition:: Philosophy + + Generating admin sites for your staff or clients to add, change and delete + content is tedious work that doesn't require much creativity. For that + reason, Django entirely automates creation of admin interfaces for models. + + Django was written in a newsroom environment, with a very clear separation + between "content publishers" and the "public" site. Site managers use the + system to add news stories, events, sports scores, etc., and that content is + displayed on the public site. Django solves the problem of creating a + unified interface for site administrators to edit content. + + The admin isn't necessarily intended to be used by site visitors; it's for + site managers. + +Activate the admin site +======================= + +The Django admin site is not activated by default -- it's an opt-in thing. To +activate the admin site for your installation, do these three things: + + * Add ``"django.contrib.admin"`` to your :setting:`INSTALLED_APPS` setting. + + * Run ``python manage.py syncdb``. Since you have added a new application + to :setting:`INSTALLED_APPS`, the database tables need to be updated. + + * Edit your ``mysite/urls.py`` file and uncomment the lines that reference + the admin -- there are three lines in total to uncomment. This file is a + URLconf; we'll dig into URLconfs in the next tutorial. For now, all you + need to know is that it maps URL roots to applications. In the end, you + should have a ``urls.py`` file that looks like this: + + .. versionchanged:: 1.1 + The method for adding admin urls has changed in Django 1.1. + + .. parsed-literal:: + + from django.conf.urls.defaults import * + + # Uncomment the next two lines to enable the admin: + **from django.contrib import admin** + **admin.autodiscover()** + + urlpatterns = patterns('', + # Example: + # (r'^mysite/', include('mysite.foo.urls')), + + # Uncomment the admin/doc line below and add 'django.contrib.admindocs' + # to INSTALLED_APPS to enable admin documentation: + # (r'^admin/doc/', include('django.contrib.admindocs.urls')), + + # Uncomment the next line to enable the admin: + **(r'^admin/', include(admin.site.urls)),** + ) + + (The bold lines are the ones that needed to be uncommented.) + +Start the development server +============================ + +Let's start the development server and explore the admin site. + +Recall from Tutorial 1 that you start the development server like so: + +.. code-block:: bash + + python manage.py runserver + +Now, open a Web browser and go to "/admin/" on your local domain -- e.g., +http://127.0.0.1:8000/admin/. You should see the admin's login screen: + +.. image:: _images/admin01.png + :alt: Django admin login screen + +Enter the admin site +==================== + +Now, try logging in. (You created a superuser account in the first part of this +tutorial, remember? If you didn't create one or forgot the password you can +:ref:`create another one <topics-auth-creating-superusers>`.) You should see +the Django admin index page: + +.. image:: _images/admin02t.png + :alt: Django admin index page + +You should see a few other types of editable content, including groups, users +and sites. These are core features Django ships with by default. + +Make the poll app modifiable in the admin +========================================= + +But where's our poll app? It's not displayed on the admin index page. + +Just one thing to do: We need to tell the admin that ``Poll`` +objects have an admin interface. To do this, create a file called +``admin.py`` in your ``polls`` directory, and edit it to look like this:: + + from polls.models import Poll + from django.contrib import admin + + admin.site.register(Poll) + +You'll need to restart the development server to see your changes. Normally, +the server auto-reloads code every time you modify a file, but the action of +creating a new file doesn't trigger the auto-reloading logic. + +Explore the free admin functionality +==================================== + +Now that we've registered ``Poll``, Django knows that it should be displayed on +the admin index page: + +.. image:: _images/admin03t.png + :alt: Django admin index page, now with polls displayed + +Click "Polls." Now you're at the "change list" page for polls. This page +displays all the polls in the database and lets you choose one to change it. +There's the "What's up?" poll we created in the first tutorial: + +.. image:: _images/admin04t.png + :alt: Polls change list page + +Click the "What's up?" poll to edit it: + +.. image:: _images/admin05t.png + :alt: Editing form for poll object + +Things to note here: + + * The form is automatically generated from the Poll model. + + * The different model field types (:class:`~django.db.models.DateTimeField`, + :class:`~django.db.models.CharField`) correspond to the appropriate HTML + input widget. Each type of field knows how to display itself in the Django + admin. + + * Each :class:`~django.db.models.DateTimeField` gets free JavaScript + shortcuts. Dates get a "Today" shortcut and calendar popup, and times get + a "Now" shortcut and a convenient popup that lists commonly entered times. + +The bottom part of the page gives you a couple of options: + + * Save -- Saves changes and returns to the change-list page for this type of + object. + + * Save and continue editing -- Saves changes and reloads the admin page for + this object. + + * Save and add another -- Saves changes and loads a new, blank form for this + type of object. + + * Delete -- Displays a delete confirmation page. + +Change the "Date published" by clicking the "Today" and "Now" shortcuts. Then +click "Save and continue editing." Then click "History" in the upper right. +You'll see a page listing all changes made to this object via the Django admin, +with the timestamp and username of the person who made the change: + +.. image:: _images/admin06t.png + :alt: History page for poll object + +Customize the admin form +======================== + +Take a few minutes to marvel at all the code you didn't have to write. By +registering the Poll model with ``admin.site.register(Poll)``, Django was able +to construct a default form representation. Often, you'll want to customize how +the admin form looks and works. You'll do this by telling Django the options +you want when you register the object. + +Let's see how this works by re-ordering the fields on the edit form. Replace +the ``admin.site.register(Poll)`` line with:: + + class PollAdmin(admin.ModelAdmin): + fields = ['pub_date', 'question'] + + admin.site.register(Poll, PollAdmin) + +You'll follow this pattern -- create a model admin object, then pass it as the +second argument to ``admin.site.register()`` -- any time you need to change the +admin options for an object. + +This particular change above makes the "Publication date" come before the +"Question" field: + +.. image:: _images/admin07.png + :alt: Fields have been reordered + +This isn't impressive with only two fields, but for admin forms with dozens +of fields, choosing an intuitive order is an important usability detail. + +And speaking of forms with dozens of fields, you might want to split the form +up into fieldsets:: + + class PollAdmin(admin.ModelAdmin): + fieldsets = [ + (None, {'fields': ['question']}), + ('Date information', {'fields': ['pub_date']}), + ] + + admin.site.register(Poll, PollAdmin) + +The first element of each tuple in ``fieldsets`` is the title of the fieldset. +Here's what our form looks like now: + +.. image:: _images/admin08t.png + :alt: Form has fieldsets now + +You can assign arbitrary HTML classes to each fieldset. Django provides a +``"collapse"`` class that displays a particular fieldset initially collapsed. +This is useful when you have a long form that contains a number of fields that +aren't commonly used:: + + class PollAdmin(admin.ModelAdmin): + fieldsets = [ + (None, {'fields': ['question']}), + ('Date information', {'fields': ['pub_date'], 'classes': ['collapse']}), + ] + +.. image:: _images/admin09.png + :alt: Fieldset is initially collapsed + +Adding related objects +====================== + +OK, we have our Poll admin page. But a ``Poll`` has multiple ``Choices``, and +the admin page doesn't display choices. + +Yet. + +There are two ways to solve this problem. The first is to register ``Choice`` +with the admin just as we did with ``Poll``. That's easy:: + + from polls.models import Choice + + admin.site.register(Choice) + +Now "Choices" is an available option in the Django admin. The "Add choice" form +looks like this: + +.. image:: _images/admin10.png + :alt: Choice admin page + +In that form, the "Poll" field is a select box containing every poll in the +database. Django knows that a :class:`~django.db.models.ForeignKey` should be +represented in the admin as a ``<select>`` box. In our case, only one poll +exists at this point. + +Also note the "Add Another" link next to "Poll." Every object with a +``ForeignKey`` relationship to another gets this for free. When you click "Add +Another," you'll get a popup window with the "Add poll" form. If you add a poll +in that window and click "Save," Django will save the poll to the database and +dynamically add it as the selected choice on the "Add choice" form you're +looking at. + +But, really, this is an inefficient way of adding Choice objects to the system. +It'd be better if you could add a bunch of Choices directly when you create the +Poll object. Let's make that happen. + +Remove the ``register()`` call for the Choice model. Then, edit the ``Poll`` +registration code to read:: + + class ChoiceInline(admin.StackedInline): + model = Choice + extra = 3 + + class PollAdmin(admin.ModelAdmin): + fieldsets = [ + (None, {'fields': ['question']}), + ('Date information', {'fields': ['pub_date'], 'classes': ['collapse']}), + ] + inlines = [ChoiceInline] + + admin.site.register(Poll, PollAdmin) + +This tells Django: "Choice objects are edited on the Poll admin page. By +default, provide enough fields for 3 choices." + +Load the "Add poll" page to see how that looks, you may need to restart your development server: + +.. image:: _images/admin11t.png + :alt: Add poll page now has choices on it + +It works like this: There are three slots for related Choices -- as specified +by ``extra`` -- and each time you come back to the "Change" page for an +already-created object, you get another three extra slots. + +One small problem, though. It takes a lot of screen space to display all the +fields for entering related Choice objects. For that reason, Django offers a +tabular way of displaying inline related objects; you just need to change +the ``ChoiceInline`` declaration to read:: + + class ChoiceInline(admin.TabularInline): + #... + +With that ``TabularInline`` (instead of ``StackedInline``), the +related objects are displayed in a more compact, table-based format: + +.. image:: _images/admin12.png + :alt: Add poll page now has more compact choices + +Customize the admin change list +=============================== + +Now that the Poll admin page is looking good, let's make some tweaks to the +"change list" page -- the one that displays all the polls in the system. + +Here's what it looks like at this point: + +.. image:: _images/admin04t.png + :alt: Polls change list page + +By default, Django displays the ``str()`` of each object. But sometimes it'd be +more helpful if we could display individual fields. To do that, use the +``list_display`` admin option, which is a tuple of field names to display, as +columns, on the change list page for the object:: + + class PollAdmin(admin.ModelAdmin): + # ... + list_display = ('question', 'pub_date') + +Just for good measure, let's also include the ``was_published_today`` custom +method from Tutorial 1:: + + class PollAdmin(admin.ModelAdmin): + # ... + list_display = ('question', 'pub_date', 'was_published_today') + +Now the poll change list page looks like this: + +.. image:: _images/admin13t.png + :alt: Polls change list page, updated + +You can click on the column headers to sort by those values -- except in the +case of the ``was_published_today`` header, because sorting by the output of +an arbitrary method is not supported. Also note that the column header for +``was_published_today`` is, by default, the name of the method (with +underscores replaced with spaces). But you can change that by giving that +method (in ``models.py``) a ``short_description`` attribute:: + + def was_published_today(self): + return self.pub_date.date() == datetime.date.today() + was_published_today.short_description = 'Published today?' + +Edit your admin.py file again and add an improvement to the Poll change list page: Filters. Add the +following line to ``PollAdmin``:: + + list_filter = ['pub_date'] + +That adds a "Filter" sidebar that lets people filter the change list by the +``pub_date`` field: + +.. image:: _images/admin14t.png + :alt: Polls change list page, updated + +The type of filter displayed depends on the type of field you're filtering on. +Because ``pub_date`` is a DateTimeField, Django knows to give the default +filter options for DateTimeFields: "Any date," "Today," "Past 7 days," +"This month," "This year." + +This is shaping up well. Let's add some search capability:: + + search_fields = ['question'] + +That adds a search box at the top of the change list. When somebody enters +search terms, Django will search the ``question`` field. You can use as many +fields as you'd like -- although because it uses a ``LIKE`` query behind the +scenes, keep it reasonable, to keep your database happy. + +Finally, because Poll objects have dates, it'd be convenient to be able to +drill down by date. Add this line:: + + date_hierarchy = 'pub_date' + +That adds hierarchical navigation, by date, to the top of the change list page. +At top level, it displays all available years. Then it drills down to months +and, ultimately, days. + +Now's also a good time to note that change lists give you free pagination. The +default is to display 50 items per page. Change-list pagination, search boxes, +filters, date-hierarchies and column-header-ordering all work together like you +think they should. + +Customize the admin look and feel +================================= + +Clearly, having "Django administration" at the top of each admin page is +ridiculous. It's just placeholder text. + +That's easy to change, though, using Django's template system. The Django admin +is powered by Django itself, and its interfaces use Django's own template +system. + +Open your settings file (``mysite/settings.py``, remember) and look at the +:setting:`TEMPLATE_DIRS` setting. :setting:`TEMPLATE_DIRS` is a tuple of +filesystem directories to check when loading Django templates. It's a search +path. + +By default, :setting:`TEMPLATE_DIRS` is empty. So, let's add a line to it, to +tell Django where our templates live:: + + TEMPLATE_DIRS = ( + "/home/my_username/mytemplates", # Change this to your own directory. + ) + +Now copy the template ``admin/base_site.html`` from within the default Django +admin template directory in the source code of Django itself +(``django/contrib/admin/templates``) into an ``admin`` subdirectory of +whichever directory you're using in :setting:`TEMPLATE_DIRS`. For example, if +your :setting:`TEMPLATE_DIRS` includes ``"/home/my_username/mytemplates"``, as +above, then copy ``django/contrib/admin/templates/admin/base_site.html`` to +``/home/my_username/mytemplates/admin/base_site.html``. Don't forget that +``admin`` subdirectory. + +Then, just edit the file and replace the generic Django text with your own +site's name as you see fit. + +This template file contains lots of text like ``{% block branding %}`` +and ``{{ title }}``. The ``{%`` and ``{{`` tags are part of Django's +template language. When Django renders ``admin/base_site.html``, this +template language will be evaluated to produce the final HTML page. +Don't worry if you can't make any sense of the template right now -- +we'll delve into Django's templating language in Tutorial 3. + +Note that any of Django's default admin templates can be overridden. To +override a template, just do the same thing you did with ``base_site.html`` -- +copy it from the default directory into your custom directory, and make +changes. + +Astute readers will ask: But if :setting:`TEMPLATE_DIRS` was empty by default, +how was Django finding the default admin templates? The answer is that, by +default, Django automatically looks for a ``templates/`` subdirectory within +each app package, for use as a fallback. See the :ref:`template loader +documentation <template-loaders>` for full information. + +Customize the admin index page +============================== + +On a similar note, you might want to customize the look and feel of the Django +admin index page. + +By default, it displays all the apps in :setting:`INSTALLED_APPS` that have been +registered with the admin application, in alphabetical order. You may want to +make significant changes to the layout. After all, the index is probably the +most important page of the admin, and it should be easy to use. + +The template to customize is ``admin/index.html``. (Do the same as with +``admin/base_site.html`` in the previous section -- copy it from the default +directory to your custom template directory.) Edit the file, and you'll see it +uses a template variable called ``app_list``. That variable contains every +installed Django app. Instead of using that, you can hard-code links to +object-specific admin pages in whatever way you think is best. Again, +don't worry if you can't understand the template language -- we'll cover that +in more detail in Tutorial 3. + +When you're comfortable with the admin site, read :doc:`part 3 of this tutorial +</intro/tutorial03>` to start working on public poll views. diff --git a/parts/django/docs/intro/tutorial03.txt b/parts/django/docs/intro/tutorial03.txt new file mode 100644 index 0000000..0843d9e --- /dev/null +++ b/parts/django/docs/intro/tutorial03.txt @@ -0,0 +1,546 @@ +===================================== +Writing your first Django app, part 3 +===================================== + +This tutorial begins where :doc:`Tutorial 2 </intro/tutorial02>` left off. We're +continuing the Web-poll application and will focus on creating the public +interface -- "views." + +Philosophy +========== + +A view is a "type" of Web page in your Django application that generally serves +a specific function and has a specific template. For example, in a Weblog +application, you might have the following views: + + * Blog homepage -- displays the latest few entries. + + * Entry "detail" page -- permalink page for a single entry. + + * Year-based archive page -- displays all months with entries in the + given year. + + * Month-based archive page -- displays all days with entries in the + given month. + + * Day-based archive page -- displays all entries in the given day. + + * Comment action -- handles posting comments to a given entry. + +In our poll application, we'll have the following four views: + + * Poll "archive" page -- displays the latest few polls. + + * Poll "detail" page -- displays a poll question, with no results but + with a form to vote. + + * Poll "results" page -- displays results for a particular poll. + + * Vote action -- handles voting for a particular choice in a particular + poll. + +In Django, each view is represented by a simple Python function. + +Design your URLs +================ + +The first step of writing views is to design your URL structure. You do this by +creating a Python module, called a URLconf. URLconfs are how Django associates +a given URL with given Python code. + +When a user requests a Django-powered page, the system looks at the +:setting:`ROOT_URLCONF` setting, which contains a string in Python dotted +syntax. Django loads that module and looks for a module-level variable called +``urlpatterns``, which is a sequence of tuples in the following format:: + + (regular expression, Python callback function [, optional dictionary]) + +Django starts at the first regular expression and makes its way down the list, +comparing the requested URL against each regular expression until it finds one +that matches. + +When it finds a match, Django calls the Python callback function, with an +:class:`~django.http.HttpRequest` object as the first argument, any "captured" +values from the regular expression as keyword arguments, and, optionally, +arbitrary keyword arguments from the dictionary (an optional third item in the +tuple). + +For more on :class:`~django.http.HttpRequest` objects, see the +:doc:`/ref/request-response`. For more details on URLconfs, see the +:doc:`/topics/http/urls`. + +When you ran ``django-admin.py startproject mysite`` at the beginning of +Tutorial 1, it created a default URLconf in ``mysite/urls.py``. It also +automatically set your :setting:`ROOT_URLCONF` setting (in ``settings.py``) to +point at that file:: + + ROOT_URLCONF = 'mysite.urls' + +Time for an example. Edit ``mysite/urls.py`` so it looks like this:: + + from django.conf.urls.defaults import * + + from django.contrib import admin + admin.autodiscover() + + urlpatterns = patterns('', + (r'^polls/$', 'polls.views.index'), + (r'^polls/(?P<poll_id>\d+)/$', 'polls.views.detail'), + (r'^polls/(?P<poll_id>\d+)/results/$', 'polls.views.results'), + (r'^polls/(?P<poll_id>\d+)/vote/$', 'polls.views.vote'), + (r'^admin/', include(admin.site.urls)), + ) + +This is worth a review. When somebody requests a page from your Web site -- say, +"/polls/23/", Django will load this Python module, because it's pointed to by +the :setting:`ROOT_URLCONF` setting. It finds the variable named ``urlpatterns`` +and traverses the regular expressions in order. When it finds a regular +expression that matches -- ``r'^polls/(?P<poll_id>\d+)/$'`` -- it loads the +function ``detail()`` from ``polls/views.py``. Finally, it calls that +``detail()`` function like so:: + + detail(request=<HttpRequest object>, poll_id='23') + +The ``poll_id='23'`` part comes from ``(?P<poll_id>\d+)``. Using parentheses +around a pattern "captures" the text matched by that pattern and sends it as an +argument to the view function; the ``?P<poll_id>`` defines the name that will be +used to identify the matched pattern; and ``\d+`` is a regular expression to +match a sequence of digits (i.e., a number). + +Because the URL patterns are regular expressions, there really is no limit on +what you can do with them. And there's no need to add URL cruft such as ``.php`` +-- unless you have a sick sense of humor, in which case you can do something +like this:: + + (r'^polls/latest\.php$', 'polls.views.index'), + +But, don't do that. It's silly. + +Note that these regular expressions do not search GET and POST parameters, or +the domain name. For example, in a request to ``http://www.example.com/myapp/``, +the URLconf will look for ``myapp/``. In a request to +``http://www.example.com/myapp/?page=3``, the URLconf will look for ``myapp/``. + +If you need help with regular expressions, see `Wikipedia's entry`_ and the +`Python documentation`_. Also, the O'Reilly book "Mastering Regular Expressions" +by Jeffrey Friedl is fantastic. + +Finally, a performance note: these regular expressions are compiled the first +time the URLconf module is loaded. They're super fast. + +.. _Wikipedia's entry: http://en.wikipedia.org/wiki/Regular_expression +.. _Python documentation: http://docs.python.org/library/re.html + +Write your first view +===================== + +Well, we haven't created any views yet -- we just have the URLconf. But let's +make sure Django is following the URLconf properly. + +Fire up the Django development Web server: + +.. code-block:: bash + + python manage.py runserver + +Now go to "http://localhost:8000/polls/" on your domain in your Web browser. +You should get a pleasantly-colored error page with the following message:: + + ViewDoesNotExist at /polls/ + + Tried index in module polls.views. Error was: 'module' + object has no attribute 'index' + +This error happened because you haven't written a function ``index()`` in the +module ``polls/views.py``. + +Try "/polls/23/", "/polls/23/results/" and "/polls/23/vote/". The error +messages tell you which view Django tried (and failed to find, because you +haven't written any views yet). + +Time to write the first view. Open the file ``polls/views.py`` +and put the following Python code in it:: + + from django.http import HttpResponse + + def index(request): + return HttpResponse("Hello, world. You're at the poll index.") + +This is the simplest view possible. Go to "/polls/" in your browser, and you +should see your text. + +Now lets add a few more views. These views are slightly different, because +they take an argument (which, remember, is passed in from whatever was +captured by the regular expression in the URLconf):: + + def detail(request, poll_id): + return HttpResponse("You're looking at poll %s." % poll_id) + + def results(request, poll_id): + return HttpResponse("You're looking at the results of poll %s." % poll_id) + + def vote(request, poll_id): + return HttpResponse("You're voting on poll %s." % poll_id) + +Take a look in your browser, at "/polls/34/". It'll run the `detail()` method +and display whatever ID you provide in the URL. Try "/polls/34/results/" and +"/polls/34/vote/" too -- these will display the placeholder results and voting +pages. + +Write views that actually do something +====================================== + +Each view is responsible for doing one of two things: Returning an +:class:`~django.http.HttpResponse` object containing the content for the +requested page, or raising an exception such as :exc:`~django.http.Http404`. The +rest is up to you. + +Your view can read records from a database, or not. It can use a template +system such as Django's -- or a third-party Python template system -- or not. +It can generate a PDF file, output XML, create a ZIP file on the fly, anything +you want, using whatever Python libraries you want. + +All Django wants is that :class:`~django.http.HttpResponse`. Or an exception. + +Because it's convenient, let's use Django's own database API, which we covered +in :doc:`Tutorial 1 </intro/tutorial01>`. Here's one stab at the ``index()`` +view, which displays the latest 5 poll questions in the system, separated by +commas, according to publication date:: + + from polls.models import Poll + from django.http import HttpResponse + + def index(request): + latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5] + output = ', '.join([p.question for p in latest_poll_list]) + return HttpResponse(output) + +There's a problem here, though: The page's design is hard-coded in the view. If +you want to change the way the page looks, you'll have to edit this Python code. +So let's use Django's template system to separate the design from Python:: + + from django.template import Context, loader + from polls.models import Poll + from django.http import HttpResponse + + def index(request): + latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5] + t = loader.get_template('polls/index.html') + c = Context({ + 'latest_poll_list': latest_poll_list, + }) + return HttpResponse(t.render(c)) + +That code loads the template called "polls/index.html" and passes it a context. +The context is a dictionary mapping template variable names to Python objects. + +Reload the page. Now you'll see an error:: + + TemplateDoesNotExist at /polls/ + polls/index.html + +Ah. There's no template yet. First, create a directory, somewhere on your +filesystem, whose contents Django can access. (Django runs as whatever user your +server runs.) Don't put them under your document root, though. You probably +shouldn't make them public, just for security's sake. +Then edit :setting:`TEMPLATE_DIRS` in your ``settings.py`` to tell Django where +it can find templates -- just as you did in the "Customize the admin look and +feel" section of Tutorial 2. + +When you've done that, create a directory ``polls`` in your template directory. +Within that, create a file called ``index.html``. Note that our +``loader.get_template('polls/index.html')`` code from above maps to +"[template_directory]/polls/index.html" on the filesystem. + +Put the following code in that template: + +.. code-block:: html+django + + {% if latest_poll_list %} + <ul> + {% for poll in latest_poll_list %} + <li><a href="/polls/{{ poll.id }}/">{{ poll.question }}</a></li> + {% endfor %} + </ul> + {% else %} + <p>No polls are available.</p> + {% endif %} + +Load the page in your Web browser, and you should see a bulleted-list +containing the "What's up" poll from Tutorial 1. The link points to the poll's +detail page. + +A shortcut: render_to_response() +-------------------------------- + +It's a very common idiom to load a template, fill a context and return an +:class:`~django.http.HttpResponse` object with the result of the rendered +template. Django provides a shortcut. Here's the full ``index()`` view, +rewritten:: + + from django.shortcuts import render_to_response + from polls.models import Poll + + def index(request): + latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5] + return render_to_response('polls/index.html', {'latest_poll_list': latest_poll_list}) + +Note that once we've done this in all these views, we no longer need to import +:mod:`~django.template.loader`, :class:`~django.template.Context` and +:class:`~django.http.HttpResponse`. + +The :func:`~django.shortcuts.render_to_response` function takes a template name +as its first argument and a dictionary as its optional second argument. It +returns an :class:`~django.http.HttpResponse` object of the given template +rendered with the given context. + +Raising 404 +=========== + +Now, let's tackle the poll detail view -- the page that displays the question +for a given poll. Here's the view:: + + from django.http import Http404 + # ... + def detail(request, poll_id): + try: + p = Poll.objects.get(pk=poll_id) + except Poll.DoesNotExist: + raise Http404 + return render_to_response('polls/detail.html', {'poll': p}) + +The new concept here: The view raises the :exc:`~django.http.Http404` exception +if a poll with the requested ID doesn't exist. + +We'll discuss what you could put in that ``polls/detail.html`` template a bit +later, but if you'd like to quickly get the above example working, just:: + + {{ poll }} + +will get you started for now. + +A shortcut: get_object_or_404() +------------------------------- + +It's a very common idiom to use :meth:`~django.db.models.QuerySet.get` and raise +:exc:`~django.http.Http404` if the object doesn't exist. Django provides a +shortcut. Here's the ``detail()`` view, rewritten:: + + from django.shortcuts import render_to_response, get_object_or_404 + # ... + def detail(request, poll_id): + p = get_object_or_404(Poll, pk=poll_id) + return render_to_response('polls/detail.html', {'poll': p}) + +The :func:`~django.shortcuts.get_object_or_404` function takes a Django model +as its first argument and an arbitrary number of keyword arguments, which it +passes to the module's :meth:`~django.db.models.QuerySet.get` function. It +raises :exc:`~django.http.Http404` if the object doesn't exist. + +.. admonition:: Philosophy + + Why do we use a helper function :func:`~django.shortcuts.get_object_or_404` + instead of automatically catching the + :exc:`~django.core.exceptions.ObjectDoesNotExist` exceptions at a higher + level, or having the model API raise :exc:`~django.http.Http404` instead of + :exc:`~django.core.exceptions.ObjectDoesNotExist`? + + Because that would couple the model layer to the view layer. One of the + foremost design goals of Django is to maintain loose coupling. + +There's also a :func:`~django.shortcuts.get_list_or_404` function, which works +just as :func:`~django.shortcuts.get_object_or_404` -- except using +:meth:`~django.db.models.QuerySet.filter` instead of +:meth:`~django.db.models.QuerySet.get`. It raises :exc:`~django.http.Http404` if +the list is empty. + +Write a 404 (page not found) view +================================= + +When you raise :exc:`~django.http.Http404` from within a view, Django will load +a special view devoted to handling 404 errors. It finds it by looking for the +variable ``handler404``, which is a string in Python dotted syntax -- the same +format the normal URLconf callbacks use. A 404 view itself has nothing special: +It's just a normal view. + +You normally won't have to bother with writing 404 views. By default, URLconfs +have the following line up top:: + + from django.conf.urls.defaults import * + +That takes care of setting ``handler404`` in the current module. As you can see +in ``django/conf/urls/defaults.py``, ``handler404`` is set to +:func:`django.views.defaults.page_not_found` by default. + +Four more things to note about 404 views: + + * If :setting:`DEBUG` is set to ``True`` (in your settings module) then your + 404 view will never be used (and thus the ``404.html`` template will never + be rendered) because the traceback will be displayed instead. + + * The 404 view is also called if Django doesn't find a match after checking + every regular expression in the URLconf. + + * If you don't define your own 404 view -- and simply use the default, which + is recommended -- you still have one obligation: To create a ``404.html`` + template in the root of your template directory. The default 404 view will + use that template for all 404 errors. + + * If :setting:`DEBUG` is set to ``False`` (in your settings module) and if + you didn't create a ``404.html`` file, an ``Http500`` is raised instead. + So remember to create a ``404.html``. + +Write a 500 (server error) view +=============================== + +Similarly, URLconfs may define a ``handler500``, which points to a view to call +in case of server errors. Server errors happen when you have runtime errors in +view code. + +Use the template system +======================= + +Back to the ``detail()`` view for our poll application. Given the context +variable ``poll``, here's what the "polls/detail.html" template might look +like: + +.. code-block:: html+django + + <h1>{{ poll.question }}</h1> + <ul> + {% for choice in poll.choice_set.all %} + <li>{{ choice.choice }}</li> + {% endfor %} + </ul> + +The template system uses dot-lookup syntax to access variable attributes. In +the example of ``{{ poll.question }}``, first Django does a dictionary lookup +on the object ``poll``. Failing that, it tries attribute lookup -- which works, +in this case. If attribute lookup had failed, it would've tried calling the +method ``question()`` on the poll object. + +Method-calling happens in the ``{% for %}`` loop: ``poll.choice_set.all`` is +interpreted as the Python code ``poll.choice_set.all()``, which returns an +iterable of Choice objects and is suitable for use in the ``{% for %}`` tag. + +See the :doc:`template guide </topics/templates>` for more about templates. + +Simplifying the URLconfs +======================== + +Take some time to play around with the views and template system. As you edit +the URLconf, you may notice there's a fair bit of redundancy in it:: + + urlpatterns = patterns('', + (r'^polls/$', 'polls.views.index'), + (r'^polls/(?P<poll_id>\d+)/$', 'polls.views.detail'), + (r'^polls/(?P<poll_id>\d+)/results/$', 'polls.views.results'), + (r'^polls/(?P<poll_id>\d+)/vote/$', 'polls.views.vote'), + ) + +Namely, ``polls.views`` is in every callback. + +Because this is a common case, the URLconf framework provides a shortcut for +common prefixes. You can factor out the common prefixes and add them as the +first argument to :func:`~django.conf.urls.defaults.patterns`, like so:: + + urlpatterns = patterns('polls.views', + (r'^polls/$', 'index'), + (r'^polls/(?P<poll_id>\d+)/$', 'detail'), + (r'^polls/(?P<poll_id>\d+)/results/$', 'results'), + (r'^polls/(?P<poll_id>\d+)/vote/$', 'vote'), + ) + +This is functionally identical to the previous formatting. It's just a bit +tidier. + +Since you generally don't want the prefix for one app to be applied to every +callback in your URLconf, you can concatenate multiple +:func:`~django.conf.urls.defaults.patterns`. Your full ``mysite/urls.py`` might +now look like this:: + + from django.conf.urls.defaults import * + + from django.contrib import admin + admin.autodiscover() + + urlpatterns = patterns('polls.views', + (r'^polls/$', 'index'), + (r'^polls/(?P<poll_id>\d+)/$', 'detail'), + (r'^polls/(?P<poll_id>\d+)/results/$', 'results'), + (r'^polls/(?P<poll_id>\d+)/vote/$', 'vote'), + ) + + urlpatterns += patterns('', + (r'^admin/', include(admin.site.urls)), + ) + +Decoupling the URLconfs +======================= + +While we're at it, we should take the time to decouple our poll-app URLs from +our Django project configuration. Django apps are meant to be pluggable -- that +is, each particular app should be transferable to another Django installation +with minimal fuss. + +Our poll app is pretty decoupled at this point, thanks to the strict directory +structure that ``python manage.py startapp`` created, but one part of it is +coupled to the Django settings: The URLconf. + +We've been editing the URLs in ``mysite/urls.py``, but the URL design of an +app is specific to the app, not to the Django installation -- so let's move the +URLs within the app directory. + +Copy the file ``mysite/urls.py`` to ``polls/urls.py``. Then, change +``mysite/urls.py`` to remove the poll-specific URLs and insert an +:func:`~django.conf.urls.defaults.include`, leaving you with:: + + # This also imports the include function + from django.conf.urls.defaults import * + + from django.contrib import admin + admin.autodiscover() + + urlpatterns = patterns('', + (r'^polls/', include('polls.urls')), + (r'^admin/', include(admin.site.urls)), + ) + +:func:`~django.conf.urls.defaults.include` simply references another URLconf. +Note that the regular expression doesn't have a ``$`` (end-of-string match +character) but has the trailing slash. Whenever Django encounters +:func:`~django.conf.urls.defaults.include`, it chops off whatever part of the +URL matched up to that point and sends the remaining string to the included +URLconf for further processing. + +Here's what happens if a user goes to "/polls/34/" in this system: + + * Django will find the match at ``'^polls/'`` + + * Then, Django will strip off the matching text (``"polls/"``) and send the + remaining text -- ``"34/"`` -- to the 'polls.urls' URLconf for + further processing. + +Now that we've decoupled that, we need to decouple the ``polls.urls`` +URLconf by removing the leading "polls/" from each line, and removing the +lines registering the admin site. Your ``polls.urls`` file should now look like +this:: + + from django.conf.urls.defaults import * + + urlpatterns = patterns('polls.views', + (r'^$', 'index'), + (r'^(?P<poll_id>\d+)/$', 'detail'), + (r'^(?P<poll_id>\d+)/results/$', 'results'), + (r'^(?P<poll_id>\d+)/vote/$', 'vote'), + ) + +The idea behind :func:`~django.conf.urls.defaults.include` and URLconf +decoupling is to make it easy to plug-and-play URLs. Now that polls are in their +own URLconf, they can be placed under "/polls/", or under "/fun_polls/", or +under "/content/polls/", or any other path root, and the app will still work. + +All the poll app cares about is its relative path, not its absolute path. + +When you're comfortable with writing views, read :doc:`part 4 of this tutorial +</intro/tutorial04>` to learn about simple form processing and generic views. diff --git a/parts/django/docs/intro/tutorial04.txt b/parts/django/docs/intro/tutorial04.txt new file mode 100644 index 0000000..dfbd82d --- /dev/null +++ b/parts/django/docs/intro/tutorial04.txt @@ -0,0 +1,346 @@ +===================================== +Writing your first Django app, part 4 +===================================== + +This tutorial begins where :doc:`Tutorial 3 </intro/tutorial03>` left off. We're +continuing the Web-poll application and will focus on simple form processing and +cutting down our code. + +Write a simple form +=================== + +Let's update our poll detail template ("polls/detail.html") from the last +tutorial, so that the template contains an HTML ``<form>`` element: + +.. code-block:: html+django + + <h1>{{ poll.question }}</h1> + + {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %} + + <form action="/polls/{{ poll.id }}/vote/" method="post"> + {% csrf_token %} + {% for choice in poll.choice_set.all %} + <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" /> + <label for="choice{{ forloop.counter }}">{{ choice.choice }}</label><br /> + {% endfor %} + <input type="submit" value="Vote" /> + </form> + +A quick rundown: + + * The above template displays a radio button for each poll choice. The + ``value`` of each radio button is the associated poll choice's ID. The + ``name`` of each radio button is ``"choice"``. That means, when somebody + selects one of the radio buttons and submits the form, it'll send the + POST data ``choice=3``. This is HTML Forms 101. + + * We set the form's ``action`` to ``/polls/{{ poll.id }}/vote/``, and we + set ``method="post"``. Using ``method="post"`` (as opposed to + ``method="get"``) is very important, because the act of submitting this + form will alter data server-side. Whenever you create a form that alters + data server-side, use ``method="post"``. This tip isn't specific to + Django; it's just good Web development practice. + + * ``forloop.counter`` indicates how many times the :ttag:`for` tag has gone + through its loop + + * Since we're creating a POST form (which can have the effect of modifying + data), we need to worry about Cross Site Request Forgeries. + Thankfully, you don't have to worry too hard, because Django comes with + a very easy-to-use system for protecting against it. In short, all POST + forms that are targeted at internal URLs should use the ``{% csrf_token %}`` + template tag. + +The ``{% csrf_token %}`` tag requires information from the request object, which +is not normally accessible from within the template context. To fix this, a +small adjustment needs to be made to the ``detail`` view, so that it looks like +the following:: + + from django.template import RequestContext + # ... + def detail(request, poll_id): + p = get_object_or_404(Poll, pk=poll_id) + return render_to_response('polls/detail.html', {'poll': p}, + context_instance=RequestContext(request)) + +The details of how this works are explained in the documentation for +:ref:`RequestContext <subclassing-context-requestcontext>`. + +Now, let's create a Django view that handles the submitted data and does +something with it. Remember, in :doc:`Tutorial 3 </intro/tutorial03>`, we +created a URLconf for the polls application that includes this line:: + + (r'^(?P<poll_id>\d+)/vote/$', 'vote'), + +We also created a dummy implementation of the ``vote()`` function. Let's +create a real version. Add the following to ``polls/views.py``:: + + from django.shortcuts import get_object_or_404, render_to_response + from django.http import HttpResponseRedirect, HttpResponse + from django.core.urlresolvers import reverse + from django.template import RequestContext + from polls.models import Choice, Poll + # ... + def vote(request, poll_id): + p = get_object_or_404(Poll, pk=poll_id) + try: + selected_choice = p.choice_set.get(pk=request.POST['choice']) + except (KeyError, Choice.DoesNotExist): + # Redisplay the poll voting form. + return render_to_response('polls/detail.html', { + 'poll': p, + 'error_message': "You didn't select a choice.", + }, context_instance=RequestContext(request)) + else: + selected_choice.votes += 1 + selected_choice.save() + # Always return an HttpResponseRedirect after successfully dealing + # with POST data. This prevents data from being posted twice if a + # user hits the Back button. + return HttpResponseRedirect(reverse('polls.views.results', args=(p.id,))) + +This code includes a few things we haven't covered yet in this tutorial: + + * :attr:`request.POST <django.http.HttpRequest.POST>` is a dictionary-like + object that lets you access submitted data by key name. In this case, + ``request.POST['choice']`` returns the ID of the selected choice, as a + string. :attr:`request.POST <django.http.HttpRequest.POST>` values are + always strings. + + Note that Django also provides :attr:`request.GET + <django.http.HttpRequest.GET>` for accessing GET data in the same way -- + but we're explicitly using :attr:`request.POST + <django.http.HttpRequest.POST>` in our code, to ensure that data is only + altered via a POST call. + + * ``request.POST['choice']`` will raise :exc:`KeyError` if ``choice`` wasn't + provided in POST data. The above code checks for :exc:`KeyError` and + redisplays the poll form with an error message if ``choice`` isn't given. + + * After incrementing the choice count, the code returns an + :class:`~django.http.HttpResponseRedirect` rather than a normal + :class:`~django.http.HttpResponse`. + :class:`~django.http.HttpResponseRedirect` takes a single argument: the + URL to which the user will be redirected (see the following point for how + we construct the URL in this case). + + As the Python comment above points out, you should always return an + :class:`~django.http.HttpResponseRedirect` after successfully dealing with + POST data. This tip isn't specific to Django; it's just good Web + development practice. + + * We are using the :func:`~django.core.urlresolvers.reverse` function in the + :class:`~django.http.HttpResponseRedirect` constructor in this example. + This function helps avoid having to hardcode a URL in the view function. + It is given the name of the view that we want to pass control to and the + variable portion of the URL pattern that points to that view. In this + case, using the URLconf we set up in Tutorial 3, this + :func:`~django.core.urlresolvers.reverse` call will return a string like + :: + + '/polls/3/results/' + + ... where the ``3`` is the value of ``p.id``. This redirected URL will + then call the ``'results'`` view to display the final page. Note that you + need to use the full name of the view here (including the prefix). + +As mentioned in Tutorial 3, ``request`` is a :class:`~django.http.HttpRequest` +object. For more on :class:`~django.http.HttpRequest` objects, see the +:doc:`request and response documentation </ref/request-response>`. + +After somebody votes in a poll, the ``vote()`` view redirects to the results +page for the poll. Let's write that view:: + + def results(request, poll_id): + p = get_object_or_404(Poll, pk=poll_id) + return render_to_response('polls/results.html', {'poll': p}) + +This is almost exactly the same as the ``detail()`` view from :doc:`Tutorial 3 +</intro/tutorial03>`. The only difference is the template name. We'll fix this +redundancy later. + +Now, create a ``results.html`` template: + +.. code-block:: html+django + + <h1>{{ poll.question }}</h1> + + <ul> + {% for choice in poll.choice_set.all %} + <li>{{ choice.choice }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li> + {% endfor %} + </ul> + + <a href="/polls/{{ poll.id }}/">Vote again?</a> + +Now, go to ``/polls/1/`` in your browser and vote in the poll. You should see a +results page that gets updated each time you vote. If you submit the form +without having chosen a choice, you should see the error message. + +Use generic views: Less code is better +====================================== + +The ``detail()`` (from :doc:`Tutorial 3 </intro/tutorial03>`) and ``results()`` +views are stupidly simple -- and, as mentioned above, redundant. The ``index()`` +view (also from Tutorial 3), which displays a list of polls, is similar. + +These views represent a common case of basic Web development: getting data from +the database according to a parameter passed in the URL, loading a template and +returning the rendered template. Because this is so common, Django provides a +shortcut, called the "generic views" system. + +Generic views abstract common patterns to the point where you don't even need +to write Python code to write an app. + +Let's convert our poll app to use the generic views system, so we can delete a +bunch of our own code. We'll just have to take a few steps to make the +conversion. We will: + + 1. Convert the URLconf. + + 2. Rename a few templates. + + 3. Delete some of the old, unneeded views. + + 4. Fix up URL handling for the new views. + +Read on for details. + +.. admonition:: Why the code-shuffle? + + Generally, when writing a Django app, you'll evaluate whether generic views + are a good fit for your problem, and you'll use them from the beginning, + rather than refactoring your code halfway through. But this tutorial + intentionally has focused on writing the views "the hard way" until now, to + focus on core concepts. + + You should know basic math before you start using a calculator. + +First, open the ``polls/urls.py`` URLconf. It looks like this, according to the +tutorial so far:: + + from django.conf.urls.defaults import * + + urlpatterns = patterns('polls.views', + (r'^$', 'index'), + (r'^(?P<poll_id>\d+)/$', 'detail'), + (r'^(?P<poll_id>\d+)/results/$', 'results'), + (r'^(?P<poll_id>\d+)/vote/$', 'vote'), + ) + +Change it like so:: + + from django.conf.urls.defaults import * + from polls.models import Poll + + info_dict = { + 'queryset': Poll.objects.all(), + } + + urlpatterns = patterns('', + (r'^$', 'django.views.generic.list_detail.object_list', info_dict), + (r'^(?P<object_id>\d+)/$', 'django.views.generic.list_detail.object_detail', info_dict), + url(r'^(?P<object_id>\d+)/results/$', 'django.views.generic.list_detail.object_detail', dict(info_dict, template_name='polls/results.html'), 'poll_results'), + (r'^(?P<poll_id>\d+)/vote/$', 'polls.views.vote'), + ) + +We're using two generic views here: +:func:`~django.views.generic.list_detail.object_list` and +:func:`~django.views.generic.list_detail.object_detail`. Respectively, those two +views abstract the concepts of "display a list of objects" and "display a detail +page for a particular type of object." + + * Each generic view needs to know what data it will be acting upon. This + data is provided in a dictionary. The ``queryset`` key in this dictionary + points to the list of objects to be manipulated by the generic view. + + * The :func:`~django.views.generic.list_detail.object_detail` generic view + expects the ID value captured from the URL to be called ``"object_id"``, + so we've changed ``poll_id`` to ``object_id`` for the generic views. + + * We've added a name, ``poll_results``, to the results view so that we have + a way to refer to its URL later on (see the documentation about + :ref:`naming URL patterns <naming-url-patterns>` for information). We're + also using the :func:`~django.conf.urls.default.url` function from + :mod:`django.conf.urls.defaults` here. It's a good habit to use + :func:`~django.conf.urls.defaults.url` when you are providing a pattern + name like this. + +By default, the :func:`~django.views.generic.list_detail.object_detail` generic +view uses a template called ``<app name>/<model name>_detail.html``. In our +case, it'll use the template ``"polls/poll_detail.html"``. Thus, rename your +``polls/detail.html`` template to ``polls/poll_detail.html``, and change the +:func:`~django.shortcuts.render_to_response` line in ``vote()``. + +Similarly, the :func:`~django.views.generic.list_detail.object_list` generic +view uses a template called ``<app name>/<model name>_list.html``. Thus, rename +``polls/index.html`` to ``polls/poll_list.html``. + +Because we have more than one entry in the URLconf that uses +:func:`~django.views.generic.list_detail.object_detail` for the polls app, we +manually specify a template name for the results view: +``template_name='polls/results.html'``. Otherwise, both views would use the same +template. Note that we use ``dict()`` to return an altered dictionary in place. + +.. note:: :meth:`django.db.models.QuerySet.all` is lazy + + It might look a little frightening to see ``Poll.objects.all()`` being used + in a detail view which only needs one ``Poll`` object, but don't worry; + ``Poll.objects.all()`` is actually a special object called a + :class:`~django.db.models.QuerySet`, which is "lazy" and doesn't hit your + database until it absolutely has to. By the time the database query happens, + the :func:`~django.views.generic.list_detail.object_detail` generic view + will have narrowed its scope down to a single object, so the eventual query + will only select one row from the database. + + If you'd like to know more about how that works, The Django database API + documentation :ref:`explains the lazy nature of QuerySet objects + <querysets-are-lazy>`. + +In previous parts of the tutorial, the templates have been provided with a +context that contains the ``poll`` and ``latest_poll_list`` context variables. +However, the generic views provide the variables ``object`` and ``object_list`` +as context. Therefore, you need to change your templates to match the new +context variables. Go through your templates, and modify any reference to +``latest_poll_list`` to ``object_list``, and change any reference to ``poll`` +to ``object``. + +You can now delete the ``index()``, ``detail()`` and ``results()`` views +from ``polls/views.py``. We don't need them anymore -- they have been replaced +by generic views. + +The ``vote()`` view is still required. However, it must be modified to match the +new context variables. In the :func:`~django.shortcuts.render_to_response` call, +rename the ``poll`` context variable to ``object``. + +The last thing to do is fix the URL handling to account for the use of generic +views. In the vote view above, we used the +:func:`~django.core.urlresolvers.reverse` function to avoid hard-coding our +URLs. Now that we've switched to a generic view, we'll need to change the +:func:`~django.core.urlresolvers.reverse` call to point back to our new generic +view. We can't simply use the view function anymore -- generic views can be (and +are) used multiple times -- but we can use the name we've given:: + + return HttpResponseRedirect(reverse('poll_results', args=(p.id,))) + +Run the server, and use your new polling app based on generic views. + +For full details on generic views, see the :doc:`generic views documentation +</topics/http/generic-views>`. + +Coming soon +=========== + +The tutorial ends here for the time being. Future installments of the tutorial +will cover: + + * Advanced form processing + * Using the RSS framework + * Using the cache framework + * Using the comments framework + * Advanced admin features: Permissions + * Advanced admin features: Custom JavaScript + +In the meantime, you might want to check out some pointers on :doc:`where to go +from here </intro/whatsnext>` diff --git a/parts/django/docs/intro/whatsnext.txt b/parts/django/docs/intro/whatsnext.txt new file mode 100644 index 0000000..00c1654 --- /dev/null +++ b/parts/django/docs/intro/whatsnext.txt @@ -0,0 +1,231 @@ +================= +What to read next +================= + +So you've read all the :doc:`introductory material </intro/index>` and have +decided you'd like to keep using Django. We've only just scratched the surface +with this intro (in fact, if you've read every single word you've still read +less than 10% of the overall documentation). + +So what's next? + +Well, we've always been big fans of learning by doing. At this point you should +know enough to start a project of your own and start fooling around. As you need +to learn new tricks, come back to the documentation. + +We've put a lot of effort into making Django's documentation useful, easy to +read and as complete as possible. The rest of this document explains more about +how the documentation works so that you can get the most out of it. + +(Yes, this is documentation about documentation. Rest assured we have no plans +to write a document about how to read the document about documentation.) + +Finding documentation +===================== + +Django's got a *lot* of documentation -- almost 200,000 words -- so finding what +you need can sometimes be tricky. A few good places to start are the :ref:`search` +and the :ref:`genindex`. + +Or you can just browse around! + +How the documentation is organized +================================== + +Django's main documentation is broken up into "chunks" designed to fill +different needs: + + * The :doc:`introductory material </intro/index>` is designed for people new + to Django -- or to Web development in general. It doesn't cover anything + in depth, but instead gives a high-level overview of how developing in + Django "feels". + + * The :doc:`topic guides </topics/index>`, on the other hand, dive deep into + individual parts of Django. There are complete guides to Django's + :doc:`model system </topics/db/index>`, :doc:`template engine + </topics/templates>`, :doc:`forms framework </topics/forms/index>`, and much + more. + + This is probably where you'll want to spend most of your time; if you work + your way through these guides you should come out knowing pretty much + everything there is to know about Django. + + * Web development is often broad, not deep -- problems span many domains. + We've written a set of :doc:`how-to guides </howto/index>` that answer + common "How do I ...?" questions. Here you'll find information about + :doc:`generating PDFs with Django </howto/outputting-pdf>`, :doc:`writing + custom template tags </howto/custom-template-tags>`, and more. + + Answers to really common questions can also be found in the :doc:`FAQ + </faq/index>`. + + * The guides and how-to's don't cover every single class, function, and + method available in Django -- that would be overwhelming when you're + trying to learn. Instead, details about individual classes, functions, + methods, and modules are kept in the :doc:`reference </ref/index>`. This is + where you'll turn to find the details of a particular function or + whathaveyou. + + * Finally, there's some "specialized" documentation not usually relevant to + most developers. This includes the :doc:`release notes </releases/index>`, + :doc:`documentation of obsolete features </obsolete/index>`, + :doc:`internals documentation </internals/index>` for those who want to add + code to Django itself, and a :doc:`few other things that simply don't fit + elsewhere </misc/index>`. + + +How documentation is updated +============================ + +Just as the Django code base is developed and improved on a daily basis, our +documentation is consistently improving. We improve documentation for several +reasons: + + * To make content fixes, such as grammar/typo corrections. + + * To add information and/or examples to existing sections that need to be + expanded. + + * To document Django features that aren't yet documented. (The list of + such features is shrinking but exists nonetheless.) + + * To add documentation for new features as new features get added, or as + Django APIs or behaviors change. + +Django's documentation is kept in the same source control system as its code. It +lives in the `django/trunk/docs`_ directory of our Subversion repository. Each +document online is a separate text file in the repository. + +.. _django/trunk/docs: http://code.djangoproject.com/browser/django/trunk/docs + +Where to get it +=============== + +You can read Django documentation in several ways. They are, in order of +preference: + +On the Web +---------- + +The most recent version of the Django documentation lives at +http://docs.djangoproject.com/en/dev/. These HTML pages are generated +automatically from the text files in source control. That means they reflect the +"latest and greatest" in Django -- they include the very latest corrections and +additions, and they discuss the latest Django features, which may only be +available to users of the Django development version. (See "Differences between +versions" below.) + +We encourage you to help improve the docs by submitting changes, corrections and +suggestions in the `ticket system`_. The Django developers actively monitor the +ticket system and use your feedback to improve the documentation for everybody. + +Note, however, that tickets should explicitly relate to the documentation, +rather than asking broad tech-support questions. If you need help with your +particular Django setup, try the `django-users mailing list`_ or the `#django +IRC channel`_ instead. + +.. _ticket system: http://code.djangoproject.com/simpleticket?component=Documentation +.. _django-users mailing list: http://groups.google.com/group/django-users +.. _#django IRC channel: irc://irc.freenode.net/django + +In plain text +------------- + +For offline reading, or just for convenience, you can read the Django +documentation in plain text. + +If you're using an official release of Django, note that the zipped package +(tarball) of the code includes a ``docs/`` directory, which contains all the +documentation for that release. + +If you're using the development version of Django (aka the Subversion "trunk"), +note that the ``docs/`` directory contains all of the documentation. You can +``svn update`` it, just as you ``svn update`` the Python code, in order to get +the latest changes. + +You can check out the latest Django documentation from Subversion using this +shell command: + +.. code-block:: bash + + $ svn co http://code.djangoproject.com/svn/django/trunk/docs/ django_docs + +One low-tech way of taking advantage of the text documentation is by using the +Unix ``grep`` utility to search for a phrase in all of the documentation. For +example, this will show you each mention of the phrase "max_length" in any +Django document: + +.. code-block:: bash + + $ grep -r max_length /path/to/django/docs/ + +As HTML, locally +---------------- + +You can get a local copy of the HTML documentation following a few easy steps: + + * Django's documentation uses a system called Sphinx__ to convert from + plain text to HTML. You'll need to install Sphinx by either downloading + and installing the package from the Sphinx Web site, or by Python's + ``easy_install``: + + .. code-block:: bash + + $ easy_install Sphinx + + * Then, just use the included ``Makefile`` to turn the documentation into + HTML: + + .. code-block:: bash + + $ cd path/to/django/docs + $ make html + + You'll need `GNU Make`__ installed for this. + + * The HTML documentation will be placed in ``docs/_build/html``. + +.. note:: + + Generation of the Django documentation will work with Sphinx version 0.6 + or newer, but we recommend going straight to Sphinx 1.0.2 or newer. + +__ http://sphinx.pocoo.org/ +__ http://www.gnu.org/software/make/ + +Differences between versions +============================ + +As previously mentioned, the text documentation in our Subversion repository +contains the "latest and greatest" changes and additions. These changes often +include documentation of new features added in the Django development version +-- the Subversion ("trunk") version of Django. For that reason, it's worth +pointing out our policy on keeping straight the documentation for various +versions of the framework. + +We follow this policy: + + * The primary documentation on djangoproject.com is an HTML version of the + latest docs in Subversion. These docs always correspond to the latest + official Django release, plus whatever features we've added/changed in + the framework *since* the latest release. + + * As we add features to Django's development version, we try to update the + documentation in the same Subversion commit transaction. + + * To distinguish feature changes/additions in the docs, we use the phrase: + "New in version X.Y", being X.Y the next release version (hence, the one + being developed). + + * Documentation for a particular Django release is frozen once the version + has been released officially. It remains a snapshot of the docs as of the + moment of the release. We will make exceptions to this rule in + the case of retroactive security updates or other such retroactive + changes. Once documentation is frozen, we add a note to the top of each + frozen document that says "These docs are frozen for Django version XXX" + and links to the current version of that document. + + * The `main documentation Web page`_ includes links to documentation for + all previous versions. + +.. _main documentation Web page: http://docs.djangoproject.com/en/dev/ |