1
0
mirror of https://github.com/django/django.git synced 2025-07-04 17:59:13 +00:00

magic-removal: Fixes #1599, Refs #1464 -- Updated all tutorials for new syntax, changes in manage.py, etc.

git-svn-id: http://code.djangoproject.com/svn/django/branches/magic-removal@2632 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Russell Keith-Magee 2006-04-08 08:33:52 +00:00
parent 218035c3e1
commit d1083f15fb
4 changed files with 102 additions and 92 deletions

View File

@ -24,7 +24,7 @@ Initial setup
If this is your first time using Django, you'll have to take care of some If this is your first time using Django, you'll have to take care of some
initial setup. initial setup.
Run the command ``django-admin.py startproject myproject``. That'll create a Run the command ``django-admin.py startproject myproject``. This will create a
``myproject`` directory in your current directory. ``myproject`` directory in your current directory.
(``django-admin.py`` should be on your system path if you installed Django via (``django-admin.py`` should be on your system path if you installed Django via
@ -125,9 +125,10 @@ database's connection parameters:
database's interactive prompt. database's interactive prompt.
While you're editing ``settings.py``, take note of the ``INSTALLED_APPS`` While you're editing ``settings.py``, take note of the ``INSTALLED_APPS``
setting. That variable holds the names of all Django applications that are setting towards the bottom of the file. That variable holds the names of all
activated in this Django instance. Apps can be used in multiple projects, Django applications that are activated in this Django instance. Apps can be
and you can distribute them. used in multiple projects, and you can package and distribute them for use
by others in their projects.
By default, ``INSTALLED_APPS`` contains the following apps, all of which come By default, ``INSTALLED_APPS`` contains the following apps, all of which come
with Django:: with Django::
@ -285,12 +286,9 @@ to include the string ``'myproject.polls'``. So it'll look like this::
'django.contrib.contenttypes', 'django.contrib.contenttypes',
'django.contrib.sessions', 'django.contrib.sessions',
'django.contrib.sites', 'django.contrib.sites',
'myproject.polls', 'myproject.polls'
) )
(Don't forget the trailing comma, because of Python's rule about single-value
tuples: Without a trailing comma, Python wouldn't know this was a tuple.)
Now Django knows ``myproject`` includes the ``polls`` app. Let's run another command:: Now Django knows ``myproject`` includes the ``polls`` app. Let's run another command::
python manage.py sql polls python manage.py sql polls
@ -331,6 +329,12 @@ Note the following:
quotes. The author of this tutorial runs PostgreSQL, so the example quotes. The author of this tutorial runs PostgreSQL, so the example
output is in PostgreSQL syntax. output is in PostgreSQL syntax.
* The `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: If you're interested, also run the following commands:
* ``python manage.py sqlinitialdata polls`` -- Outputs any initial data * ``python manage.py sqlinitialdata polls`` -- Outputs any initial data
@ -343,8 +347,8 @@ If you're interested, also run the following commands:
* ``python manage.py sqlindexes polls`` -- Outputs the ``CREATE INDEX`` * ``python manage.py sqlindexes polls`` -- Outputs the ``CREATE INDEX``
statements for this app. statements for this app.
* ``python manage.py sqlall polls`` -- A combination of 'sql' and * ``python manage.py sqlall polls`` -- A combination of all the SQL from
'sqlinitialdata'. the 'sql', 'sqlinitialdata', and 'sqlindexes' commands.
Looking at the output of those commands can help you understand what's actually Looking at the output of those commands can help you understand what's actually
happening under the hood. happening under the hood.
@ -353,9 +357,11 @@ Now, run ``syncdb`` again to create those model tables in your database::
python manage.py syncdb python manage.py syncdb
As a review, the ``syncdb`` command creates the tables for all apps in The ``syncdb`` command runs the sql from 'sqlall' on your database for all apps
``INSTALLED_APPS`` that don't already exist in your database. So you can run it in ``INSTALLED_APPS`` that don't already exist in your database. This creates
again and again, and it'll always just create the tables that don't exist. all the tables, initial data and indexes for any apps you have added to your
project since the last time you ran syncdb. ``syncdb`` can be called as often
as you like, and it will only ever create the tables that don't exist.
Read the `django-admin.py documentation`_ for full information on what the Read the `django-admin.py documentation`_ for full information on what the
``manage.py`` utility can do. ``manage.py`` utility can do.
@ -454,10 +460,11 @@ representations are used throughout Django's automatically-generated admin.
Note these are normal Python methods. Let's add a custom method, just for Note these are normal Python methods. Let's add a custom method, just for
demonstration:: demonstration::
import datetime
# ...
class Poll(models.Model): class Poll(models.Model):
# ... # ...
def was_published_today(self): def was_published_today(self):
import datetime
return self.pub_date.date() == datetime.date.today() return self.pub_date.date() == datetime.date.today()
Note the addition of ``import datetime`` to reference Python's standard Note the addition of ``import datetime`` to reference Python's standard
@ -488,8 +495,6 @@ Let's jump back into the Python interactive shell by running
Traceback (most recent call last): Traceback (most recent call last):
... ...
DoesNotExist: Poll does not exist for {'id': 2} DoesNotExist: Poll does not exist for {'id': 2}
>>> Poll.objects.filter(question__startswith='What')
[What's up?]
# Lookup by a primary key is the most common case, so Django provides a # Lookup by a primary key is the most common case, so Django provides a
# shortcut for primary-key exact lookups. # shortcut for primary-key exact lookups.
@ -502,14 +507,15 @@ Let's jump back into the Python interactive shell by running
>>> p.was_published_today() >>> p.was_published_today()
False False
# Give the Poll a couple of Choices. Each one of these method calls does an # Give the Poll a couple of Choices. The create call constructs a new
# INSERT statement behind the scenes and returns the new Choice object. # choice object, does the INSERT statement, adds the choice to the set
# of available choices and returns the new Choice object.
>>> p = Poll.objects.get(pk=1) >>> p = Poll.objects.get(pk=1)
>>> p.choice_set.add(choice='Not much', votes=0) >>> p.choice_set.create(choice='Not much', votes=0)
Not much Not much
>>> p.choice_set.add(choice='The sky', votes=0) >>> p.choice_set.create(choice='The sky', votes=0)
The sky The sky
>>> c = p.choice_set.add(choice='Just hacking again', votes=0) >>> c = p.choice_set.create(choice='Just hacking again', votes=0)
# Choice objects have API access to their related Poll objects. # Choice objects have API access to their related Poll objects.
>>> c.poll >>> c.poll
@ -518,7 +524,7 @@ Let's jump back into the Python interactive shell by running
# And vice versa: Poll objects get access to Choice objects. # And vice versa: Poll objects get access to Choice objects.
>>> p.choice_set.all() >>> p.choice_set.all()
[Not much, The sky, Just hacking again] [Not much, The sky, Just hacking again]
>>> p.choice_set.all().count() >>> p.choice_set.count()
3 3
# The API automatically follows relationships as far as you need. # The API automatically follows relationships as far as you need.

View File

@ -31,22 +31,13 @@ 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: activate the admin site for your installation, do these three things:
* Add ``"django.contrib.admin"`` to your ``INSTALLED_APPS`` setting. * Add ``"django.contrib.admin"`` to your ``INSTALLED_APPS`` setting.
* Run the command ``python manage.py install admin``. This will create an * Run ``python manage.py syncdb``. Since you have added a new application
extra database table that the admin needs. to ``INSTALLED_APPS``, the database tables need to be updated.
* Edit your ``myproject/urls.py`` file and uncomment the line below * Edit your ``myproject/urls.py`` file and uncomment the line below
"Uncomment this for admin:". This file is a URLconf; we'll dig into "Uncomment this for admin:". This file is a URLconf; we'll dig into
URLconfs in the next tutorial. For now, all you need to know is that it URLconfs in the next tutorial. For now, all you need to know is that it
maps URL roots to applications. maps URL roots to applications.
Create a user account
=====================
Run the following command to create a superuser account for your admin site::
python manage.py createsuperuser
The script will prompt you for a username, e-mail address and password (twice).
Start the development server Start the development server
============================ ============================
@ -96,7 +87,8 @@ creating an empty class means "give this object an admin interface using
all the default options." all the default options."
Now reload the Django admin page to see your changes. Note that you don't have Now reload the Django admin page to see your changes. Note that you don't have
to restart the development server -- it auto-reloads code. to restart the development server -- the server will auto-reloads your project,
so any modifications code will be seen immediately in your browser.
Explore the free admin functionality Explore the free admin functionality
==================================== ====================================

View File

@ -74,25 +74,27 @@ Time for an example. Edit ``myproject/urls.py`` so it looks like this::
urlpatterns = patterns('', urlpatterns = patterns('',
(r'^polls/$', 'myproject.polls.views.index'), (r'^polls/$', 'myproject.polls.views.index'),
(r'^polls/(\d+)/$', 'myproject.polls.views.detail'), (r'^polls/(?P<poll_id>\d+)/$', 'myproject.polls.views.detail'),
(r'^polls/(\d+)/results/$', 'myproject.polls.views.results'), (r'^polls/(?P<poll_id>\d+)/results/$', 'myproject.polls.views.results'),
(r'^polls/(\d+)/vote/$', 'myproject.polls.views.vote'), (r'^polls/(?P<poll_id>\d+)/vote/$', 'myproject.polls.views.vote'),
) )
This is worth a review. When somebody requests a page from your Web site -- 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 say, "/polls/23/", Django will load this Python module, because it's pointed to
by the ``ROOT_URLCONF`` setting. It finds the variable named ``urlpatterns`` by the ``ROOT_URLCONF`` setting. It finds the variable named ``urlpatterns``
and traverses the regular expressions in order. When it finds a regular and traverses the regular expressions in order. When it finds a regular
expression that matches -- ``r'^polls/(\d+)/$'`` -- it loads the expression that matches -- ``r'^polls/(?P<poll_id>\d+)/$'`` -- it loads the
associated Python package/module: ``myproject.polls.views.detail``. That associated Python package/module: ``myproject.polls.views.detail``. That
corresponds to the function ``detail()`` in ``myproject/polls/views.py``. corresponds to the function ``detail()`` in ``myproject/polls/views.py``.
Finally, it calls that ``detail()`` function like so:: Finally, it calls that ``detail()`` function like so::
detail(request=<HttpRequest object>, poll_id='23') detail(request=<HttpRequest object>, poll_id='23')
The ``poll_id='23'`` part comes from ``(\d+)``. Using parenthesis around a The ``poll_id='23'`` part comes from ``(?P<poll_id>\d+)``. Using parenthesis around a
pattern "captures" the text matched by that pattern and sends it as an argument pattern "captures" the text matched by that pattern and sends it as an argument
to the view function. 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 experession to match a sequence of
digits (i.e., a number).
Because the URL patterns are regular expressions, there really is no limit on 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 what you can do with them. And there's no need to add URL cruft such as
@ -185,29 +187,29 @@ in Tutorial 1. Here's one stab at the ``index()`` view, which displays the
latest 5 poll questions in the system, separated by commas, according to latest 5 poll questions in the system, separated by commas, according to
publication date:: publication date::
from django.models.polls import polls from myproject.polls.models import Poll
from django.http import HttpResponse from django.http import HttpResponse
def index(request): def index(request):
latest_poll_list = polls.get_list(order_by=['-pub_date'], limit=5) latest_poll_list = Poll.objects.all().order_by('-pub_date')
output = ', '.join([p.question for p in latest_poll_list]) output = ', '.join([p.question for p in latest_poll_list])
return HttpResponse(output) return HttpResponse(output)
There's a problem here, though: The page's design is hard-coded in the view. If 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. 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:: So let's use Django's template system to separate the design from Python::
from django.template import Context, loader from django.template import Context, loader
from django.models.polls import polls from myproject.polls.models import Poll
from django.http import HttpResponse from django.http import HttpResponse
def index(request): def index(request):
latest_poll_list = polls.get_list(order_by=['-pub_date'], limit=5) latest_poll_list = Poll.objects.all().order_by('-pub_date')
t = loader.get_template('polls/index') t = loader.get_template('polls/index')
c = Context({ c = Context({
'latest_poll_list': latest_poll_list, 'latest_poll_list': latest_poll_list,
}) })
return HttpResponse(t.render(c)) return HttpResponse(t.render(c))
That code loads the template called "polls/index" and passes it a context. The That code loads the template called "polls/index" and passes it a context. The
context is a dictionary mapping template variable names to Python objects. context is a dictionary mapping template variable names to Python objects.
@ -254,11 +256,11 @@ It's a very common idiom to load a template, fill a context and return an
``HttpResponse`` object with the result of the rendered template. Django ``HttpResponse`` object with the result of the rendered template. Django
provides a shortcut. Here's the full ``index()`` view, rewritten:: provides a shortcut. Here's the full ``index()`` view, rewritten::
from django.shortcuts import render_to_response from django.shortcuts import render_to_response
from django.models.polls import polls from myproject.polls.models import Poll
def index(request): def index(request):
latest_poll_list = polls.get_list(order_by=['-pub_date'], limit=5) latest_poll_list = Poll.objects.all().order_by('-pub_date')
return render_to_response('polls/index', {'latest_poll_list': latest_poll_list}) return render_to_response('polls/index', {'latest_poll_list': latest_poll_list})
Note that we no longer need to import ``loader``, ``Context`` or Note that we no longer need to import ``loader``, ``Context`` or
@ -275,12 +277,13 @@ Now, let's tackle the poll detail view -- the page that displays the question
for a given poll. Here's the view:: for a given poll. Here's the view::
from django.http import Http404 from django.http import Http404
def detail(request, poll_id): # ...
try: def detail(request, poll_id):
p = polls.get_object(pk=poll_id) try:
except polls.PollDoesNotExist: p = Poll.objects.get(pk=poll_id)
raise Http404 except Poll.DoesNotExist:
return render_to_response('polls/detail', {'poll': p}) raise Http404
return render_to_response('polls/detail', {'poll': p})
The new concept here: The view raises the ``django.http.Http404`` The new concept here: The view raises the ``django.http.Http404``
exception if a poll with the requested ID doesn't exist. exception if a poll with the requested ID doesn't exist.
@ -292,9 +295,10 @@ It's a very common idiom to use ``get_object()`` and raise ``Http404`` if the
object doesn't exist. Django provides a shortcut. Here's the ``detail()`` view, object doesn't exist. Django provides a shortcut. Here's the ``detail()`` view,
rewritten:: rewritten::
from django.shortcuts import get_object_or_404 from django.shortcuts import render_to_response, get_object_or_404
# ...
def detail(request, poll_id): def detail(request, poll_id):
p = get_object_or_404(polls, pk=poll_id) p = get_object_or_404(Poll, pk=poll_id)
return render_to_response('polls/detail', {'poll': p}) return render_to_response('polls/detail', {'poll': p})
The ``get_object_or_404()`` function takes a Django model module as its first The ``get_object_or_404()`` function takes a Django model module as its first

View File

@ -18,7 +18,7 @@ template contains an HTML ``<form>`` element::
{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %} {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
<form action="/polls/{{ poll.id }}/vote/" method="post"> <form action="/polls/{{ poll.id }}/vote/" method="post">
{% for choice in poll.get_choice_list %} {% for choice in poll.choice_set.all %}
<input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" /> <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" />
<label for="choice{{ forloop.counter }}">{{ choice.choice }}</label><br /> <label for="choice{{ forloop.counter }}">{{ choice.choice }}</label><br />
{% endfor %} {% endfor %}
@ -41,22 +41,22 @@ A quick rundown:
Django; it's just good Web development practice. Django; it's just good Web development practice.
Now, let's create a Django view that handles the submitted data and does Now, let's create a Django view that handles the submitted data and does
something with it. Remember, in `Tutorial 3`_, we create a URLconf that something with it. Remember, in `Tutorial 3`_, we create a URLconf for the
included this line:: polls application that includes this line::
(r'^polls/(?P<poll_id>\d+)/vote/$', 'myproject.polls.views.vote'), (r'^(?P<poll_id>\d+)/vote/$', 'myproject.polls.views.vote'),
So let's create a ``vote()`` function in ``myproject/polls/views.py``:: So let's create a ``vote()`` function in ``myproject/polls/views.py``::
from django.shortcuts import get_object_or_404, render_to_response from django.shortcuts import get_object_or_404, render_to_response
from django.http import HttpResponseRedirect from django.http import Http404,HttpResponseRedirect
from myproject.polls.models import Choice, Poll from myproject.polls.models import Choice, Poll
# ...
def vote(request, poll_id): def vote(request, poll_id):
p = get_object_or_404(Poll, pk=poll_id) p = get_object_or_404(Poll, pk=poll_id)
try: try:
selected_choice = p.choice_set.filter(pk=request.POST['choice']) selected_choice = p.choice_set.filter(pk=request.POST['choice'])
except (KeyError, choices.ChoiceDoesNotExist): except (KeyError, Choice.DoesNotExist):
# Redisplay the poll voting form. # Redisplay the poll voting form.
return render_to_response('polls/detail', { return render_to_response('polls/detail', {
'poll': p, 'poll': p,
@ -113,7 +113,7 @@ Now, create a ``results.html`` template::
<h1>{{ poll.question }}</h1> <h1>{{ poll.question }}</h1>
<ul> <ul>
{% for choice in poll.get_choice_list %} {% for choice in poll.choice_set.all %}
<li>{{ choice.choice }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li> <li>{{ choice.choice }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
{% endfor %} {% endfor %}
</ul> </ul>
@ -185,37 +185,45 @@ We're using two generic views here: ``object_list`` and ``object_detail``.
Respectively, those two views abstract the concepts of "display a list of Respectively, those two views abstract the concepts of "display a list of
objects" and "display a detail page for a particular type of object." objects" and "display a detail page for a particular type of object."
* Each generic view needs to know which model its acting on. This * Each generic view needs to know what data it will be acting upon. This
is done using a QuerySet. 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 ``object_detail`` generic view expects that the ID value captured * The ``object_detail`` generic view expects the ID value captured
from the URL is called ``"object_id"``, so we've changed ``poll_id`` to from the URL to be called ``"object_id"``, so we've changed ``poll_id`` to
``object_id`` for the generic views. ``object_id`` for the generic views.
By default, the ``object_detail`` generic view uses a template called By default, the ``object_detail`` generic view uses a template called
``<app_label>/<module_name>_detail``. In our case, it'll use the template ``<app name>/<module name>_detail``. In our case, it'll use the template
``"polls/polls_detail"``. Thus, rename your ``polls/detail.html`` template to ``"polls/poll_detail"``. Thus, rename your ``polls/detail.html`` template to
``polls/polls_detail.html``, and change the ``render_to_response()`` line in ``polls/poll_detail.html``, and change the ``render_to_response()`` line in
``vote()``. ``vote()``.
Similarly, the ``object_list`` generic view uses a template called Similarly, the ``object_list`` generic view uses a template called
``<app_label>/<module_name>_list``. Thus, rename ``polls/index.html`` to ``<app name>/<module name>_list``. Thus, rename ``poll/index.html`` to
``polls/polls_list.html``. ``polls/poll_list.html``.
Because we have more than one entry in the URLconf that uses ``object_detail`` Because we have more than one entry in the URLconf that uses ``object_detail``
for the polls app, we manually specify a template name for the results view: for the polls app, we manually specify a template name for the results view:
``template_name='polls/results'``. Otherwise, both views would use the same ``template_name='polls/results'``. Otherwise, both views would use the same
template. Note that we use ``dict()`` to return an altered dictionary in place. template. Note that we use ``dict()`` to return an altered dictionary in place.
The generic views pass ``object`` and ``object_list`` to their templates, so In previous versions of the tutorial, the templates have been provided with a context
change your templates so that ``latest_poll_list`` becomes ``object_list`` and that contains the ``poll` and ``latest_poll_list`` context variables. However,
``poll`` becomes ``object``. 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``.
In the ``vote()`` view, change the template call from ``polls/detail`` to You can now delete the ``index()``, ``detail()`` and ``results()`` views
``polls/polls_detail``, and pass ``object`` in the context instead of ``poll``. from ``polls/views.py``. We don't need them anymore -- they have been replaced
by generic views.
Finally, you can delete the ``index()``, ``detail()`` and ``results()`` views The ``vote()`` view is still required. However, it must be modified to match
from ``polls/views.py``. We don't need them anymore. the new templates and context variables. Change the template call from ``polls/detail``
to ``polls/polls_detail``, and pass ``object`` in the context instead of ``poll``.
Run the server, and use your new polling app based on generic views.
For full details on generic views, see the `generic views documentation`_. For full details on generic views, see the `generic views documentation`_.