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:
parent
218035c3e1
commit
d1083f15fb
@ -24,7 +24,7 @@ Initial setup
|
||||
If this is your first time using Django, you'll have to take care of some
|
||||
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.
|
||||
|
||||
(``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.
|
||||
|
||||
While you're editing ``settings.py``, take note of the ``INSTALLED_APPS``
|
||||
setting. 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 distribute them.
|
||||
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, ``INSTALLED_APPS`` contains the following apps, all of which come
|
||||
with Django::
|
||||
@ -285,12 +286,9 @@ to include the string ``'myproject.polls'``. So it'll look like this::
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'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::
|
||||
|
||||
python manage.py sql polls
|
||||
@ -331,6 +329,12 @@ Note the following:
|
||||
quotes. The author of this tutorial runs PostgreSQL, so the example
|
||||
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:
|
||||
|
||||
* ``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``
|
||||
statements for this app.
|
||||
|
||||
* ``python manage.py sqlall polls`` -- A combination of 'sql' and
|
||||
'sqlinitialdata'.
|
||||
* ``python manage.py sqlall polls`` -- A combination of all the SQL from
|
||||
the 'sql', 'sqlinitialdata', and 'sqlindexes' commands.
|
||||
|
||||
Looking at the output of those commands can help you understand what's actually
|
||||
happening under the hood.
|
||||
@ -353,9 +357,11 @@ Now, run ``syncdb`` again to create those model tables in your database::
|
||||
|
||||
python manage.py syncdb
|
||||
|
||||
As a review, the ``syncdb`` command creates the tables for all apps in
|
||||
``INSTALLED_APPS`` that don't already exist in your database. So you can run it
|
||||
again and again, and it'll always just create the tables that don't exist.
|
||||
The ``syncdb`` command runs the sql from 'sqlall' on your database for all apps
|
||||
in ``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. ``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
|
||||
``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
|
||||
demonstration::
|
||||
|
||||
import datetime
|
||||
# ...
|
||||
class Poll(models.Model):
|
||||
# ...
|
||||
def was_published_today(self):
|
||||
import datetime
|
||||
return self.pub_date.date() == datetime.date.today()
|
||||
|
||||
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):
|
||||
...
|
||||
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
|
||||
# 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()
|
||||
False
|
||||
|
||||
# Give the Poll a couple of Choices. Each one of these method calls does an
|
||||
# INSERT statement behind the scenes and returns the new Choice object.
|
||||
# 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.
|
||||
>>> 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
|
||||
>>> p.choice_set.add(choice='The sky', votes=0)
|
||||
>>> p.choice_set.create(choice='The sky', votes=0)
|
||||
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.
|
||||
>>> 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.
|
||||
>>> p.choice_set.all()
|
||||
[Not much, The sky, Just hacking again]
|
||||
>>> p.choice_set.all().count()
|
||||
>>> p.choice_set.count()
|
||||
3
|
||||
|
||||
# The API automatically follows relationships as far as you need.
|
||||
|
@ -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:
|
||||
|
||||
* Add ``"django.contrib.admin"`` to your ``INSTALLED_APPS`` setting.
|
||||
* Run the command ``python manage.py install admin``. This will create an
|
||||
extra database table that the admin needs.
|
||||
* Run ``python manage.py syncdb``. Since you have added a new application
|
||||
to ``INSTALLED_APPS``, the database tables need to be updated.
|
||||
* Edit your ``myproject/urls.py`` file and uncomment the line below
|
||||
"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
|
||||
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
|
||||
============================
|
||||
|
||||
@ -96,7 +87,8 @@ creating an empty class means "give this object an admin interface using
|
||||
all the default options."
|
||||
|
||||
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
|
||||
====================================
|
||||
|
@ -74,25 +74,27 @@ Time for an example. Edit ``myproject/urls.py`` so it looks like this::
|
||||
|
||||
urlpatterns = patterns('',
|
||||
(r'^polls/$', 'myproject.polls.views.index'),
|
||||
(r'^polls/(\d+)/$', 'myproject.polls.views.detail'),
|
||||
(r'^polls/(\d+)/results/$', 'myproject.polls.views.results'),
|
||||
(r'^polls/(\d+)/vote/$', 'myproject.polls.views.vote'),
|
||||
(r'^polls/(?P<poll_id>\d+)/$', 'myproject.polls.views.detail'),
|
||||
(r'^polls/(?P<poll_id>\d+)/results/$', 'myproject.polls.views.results'),
|
||||
(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 --
|
||||
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``
|
||||
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
|
||||
corresponds to the function ``detail()`` in ``myproject/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 ``(\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
|
||||
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
|
||||
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
|
||||
publication date::
|
||||
|
||||
from django.models.polls import polls
|
||||
from django.http import HttpResponse
|
||||
from myproject.polls.models import Poll
|
||||
from django.http import HttpResponse
|
||||
|
||||
def index(request):
|
||||
latest_poll_list = polls.get_list(order_by=['-pub_date'], limit=5)
|
||||
output = ', '.join([p.question for p in latest_poll_list])
|
||||
return HttpResponse(output)
|
||||
def index(request):
|
||||
latest_poll_list = Poll.objects.all().order_by('-pub_date')
|
||||
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 django.models.polls import polls
|
||||
from django.http import HttpResponse
|
||||
from django.template import Context, loader
|
||||
from myproject.polls.models import Poll
|
||||
from django.http import HttpResponse
|
||||
|
||||
def index(request):
|
||||
latest_poll_list = polls.get_list(order_by=['-pub_date'], limit=5)
|
||||
t = loader.get_template('polls/index')
|
||||
c = Context({
|
||||
'latest_poll_list': latest_poll_list,
|
||||
})
|
||||
return HttpResponse(t.render(c))
|
||||
def index(request):
|
||||
latest_poll_list = Poll.objects.all().order_by('-pub_date')
|
||||
t = loader.get_template('polls/index')
|
||||
c = Context({
|
||||
'latest_poll_list': latest_poll_list,
|
||||
})
|
||||
return HttpResponse(t.render(c))
|
||||
|
||||
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.
|
||||
@ -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
|
||||
provides a shortcut. Here's the full ``index()`` view, rewritten::
|
||||
|
||||
from django.shortcuts import render_to_response
|
||||
from django.models.polls import polls
|
||||
from django.shortcuts import render_to_response
|
||||
from myproject.polls.models import Poll
|
||||
|
||||
def index(request):
|
||||
latest_poll_list = polls.get_list(order_by=['-pub_date'], limit=5)
|
||||
def index(request):
|
||||
latest_poll_list = Poll.objects.all().order_by('-pub_date')
|
||||
return render_to_response('polls/index', {'latest_poll_list': latest_poll_list})
|
||||
|
||||
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::
|
||||
|
||||
from django.http import Http404
|
||||
def detail(request, poll_id):
|
||||
try:
|
||||
p = polls.get_object(pk=poll_id)
|
||||
except polls.PollDoesNotExist:
|
||||
raise Http404
|
||||
return render_to_response('polls/detail', {'poll': p})
|
||||
# ...
|
||||
def detail(request, poll_id):
|
||||
try:
|
||||
p = Poll.objects.get(pk=poll_id)
|
||||
except Poll.DoesNotExist:
|
||||
raise Http404
|
||||
return render_to_response('polls/detail', {'poll': p})
|
||||
|
||||
The new concept here: The view raises the ``django.http.Http404``
|
||||
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,
|
||||
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):
|
||||
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})
|
||||
|
||||
The ``get_object_or_404()`` function takes a Django model module as its first
|
||||
|
@ -18,7 +18,7 @@ template contains an HTML ``<form>`` element::
|
||||
{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
|
||||
|
||||
<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 }}" />
|
||||
<label for="choice{{ forloop.counter }}">{{ choice.choice }}</label><br />
|
||||
{% endfor %}
|
||||
@ -41,22 +41,22 @@ A quick rundown:
|
||||
Django; it's just good Web development practice.
|
||||
|
||||
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
|
||||
included this line::
|
||||
something with it. Remember, in `Tutorial 3`_, we create a URLconf for the
|
||||
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``::
|
||||
|
||||
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
|
||||
|
||||
# ...
|
||||
def vote(request, poll_id):
|
||||
p = get_object_or_404(Poll, pk=poll_id)
|
||||
try:
|
||||
selected_choice = p.choice_set.filter(pk=request.POST['choice'])
|
||||
except (KeyError, choices.ChoiceDoesNotExist):
|
||||
except (KeyError, Choice.DoesNotExist):
|
||||
# Redisplay the poll voting form.
|
||||
return render_to_response('polls/detail', {
|
||||
'poll': p,
|
||||
@ -113,7 +113,7 @@ Now, create a ``results.html`` template::
|
||||
<h1>{{ poll.question }}</h1>
|
||||
|
||||
<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>
|
||||
{% endfor %}
|
||||
</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
|
||||
objects" and "display a detail page for a particular type of object."
|
||||
|
||||
* Each generic view needs to know which model its acting on. This
|
||||
is done using a QuerySet.
|
||||
* 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 ``object_detail`` generic view expects that the ID value captured
|
||||
from the URL is called ``"object_id"``, so we've changed ``poll_id`` to
|
||||
* The ``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.
|
||||
|
||||
By default, the ``object_detail`` generic view uses a template called
|
||||
``<app_label>/<module_name>_detail``. In our case, it'll use the template
|
||||
``"polls/polls_detail"``. Thus, rename your ``polls/detail.html`` template to
|
||||
``polls/polls_detail.html``, and change the ``render_to_response()`` line in
|
||||
``<app name>/<module name>_detail``. In our case, it'll use the template
|
||||
``"polls/poll_detail"``. Thus, rename your ``polls/detail.html`` template to
|
||||
``polls/poll_detail.html``, and change the ``render_to_response()`` line in
|
||||
``vote()``.
|
||||
|
||||
Similarly, the ``object_list`` generic view uses a template called
|
||||
``<app_label>/<module_name>_list``. Thus, rename ``polls/index.html`` to
|
||||
``polls/polls_list.html``.
|
||||
``<app name>/<module name>_list``. Thus, rename ``poll/index.html`` to
|
||||
``polls/poll_list.html``.
|
||||
|
||||
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:
|
||||
``template_name='polls/results'``. Otherwise, both views would use the same
|
||||
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
|
||||
change your templates so that ``latest_poll_list`` becomes ``object_list`` and
|
||||
``poll`` becomes ``object``.
|
||||
In previous versions 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``.
|
||||
|
||||
In the ``vote()`` view, change the template call from ``polls/detail`` to
|
||||
``polls/polls_detail``, and pass ``object`` in the context instead of ``poll``.
|
||||
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.
|
||||
|
||||
Finally, you can delete the ``index()``, ``detail()`` and ``results()`` views
|
||||
from ``polls/views.py``. We don't need them anymore.
|
||||
The ``vote()`` view is still required. However, it must be modified to match
|
||||
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`_.
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user