Skip to content
This repository has been archived by the owner on Nov 25, 2017. It is now read-only.

Chapter03 #12

Merged
merged 21 commits into from Nov 20, 2012
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
4c493d6
Update example urls.py to match the one created by django-admin.py
mpdaugherty Oct 25, 2012
81ec74a
update example 2
mpdaugherty Oct 25, 2012
eb790a4
Continuing to update example to match new version of django-admin.py
mpdaugherty Oct 25, 2012
1e3d147
Update the import to match the example.
mpdaugherty Oct 25, 2012
18636ed
Update the example to be consistent.
mpdaugherty Oct 25, 2012
3dfa18e
Update python 2.4 to 2.7
mpdaugherty Oct 25, 2012
679db00
Update settings.py location and do not reference DATABASE_NAME, which…
mpdaugherty Oct 25, 2012
07018b5
rewrap paragraph
mpdaugherty Oct 25, 2012
bdc74e6
updating urls.py example again.
mpdaugherty Oct 25, 2012
9ccd762
web is not a proper noun
mpdaugherty Oct 26, 2012
3ac9120
The default urlconf generated by django-admin includes the url() func…
mpdaugherty Oct 26, 2012
6f66efa
Update another urlconf example.
mpdaugherty Oct 26, 2012
fb49713
Another urlconf
mpdaugherty Oct 26, 2012
d84988f
updated more urlconf examples; made note to cover url() optional argu…
mpdaugherty Oct 26, 2012
4cb5190
Use active, not passive voice.
mpdaugherty Oct 26, 2012
c5762a2
pluralize function
mpdaugherty Oct 26, 2012
115b209
Fix RST note syntax.
mpdaugherty Oct 26, 2012
dc4aee4
Since we are using the url function, we are not just adding a "python…
mpdaugherty Nov 2, 2012
4969ea7
This admonition makes more sense after we have written the view.
mpdaugherty Nov 2, 2012
ca0f84b
Merge branch 'master' into chapter03
mpdaugherty Nov 2, 2012
eb3bf62
Update Django 1.5 note to include details from https://docs.djangopro…
mpdaugherty Nov 13, 2012
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion chapter02.rst
Expand Up @@ -40,7 +40,7 @@ Python 2.5, so using a later version of Python keeps your options open.
supported it experimentally. Python 3.0 introduced a substantial number of
backwards-incompatible changes to the language itself, and, as a result,
many major Python libraries and frameworks, including Django, had not yet
caught up. Python 2.6-3.2 will be supported by Django 1.5.
caught up. Django 1.5 will support Python 2.6-3.2.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks; I updated the note.


Installation
------------
Expand Down
166 changes: 85 additions & 81 deletions chapter03.rst
Expand Up @@ -96,38 +96,38 @@ When you executed ``django-admin.py startproject`` in the previous chapter, the
script created a URLconf for you automatically: the file ``urls.py``. By
default, it looks something like this::

from django.conf.urls.defaults import *
from django.conf.urls import patterns, include, url

# 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')),
# Examples:
# url(r'^$', 'mysite.views.home', name='home'),
# url(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 admin/doc line below to enable admin documentation:
# url(r'^admin/doc/', include('django.contrib.admindocs.urls')),

# Uncomment the next line to enable the admin:
# (r'^admin/', include(admin.site.urls)),
# url(r'^admin/', include(admin.site.urls)),
)

This default URLconf includes some commonly used Django features commented out,
so that activating those features is as easy as uncommenting the appropriate
lines. If we ignore the commented-out code, here's the essence of a URLconf::

from django.conf.urls.defaults import *
from django.conf.urls.defaults import patterns, include, url

urlpatterns = patterns('',
)

Let's step through this code one line at a time:

* The first line imports all objects from the ``django.conf.urls.defaults``
module, which is Django's URLconf infrastructure. This includes a
function called ``patterns``.
* The first line imports three functions from the ``django.conf.urls.defaults``
module, which is Django's URLconf infrastructure: ``patterns``, ``include``,
and ``urls``.

* The second line calls the function ``patterns`` and saves the result
into a variable called ``urlpatterns``. The ``patterns`` function gets
Expand All @@ -143,14 +143,14 @@ note, that's how Django knew to show you the "Welcome to Django" page in the
last chapter. If your URLconf is empty, Django assumes you just started a new
project and, hence, displays that message.)

To add a URL and view to the URLconf, just add a Python tuple mapping a URL
pattern to the view function. Here's how to hook in our ``hello`` view::
To add a URL and view to the URLconf, just add a mapping between a URL
pattern and the view function. Here's how to hook in our ``hello`` view::

from django.conf.urls.defaults import *
from django.conf.urls.defaults import patterns, include, url
from mysite.views import hello

urlpatterns = patterns('',
('^hello/$', hello),
url(r'^hello/$', hello),
)

(Note that we've removed the commented-out code for brevity. You can choose
Expand All @@ -163,11 +163,28 @@ We made two changes here:
import syntax. (This assumes ``mysite/views.py`` is on your Python path;
see the sidebar for details.)

* Next, we added the line ``('^hello/$', hello),`` to ``urlpatterns``. This
line is referred to as a *URLpattern*. It's a Python tuple in which the
first element is a pattern-matching string (a regular expression; more on
this in a bit) and the second element is the view function to use for
that pattern.
* Next, we added the line ``url(r'^hello/$', hello),`` to ``urlpatterns``. This
line is referred to as a *URLpattern*. The ``url()`` function tells Django how
to handle the url that you are configuring. The first argument is a
pattern-matching string (a regular expression; more on this in a bit) and the
second argument is the view function to use for that pattern. ``url()`` can
take other optional arguments as well, which we'll cover in more depth in
:doc:`chapter08`.

.. note::

One more important detail we've introduced here is that ``r`` character in
front of the regular expression string. This tells Python that the string is a
"raw string" -- its contents should not interpret backslashes. In normal
Python strings, backslashes are used for escaping special characters -- such
as in the string ``'\n'``, which is a one-character string containing a
newline. When you add the ``r`` to make it a raw string, Python does not apply
its backslash escaping -- so, ``r'\n'`` is a two-character string containing a
literal backslash and a lowercase "n". There's a natural collision between
Python's usage of backslashes and the backslashes that are found in regular
expressions, so it's strongly suggested that you use raw strings any time
you're defining a regular expression in Python. All of the URLpatterns in this
book will be raw strings.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This note is a distraction; it's not needed here.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In fact, I'd probably just not use raw strings at all. Backslashes rarely appear in URLs (and certainly not in well-formed ones) and so the distinction's just extra noise and confusion for new users.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that this note is a distraction from the point of the chapter.

On the other hand, I do think our examples should use raw strings, because that's what startproject puts into urls.py by default. Therefore, I feel that the question would naturally occur to new users if they look at our examples vs. their urlconf and think "Why do these strings start with 'r' and the ones in the book don't?"

What do you think about using raw strings, but leaving out the explanation? Or, we could change this inline explanation to be something short like "The r indicates a raw string instead of a normal string. To find out why you would use one instead of the other, read the note at the end of this chapter." and then put the longer explanation at the end of the chapter.


In a nutshell, we just told Django that any request to the URL ``/hello/`` should
be handled by the ``hello`` view function.
Expand All @@ -178,12 +195,12 @@ be handled by the ``hello`` view function.
looks when you use the Python ``import`` statement.

For example, let's say your Python path is set to ``['',
'/usr/lib/python2.4/site-packages', '/home/username/djcode']``. If you
'/usr/lib/python2.7/site-packages', '/home/username/djcode']``. If you
execute the Python statement ``from foo import bar``, Python will look for
a module called ``foo.py`` in the current directory. (The first entry in the
Python path, an empty string, means "the current directory.") If that file
doesn't exist, Python will look for the file
``/usr/lib/python2.4/site-packages/foo.py``. If that file doesn't exist, it
``/usr/lib/python2.7/site-packages/foo.py``. If that file doesn't exist, it
will try ``/home/username/djcode/foo.py``. Finally, if *that* file doesn't
exist, it will raise ``ImportError``.

Expand Down Expand Up @@ -346,7 +363,7 @@ the URLpattern ``'^$'``, which matches an empty string. For example::
from mysite.views import hello, my_homepage_view

urlpatterns = patterns('',
('^$', my_homepage_view),
url(r'^$', my_homepage_view),
# ...
)

Expand All @@ -358,13 +375,13 @@ more about how Django works. Specifically, when you view your "Hello world"
message by visiting ``http://127.0.0.1:8000/hello/`` in your Web browser, what
does Django do behind the scenes?

It all starts with the *settings file*. When you run
``python manage.py runserver``, the script looks for a file called
``settings.py`` in the same directory as ``manage.py``. This file contains all
sorts of configuration for this particular Django project, all in uppercase:
``TEMPLATE_DIRS``, ``DATABASE_NAME``, etc. The most important setting is called
``ROOT_URLCONF``. ``ROOT_URLCONF`` tells Django which Python module should be
used as the URLconf for this Web site.
It all starts with the *settings file*. When you run ``python manage.py
runserver``, the script looks for a file called ``settings.py`` in the inner
``mysite`` directory. This file contains all sorts of configuration for this
particular Django project, all in uppercase: ``TEMPLATE_DIRS``, ``DATABASES``,
etc. The most important setting is called ``ROOT_URLCONF``. ``ROOT_URLCONF``
tells Django which Python module should be used as the URLconf for this Web
site.

Remember when ``django-admin.py startproject`` created the files
``settings.py`` and ``urls.py``? The autogenerated ``settings.py`` contains a
Expand Down Expand Up @@ -496,12 +513,12 @@ After adding that to ``views.py``, add the URLpattern to ``urls.py`` to tell
Django which URL should handle this view. Something like ``/time/`` would make
sense::

from django.conf.urls.defaults import *
from django.conf.urls.defaults import patterns, include, url
from mysite.views import hello, current_datetime

urlpatterns = patterns('',
('^hello/$', hello),
('^time/$', current_datetime),
url(r'^hello/$', hello),
url(r'^time/$', current_datetime),
)

We've made two changes here. First, we imported the ``current_datetime``
Expand Down Expand Up @@ -531,7 +548,7 @@ interchangeable. If two pieces of code are loosely coupled, then changes made to
one of the pieces will have little or no effect on the other.

Django's URLconfs are a good example of this principle in practice. In a Django
Web application, the URL definitions and the view functions they call are
web application, the URL definitions and the view functions they call are
loosely coupled; that is, the decision of what the URL should be for a given
function, and the implementation of the function itself, reside in two separate
places. This lets you switch out one piece without affecting the other.
Expand All @@ -550,9 +567,9 @@ without having to touch the view code. In this example, our
this technique can come in handy::

urlpatterns = patterns('',
('^hello/$', hello),
('^time/$', current_datetime),
('^another-time-page/$', current_datetime),
url(r'^hello/$', hello),
url(r'^time/$', current_datetime),
url(r'^another-time-page/$', current_datetime),
)

URLconfs and views are loose coupling in action. We'll continue to point out
Expand All @@ -578,11 +595,11 @@ A novice might think to code a separate view function for each hour offset,
which might result in a URLconf like this::

urlpatterns = patterns('',
('^time/$', current_datetime),
('^time/plus/1/$', one_hour_ahead),
('^time/plus/2/$', two_hours_ahead),
('^time/plus/3/$', three_hours_ahead),
('^time/plus/4/$', four_hours_ahead),
url(r'^time/$', current_datetime),
url(r'^time/plus/1/$', one_hour_ahead),
url(r'^time/plus/2/$', two_hours_ahead),
url(r'^time/plus/3/$', three_hours_ahead),
url(r'^time/plus/4/$', four_hours_ahead),
)

Clearly, this line of thought is flawed. Not only would this result in redundant
Expand Down Expand Up @@ -616,7 +633,7 @@ is a regular expression; hence, we can use the regular expression pattern

urlpatterns = patterns('',
# ...
(r'^time/plus/\d+/$', hours_ahead),
url(r'^time/plus/\d+/$', hours_ahead),
# ...
)

Expand All @@ -629,7 +646,7 @@ let's limit it so that the maximum allowed offset is 99 hours. That means we
want to allow either one- or two-digit numbers -- and in regular expression
syntax, that translates into ``\d{1,2}``::

(r'^time/plus/\d{1,2}/$', hours_ahead),
url(r'^time/plus/\d{1,2}/$', hours_ahead),

.. note::

Expand All @@ -638,27 +655,14 @@ syntax, that translates into ``\d{1,2}``::
should support that input. We've curtailed the outlandishness here by
limiting the offset to 99 hours.

One more important detail we've introduced here is that ``r`` character in
front of the regular expression string. This tells Python that the string is a
"raw string" -- its contents should not interpret backslashes. In normal Python
strings, backslashes are used for escaping special characters -- such as in the
string ``'\n'``, which is a one-character string containing a newline. When you
add the ``r`` to make it a raw string, Python does not apply its backslash
escaping -- so, ``r'\n'`` is a two-character string containing a literal
backslash and a lowercase "n". There's a natural collision between Python's
usage of backslashes and the backslashes that are found in regular expressions,
so it's strongly suggested that you use raw strings any time you're defining a
regular expression in Python. From now on, all of the URLpatterns in this book
will be raw strings.

Now that we've designated a wildcard for the URL, we need a way of passing that
wildcard data to the view function, so that we can use a single view function
for any arbitrary hour offset. We do this by placing parentheses around the
data in the URLpattern that we want to save. In the case of our example, we
want to save whatever number was entered in the URL, so let's put parentheses
around the ``\d{1,2}``, like this::

(r'^time/plus/(\d{1,2})/$', hours_ahead),
url(r'^time/plus/(\d{1,2})/$', hours_ahead),

If you're familiar with regular expressions, you'll be right at home here;
we're using parentheses to *capture* data from the matched text.
Expand All @@ -669,33 +673,13 @@ The final URLconf, including our previous two views, looks like this::
from mysite.views import hello, current_datetime, hours_ahead

urlpatterns = patterns('',
(r'^hello/$', hello),
(r'^time/$', current_datetime),
(r'^time/plus/(\d{1,2})/$', hours_ahead),
url(r'^hello/$', hello),
url(r'^time/$', current_datetime),
url(r'^time/plus/(\d{1,2})/$', hours_ahead),
)

With that taken care of, let's write the ``hours_ahead`` view.

.. admonition:: Coding Order

In this example, we wrote the URLpattern first and the view second, but in
the previous examples, we wrote the view first, then the URLpattern. Which
technique is better?

Well, every developer is different.

If you're a big-picture type of person, it may make the most sense to you
to write all of the URLpatterns for your application at the same time, at
the start of your project, and then code up the views. This has the
advantage of giving you a clear to-do list, and it essentially defines the
parameter requirements for the view functions you'll need to write.

If you're more of a bottom-up developer, you might prefer to write the
views first, and then anchor them to URLs afterward. That's OK, too.

In the end, it comes down to which technique fits your brain the best. Both
approaches are valid.

``hours_ahead`` is very similar to the ``current_datetime`` view we wrote
earlier, with a key difference: it takes an extra argument, the number of hours
of offset. Here's the view code::
Expand Down Expand Up @@ -788,6 +772,26 @@ not found" error in this case, just as we saw in the section "A Quick Note
About 404 Errors" earlier. The URL ``http://127.0.0.1:8000/time/plus/`` (with
*no* hour designation) should also throw a 404.

.. admonition:: Coding Order

In this example, we wrote the URLpattern first and the view second, but in
the previous examples, we wrote the view first, then the URLpattern. Which
technique is better?

Well, every developer is different.

If you're a big-picture type of person, it may make the most sense to you
to write all of the URLpatterns for your application at the same time, at
the start of your project, and then code up the views. This has the
advantage of giving you a clear to-do list, and it essentially defines the
parameter requirements for the view functions you'll need to write.

If you're more of a bottom-up developer, you might prefer to write the
views first, and then anchor them to URLs afterward. That's OK, too.

In the end, it comes down to which technique fits your brain the best. Both
approaches are valid.

Django's Pretty Error Pages
===========================

Expand Down