Skip to content

Commit

Permalink
unicode: Merged from trunk up to [5443].
Browse files Browse the repository at this point in the history
git-svn-id: http://code.djangoproject.com/svn/django/branches/unicode@5444 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information
malcolmt committed Jun 9, 2007
1 parent 8d30cdc commit b792044
Show file tree
Hide file tree
Showing 9 changed files with 170 additions and 47 deletions.
56 changes: 40 additions & 16 deletions django/template/defaulttags.py
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from django.conf import settings from django.conf import settings
from django.utils.encoding import smart_str from django.utils.encoding import smart_str
import sys import sys
import re


register = Library() register = Library()


Expand Down Expand Up @@ -62,8 +63,8 @@ def render(self, context):
return '' return ''


class ForNode(Node): class ForNode(Node):
def __init__(self, loopvar, sequence, reversed, nodelist_loop): def __init__(self, loopvars, sequence, reversed, nodelist_loop):
self.loopvar, self.sequence = loopvar, sequence self.loopvars, self.sequence = loopvars, sequence
self.reversed = reversed self.reversed = reversed
self.nodelist_loop = nodelist_loop self.nodelist_loop = nodelist_loop


Expand All @@ -73,7 +74,7 @@ def __repr__(self):
else: else:
reversed = '' reversed = ''
return "<For Node: for %s in %s, tail_len: %d%s>" % \ return "<For Node: for %s in %s, tail_len: %d%s>" % \
(self.loopvar, self.sequence, len(self.nodelist_loop), reversed) (', '.join( self.loopvars ), self.sequence, len(self.nodelist_loop), reversed)


def __iter__(self): def __iter__(self):
for node in self.nodelist_loop: for node in self.nodelist_loop:
Expand Down Expand Up @@ -108,6 +109,7 @@ def reverse(data):
for index in range(len(data)-1, -1, -1): for index in range(len(data)-1, -1, -1):
yield data[index] yield data[index]
values = reverse(values) values = reverse(values)
unpack = len(self.loopvars) > 1
for i, item in enumerate(values): for i, item in enumerate(values):
context['forloop'] = { context['forloop'] = {
# shortcuts for current loop iteration number # shortcuts for current loop iteration number
Expand All @@ -121,9 +123,20 @@ def reverse(data):
'last': (i == len_values - 1), 'last': (i == len_values - 1),
'parentloop': parentloop, 'parentloop': parentloop,
} }
context[self.loopvar] = item if unpack:
# If there are multiple loop variables, unpack the item into them.
context.update(dict(zip(self.loopvars, item)))
else:
context[self.loopvars[0]] = item
for node in self.nodelist_loop: for node in self.nodelist_loop:
nodelist.append(node.render(context)) nodelist.append(node.render(context))
if unpack:
# The loop variables were pushed on to the context so pop them
# off again. This is necessary because the tag lets the length
# of loopvars differ to the length of each set of items and we
# don't want to leave any vars from the previous loop on the
# context.
context.pop()
context.pop() context.pop()
return nodelist.render(context) return nodelist.render(context)


Expand Down Expand Up @@ -487,7 +500,7 @@ def do_filter(parser, token):
nodelist = parser.parse(('endfilter',)) nodelist = parser.parse(('endfilter',))
parser.delete_first_token() parser.delete_first_token()
return FilterNode(filter_expr, nodelist) return FilterNode(filter_expr, nodelist)
filter = register.tag("filter", do_filter) do_filter = register.tag("filter", do_filter)


#@register.tag #@register.tag
def firstof(parser, token): def firstof(parser, token):
Expand Down Expand Up @@ -531,8 +544,14 @@ def do_for(parser, token):
{% endfor %} {% endfor %}
</ul> </ul>
You can also loop over a list in reverse by using You can loop over a list in reverse by using
``{% for obj in list reversed %}``. ``{% for obj in list reversed %}``.
You can also unpack multiple values from a two-dimensional array::
{% for key,value in dict.items %}
{{ key }}: {{ value }}
{% endfor %}
The for loop sets a number of variables available within the loop: The for loop sets a number of variables available within the loop:
Expand All @@ -553,18 +572,23 @@ def do_for(parser, token):
""" """
bits = token.contents.split() bits = token.contents.split()
if len(bits) == 5 and bits[4] != 'reversed': if len(bits) < 4:
raise TemplateSyntaxError, "'for' statements with five words should end in 'reversed': %s" % token.contents raise TemplateSyntaxError, "'for' statements should have at least four words: %s" % token.contents
if len(bits) not in (4, 5):
raise TemplateSyntaxError, "'for' statements should have either four or five words: %s" % token.contents reversed = bits[-1] == 'reversed'
if bits[2] != 'in': in_index = reversed and -3 or -2
raise TemplateSyntaxError, "'for' statement must contain 'in' as the second word: %s" % token.contents if bits[in_index] != 'in':
loopvar = bits[1] raise TemplateSyntaxError, "'for' statements should use the format 'for x in y': %s" % token.contents
sequence = parser.compile_filter(bits[3])
reversed = (len(bits) == 5) loopvars = re.sub(r' *, *', ',', ' '.join(bits[1:in_index])).split(',')
for var in loopvars:
if not var or ' ' in var:
raise TemplateSyntaxError, "'for' tag received an invalid argument: %s" % token.contents

sequence = parser.compile_filter(bits[in_index+1])
nodelist_loop = parser.parse(('endfor',)) nodelist_loop = parser.parse(('endfor',))
parser.delete_first_token() parser.delete_first_token()
return ForNode(loopvar, sequence, reversed, nodelist_loop) return ForNode(loopvars, sequence, reversed, nodelist_loop)
do_for = register.tag("for", do_for) do_for = register.tag("for", do_for)


def do_ifequal(parser, token, negate): def do_ifequal(parser, token, negate):
Expand Down
9 changes: 7 additions & 2 deletions docs/add_ons.txt
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ Django aims to follow Python's `"batteries included" philosophy`_. It ships
with a variety of extra, optional tools that solve common Web-development with a variety of extra, optional tools that solve common Web-development
problems. problems.


This code lives in ``django/contrib`` in the Django distribution. Here's a This code lives in ``django/contrib`` in the Django distribution. This document
rundown of the packages in ``contrib``: gives a rundown of the packages in ``contrib``, along with any dependencies
those packages have.


.. admonition:: Note .. admonition:: Note


Expand All @@ -26,6 +27,8 @@ The automatic Django administrative interface. For more information, see


.. _Tutorial 2: ../tutorial02/ .. _Tutorial 2: ../tutorial02/


Requires the auth_ and contenttypes_ contrib packages to be installed.

auth auth
==== ====


Expand Down Expand Up @@ -144,6 +147,8 @@ See the `flatpages documentation`_.


.. _flatpages documentation: ../flatpages/ .. _flatpages documentation: ../flatpages/


Requires the sites_ contrib package to be installed as well.

localflavor localflavor
=========== ===========


Expand Down
13 changes: 11 additions & 2 deletions docs/db-api.txt
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -134,6 +134,15 @@ the database until you explicitly call ``save()``.


The ``save()`` method has no return value. The ``save()`` method has no return value.


Updating ``ForeignKey`` fields works exactly the same way; simply assign an
object of the right type to the field in question::

joe = Author.objects.create(name="Joe")
entry.author = joe
entry.save()

Django will complain if you try to assign an object of the wrong type.

How Django knows to UPDATE vs. INSERT How Django knows to UPDATE vs. INSERT
------------------------------------- -------------------------------------


Expand Down Expand Up @@ -1229,8 +1238,8 @@ whose ``headline`` contains ``'Lennon'``::


Blog.objects.filter(entry__headline__contains='Lennon') Blog.objects.filter(entry__headline__contains='Lennon')


Escaping parenthesis and underscores in LIKE statements Escaping percent signs and underscores in LIKE statements
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


The field lookups that equate to ``LIKE`` SQL statements (``iexact``, The field lookups that equate to ``LIKE`` SQL statements (``iexact``,
``contains``, ``icontains``, ``startswith``, ``istartswith``, ``endswith`` ``contains``, ``icontains``, ``startswith``, ``istartswith``, ``endswith``
Expand Down
2 changes: 1 addition & 1 deletion docs/fastcgi.txt
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ This is probably the most common case, if you're using Django's admin site::
DocumentRoot /home/user/public_html DocumentRoot /home/user/public_html
Alias /media /home/user/python/django/contrib/admin/media Alias /media /home/user/python/django/contrib/admin/media
RewriteEngine On RewriteEngine On
RewriteRule ^/(media.*)$ /$1 [QSA,L] RewriteRule ^/(media.*)$ /$1 [QSA,L,PT]
RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^/(.*)$ /mysite.fcgi/$1 [QSA,L] RewriteRule ^/(.*)$ /mysite.fcgi/$1 [QSA,L]
</VirtualHost> </VirtualHost>
Expand Down
18 changes: 10 additions & 8 deletions docs/model-api.txt
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -1890,21 +1890,23 @@ rows. Example::
row = cursor.fetchone() row = cursor.fetchone()
return row return row


``connection`` and ``cursor`` simply use the standard `Python DB-API`_. If ``connection`` and ``cursor`` mostly implement the standard `Python DB-API`_
you're not familiar with the Python DB-API, note that the SQL statement in (except when it comes to `transaction handling`_). If you're not familiar with
``cursor.execute()`` uses placeholders, ``"%s"``, rather than adding parameters the Python DB-API, note that the SQL statement in ``cursor.execute()`` uses
directly within the SQL. If you use this technique, the underlying database placeholders, ``"%s"``, rather than adding parameters directly within the SQL.
library will automatically add quotes and escaping to your parameter(s) as If you use this technique, the underlying database library will automatically
necessary. (Also note that Django expects the ``"%s"`` placeholder, *not* the add quotes and escaping to your parameter(s) as necessary. (Also note that
``"?"`` placeholder, which is used by the SQLite Python bindings. This is for Django expects the ``"%s"`` placeholder, *not* the ``"?"`` placeholder, which is
the sake of consistency and sanity.) used by the SQLite Python bindings. This is for the sake of consistency and
sanity.)


A final note: If all you want to do is a custom ``WHERE`` clause, you can just A final note: If all you want to do is a custom ``WHERE`` clause, you can just
just the ``where``, ``tables`` and ``params`` arguments to the standard lookup just the ``where``, ``tables`` and ``params`` arguments to the standard lookup
API. See `Other lookup options`_. API. See `Other lookup options`_.


.. _Python DB-API: http://www.python.org/peps/pep-0249.html .. _Python DB-API: http://www.python.org/peps/pep-0249.html
.. _Other lookup options: ../db-api/#extra-params-select-where-tables .. _Other lookup options: ../db-api/#extra-params-select-where-tables
.. _transaction handling: ../transactions/


Overriding default model methods Overriding default model methods
-------------------------------- --------------------------------
Expand Down
12 changes: 10 additions & 2 deletions docs/modpython.txt
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -51,9 +51,17 @@ whereas ``<Location>`` points at places in the URL structure of a Web site.
``<Directory>`` would be meaningless here. ``<Directory>`` would be meaningless here.


Also, if you've manually altered your ``PYTHONPATH`` to put your Django project Also, if you've manually altered your ``PYTHONPATH`` to put your Django project
on it, you'll need to tell mod_python:: on it, you'll need to tell mod_python:


PythonPath "['/path/to/project'] + sys.path" .. parsed-literal::

<Location "/mysite/">
SetHandler python-program
PythonHandler django.core.handlers.modpython
SetEnv DJANGO_SETTINGS_MODULE mysite.settings
PythonDebug On
**PythonPath "['/path/to/project'] + sys.path"**
</Location>


.. caution:: .. caution::


Expand Down
62 changes: 51 additions & 11 deletions docs/newforms.txt
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -299,12 +299,14 @@ required. In this example, the data dictionary doesn't include a value for the
In this above example, the ``cleaned_data`` value for ``nick_name`` is set to an In this above example, the ``cleaned_data`` value for ``nick_name`` is set to an
empty string, because ``nick_name`` is ``CharField``, and ``CharField``\s treat empty string, because ``nick_name`` is ``CharField``, and ``CharField``\s treat
empty values as an empty string. Each field type knows what its "blank" value empty values as an empty string. Each field type knows what its "blank" value
is -- e.g., for ``DateField``, it's ``None`` instead of the empty string. is -- e.g., for ``DateField``, it's ``None`` instead of the empty string. For
full details on each field's behavior in this case, see the "Empty value" note
for each field in the "Built-in ``Field`` classes" section below.


Behavior of unbound forms Behavior of unbound forms
~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~


It's meaningless to request "clean" data in a form with no data, but, for the It's meaningless to request "cleaned" data in a form with no data, but, for the
record, here's what happens with unbound forms:: record, here's what happens with unbound forms::


>>> f = ContactForm() >>> f = ContactForm()
Expand Down Expand Up @@ -606,8 +608,13 @@ Using forms in views and templates
---------------------------------- ----------------------------------


Let's put this all together and use the ``ContactForm`` example in a Django Let's put this all together and use the ``ContactForm`` example in a Django
view and template. This example view displays the contact form by default and view and template.
validates/processes it if accessed via a POST request::
Simple view example
~~~~~~~~~~~~~~~~~~~

This example view displays the contact form by default and validates/processes
it if accessed via a POST request::


def contact(request): def contact(request):
if request.method == 'POST': if request.method == 'POST':
Expand All @@ -619,12 +626,12 @@ validates/processes it if accessed via a POST request::
form = ContactForm() form = ContactForm()
return render_to_response('contact.html', {'form': form}) return render_to_response('contact.html', {'form': form})


Simple template output Simple template example
~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~


The template, ``contact.html``, is responsible for displaying the form as HTML. The template in the above view example, ``contact.html``, is responsible for
To do this, we can use the techniques outlined in the "Outputting forms as HTML" displaying the form as HTML. To do this, we can use the techniques outlined in
section above. the "Outputting forms as HTML" section above.


The simplest way to display a form's HTML is to use the variable on its own, The simplest way to display a form's HTML is to use the variable on its own,
like this:: like this::
Expand Down Expand Up @@ -677,7 +684,7 @@ The easiest way is to iterate over the form's fields, with


This iteration technique is useful if you want to apply the same HTML This iteration technique is useful if you want to apply the same HTML
formatting to each field, or if you don't know the names of the form fields formatting to each field, or if you don't know the names of the form fields
ahead of time. Note that the fields will be listed in the order in which ahead of time. Note that the fields will be iterated over in the order in which
they're defined in the ``Form`` class. they're defined in the ``Form`` class.


Alternatively, you can arrange the form's fields explicitly, by name. Do that Alternatively, you can arrange the form's fields explicitly, by name. Do that
Expand All @@ -701,7 +708,10 @@ For example::
Subclassing forms Subclassing forms
----------------- -----------------


If you subclass a custom ``Form`` class, the resulting ``Form`` class will If you have multiple ``Form`` classes that share fields, you can use
subclassing to remove redundancy.

When you subclass a custom ``Form`` class, the resulting subclass will
include all fields of the parent class(es), followed by the fields you define include all fields of the parent class(es), followed by the fields you define
in the subclass. in the subclass.


Expand Down Expand Up @@ -1202,6 +1212,36 @@ custom ``Field`` classes. To do this, just create a subclass of
mentioned above (``required``, ``label``, ``initial``, ``widget``, mentioned above (``required``, ``label``, ``initial``, ``widget``,
``help_text``). ``help_text``).


A simple example
~~~~~~~~~~~~~~~~

Here's a simple example of a custom field that validates its input is a string
containing comma-separated e-mail addresses, with at least one address. We'll
keep it simple and assume e-mail validation is contained in a function called
``is_valid_email()``. The full class::

from django import newforms as forms

class MultiEmailField(forms.Field):
def clean(self, value):
emails = value.split(',')
for email in emails:
if not is_valid_email(email):
raise forms.ValidationError('%s is not a valid e-mail address.' % email)
if not emails:
raise forms.ValidationError('Enter at least one e-mail address.')
return emails

Let's alter the ongoing ``ContactForm`` example to demonstrate how you'd use
this in a form. Simply use ``MultiEmailField`` instead of ``forms.EmailField``,
like so::

class ContactForm(forms.Form):
subject = forms.CharField(max_length=100)
message = forms.CharField()
senders = MultiEmailField()
cc_myself = forms.BooleanField()

Generating forms for models Generating forms for models
=========================== ===========================


Expand Down
31 changes: 26 additions & 5 deletions docs/templates.txt
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -91,9 +91,12 @@ Filters can be "chained." The output of one filter is applied to the next.
``{{ text|escape|linebreaks }}`` is a common idiom for escaping text contents, ``{{ text|escape|linebreaks }}`` is a common idiom for escaping text contents,
then converting line breaks to ``<p>`` tags. then converting line breaks to ``<p>`` tags.


Some filters take arguments. A filter argument looks like this: Some filters take arguments. A filter argument looks like this: ``{{
``{{ bio|truncatewords:"30" }}``. This will display the first 30 words of the bio|truncatewords:30 }}``. This will display the first 30 words of the ``bio``
``bio`` variable. Filter arguments always are in double quotes. variable.

Filter arguments that contain spaces must be quoted; for example, to join a list
with commas and spaced you'd use ``{{ list|join:", " }}``.


The `Built-in filter reference`_ below describes all the built-in filters. The `Built-in filter reference`_ below describes all the built-in filters.


Expand Down Expand Up @@ -444,15 +447,33 @@ for
~~~ ~~~


Loop over each item in an array. For example, to display a list of athletes Loop over each item in an array. For example, to display a list of athletes
given ``athlete_list``:: provided in ``athlete_list``::


<ul> <ul>
{% for athlete in athlete_list %} {% for athlete in athlete_list %}
<li>{{ athlete.name }}</li> <li>{{ athlete.name }}</li>
{% endfor %} {% endfor %}
</ul> </ul>


You can also loop over a list in reverse by using ``{% for obj in list reversed %}``. You can loop over a list in reverse by using ``{% for obj in list reversed %}``.

**New in Django development version**
If you need to loop over a list of lists, you can unpack the values
in eachs sub-list into a set of known names. For example, if your context contains
a list of (x,y) coordinates called ``points``, you could use the following
to output the list of points::

{% for x, y in points %}
There is a point at {{ x }},{{ y }}
{% endfor %}

This can also be useful if you need to access the items in a dictionary.
For example, if your context contained a dictionary ``data``, the following
would display the keys and values of the dictionary::

{% for key, value in data.items %}
{{ key }}: {{ value }}
{% endfor %}


The for loop sets a number of variables available within the loop: The for loop sets a number of variables available within the loop:


Expand Down
Loading

0 comments on commit b792044

Please sign in to comment.