1
0
mirror of https://github.com/django/django.git synced 2025-07-04 09:49:12 +00:00

gis: Merged revisions 7280-7353 via svnmerge from trunk.

git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@7354 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Justin Bronn 2008-03-23 17:18:58 +00:00
parent b0a895f9e8
commit 2efc34dc5e
60 changed files with 2258 additions and 1125 deletions

View File

@ -59,6 +59,7 @@ answer newbie questions, and generally made Django that much better:
Arthur <avandorp@gmail.com>
David Avsajanishvili <avsd05@gmail.com>
axiak@mit.edu
Niran Babalola <niran@niran.org>
Morten Bagai <m@bagai.com>
Mikaël Barbero <mikael.barbero nospam at nospam free.fr>
Jiri Barton
@ -145,6 +146,7 @@ answer newbie questions, and generally made Django that much better:
Jorge Gajon <gajon@gajon.org>
gandalf@owca.info
Marc Garcia <marc.garcia@accopensys.com>
Alex Gaynor <alex.gaynor@gmail.com>
Andy Gayton <andy-django@thecablelounge.com>
Baishampayan Ghose
Dimitris Glezos <dimitris@glezos.com>
@ -243,6 +245,7 @@ answer newbie questions, and generally made Django that much better:
michael.mcewan@gmail.com
michal@plovarna.cz
Mikko Hellsing <mikko@sorl.net>
Daniel Lindsley <polarcowz@gmail.com>
Orestis Markou <orestis@orestis.gr>
Slawek Mikula <slawek dot mikula at gmail dot com>
mitakummaa@gmail.com
@ -256,6 +259,7 @@ answer newbie questions, and generally made Django that much better:
Robin Munn <http://www.geekforgod.com/>
Robert Myers <myer0052@gmail.com>
Nebojša Dorđević
Doug Napoleone <doug@dougma.com>
Gopal Narayanan <gopastro@gmail.com>
Fraser Nevett <mail@nevett.org>
Sam Newman <http://www.magpiebrain.com/>
@ -269,6 +273,7 @@ answer newbie questions, and generally made Django that much better:
Barry Pederson <bp@barryp.org>
permonik@mesias.brnonet.cz
petr.marhoun@gmail.com
peter@mymart.com
pgross@thoughtworks.com
phaedo <http://phaedo.cx/>
phil@produxion.net
@ -307,6 +312,7 @@ answer newbie questions, and generally made Django that much better:
serbaut@gmail.com
John Shaffer <jshaffer2112@gmail.com>
Pete Shinners <pete@shinners.org>
Leo Shklovskii
jason.sidabras@gmail.com
Jozko Skrablin <jozko.skrablin@gmail.com>
Ben Slavin <benjamin.slavin@gmail.com>

View File

@ -287,7 +287,7 @@ SESSION_COOKIE_PATH = '/' # The path of the sessio
SESSION_SAVE_EVERY_REQUEST = False # Whether to save the session data on every request.
SESSION_EXPIRE_AT_BROWSER_CLOSE = False # Whether sessions expire when a user closes his browser.
SESSION_ENGINE = 'django.contrib.sessions.backends.db' # The module to store session data
SESSION_FILE_PATH = '/tmp/' # Directory to store session files if using the file session module
SESSION_FILE_PATH = None # Directory to store session files if using the file session module. If set to None the backend will use a sensible default.
#########
# CACHE #

File diff suppressed because it is too large Load Diff

View File

@ -1,27 +1,27 @@
{% extends "admin/base_site.html" %}
{% load i18n %}
{% block breadcrumbs %}<div class="breadcrumbs"><a href="../">Home</a> &rsaquo; Documentation</div>{% endblock %}
{% block title %}Documentation{% endblock %}
{% block content %}
<h1>Documentation</h1>
<div id="content-main">
<h3><a href="tags/">Tags</a></h3>
<p>List of all the template tags and their functions.</p>
<h3><a href="filters/">Filters</a></h3>
<p>Filters are actions which can be applied to variables in a template to alter the output.</p>
<h3><a href="models/">Models</a></h3>
<p>Models are descriptions of all the objects in the system and their associated fields. Each model has a list of fields which can be accessed as template variables.</p>
<h3><a href="views/">Views</a></h3>
<p>Each page on the public site is generated by a view. The view defines which template is used to generate the page and which objects are available to that template.</p>
<h3><a href="bookmarklets/">Bookmarklets</a></h3>
<p>Tools for your browser to quickly access admin functionality.</p>
</div>
{% endblock %}
{% extends "admin/base_site.html" %}
{% load i18n %}
{% block breadcrumbs %}<div class="breadcrumbs"><a href="../">Home</a> &rsaquo; Documentation</div>{% endblock %}
{% block title %}Documentation{% endblock %}
{% block content %}
<h1>Documentation</h1>
<div id="content-main">
<h3><a href="tags/">Tags</a></h3>
<p>List of all the template tags and their functions.</p>
<h3><a href="filters/">Filters</a></h3>
<p>Filters are actions which can be applied to variables in a template to alter the output.</p>
<h3><a href="models/">Models</a></h3>
<p>Models are descriptions of all the objects in the system and their associated fields. Each model has a list of fields which can be accessed as template variables.</p>
<h3><a href="views/">Views</a></h3>
<p>Each page on the public site is generated by a view. The view defines which template is used to generate the page and which objects are available to that template.</p>
<h3><a href="bookmarklets/">Bookmarklets</a></h3>
<p>Tools for your browser to quickly access admin functionality.</p>
</div>
{% endblock %}

View File

@ -1,42 +1,42 @@
{% extends "admin/base_site.html" %}
{% load i18n %}
{% block coltype %}colSM{% endblock %}
{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">Home</a> &rsaquo; <a href="../">Documentation</a> &rsaquo; Views</div>{% endblock %}
{% block title %}Views{% endblock %}
{% block content %}
<h1>View documentation</h1>
{% regroup views|dictsort:"site_id" by site as views_by_site %}
<div id="content-related" class="sidebar">
<div class="module">
<h2>Jump to site</h2>
<ul>
{% for site_views in views_by_site %}
<li><a href="#site{{ site_views.grouper.id }}">{{ site_views.grouper.name }}</a></li>
{% endfor %}
</ul>
</div>
</div>
<div id="content-main">
{% for site_views in views_by_site %}
<div class="module">
<h2 id="site{{ site_views.grouper.id }}">Views by URL on {{ site_views.grouper.name }}</h2>
{% for view in site_views.list|dictsort:"url" %}
{% ifchanged %}
<h3><a href="{{ view.module }}.{{ view.name }}/">{{ view.url|escape }}</a></h3>
<p class="small quiet">View function: {{ view.module }}.{{ view.name }}</p>
<p>{{ view.title }}</p>
<hr />
{% endifchanged %}
{% endfor %}
</div>
{% endfor %}
</div>
{% endblock %}
{% extends "admin/base_site.html" %}
{% load i18n %}
{% block coltype %}colSM{% endblock %}
{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">Home</a> &rsaquo; <a href="../">Documentation</a> &rsaquo; Views</div>{% endblock %}
{% block title %}Views{% endblock %}
{% block content %}
<h1>View documentation</h1>
{% regroup views|dictsort:"site_id" by site as views_by_site %}
<div id="content-related" class="sidebar">
<div class="module">
<h2>Jump to site</h2>
<ul>
{% for site_views in views_by_site %}
<li><a href="#site{{ site_views.grouper.id }}">{{ site_views.grouper.name }}</a></li>
{% endfor %}
</ul>
</div>
</div>
<div id="content-main">
{% for site_views in views_by_site %}
<div class="module">
<h2 id="site{{ site_views.grouper.id }}">Views by URL on {{ site_views.grouper.name }}</h2>
{% for view in site_views.list|dictsort:"url" %}
{% ifchanged %}
<h3><a href="{{ view.module }}.{{ view.name }}/">{{ view.url|escape }}</a></h3>
<p class="small quiet">View function: {{ view.module }}.{{ view.name }}</p>
<p>{{ view.title }}</p>
<hr />
{% endifchanged %}
{% endfor %}
</div>
{% endfor %}
</div>
{% endblock %}

View File

@ -1,246 +1,246 @@
"""
FormWizard class -- implements a multi-page form, validating between each
step and storing the form's state as HTML hidden fields so that no state is
stored on the server side.
"""
from django import newforms as forms
from django.conf import settings
from django.http import Http404
from django.shortcuts import render_to_response
from django.template.context import RequestContext
import cPickle as pickle
import md5
class FormWizard(object):
# Dictionary of extra template context variables.
extra_context = {}
# The HTML (and POST data) field name for the "step" variable.
step_field_name="wizard_step"
# METHODS SUBCLASSES SHOULDN'T OVERRIDE ###################################
def __init__(self, form_list, initial=None):
"form_list should be a list of Form classes (not instances)."
self.form_list = form_list[:]
self.initial = initial or {}
self.step = 0 # A zero-based counter keeping track of which step we're in.
def __repr__(self):
return "step: %d\nform_list: %s\ninitial_data: %s" % (self.step, self.form_list, self.initial)
def get_form(self, step, data=None):
"Helper method that returns the Form instance for the given step."
return self.form_list[step](data, prefix=self.prefix_for_step(step), initial=self.initial.get(step, None))
def num_steps(self):
"Helper method that returns the number of steps."
# You might think we should just set "self.form_list = len(form_list)"
# in __init__(), but this calculation needs to be dynamic, because some
# hook methods might alter self.form_list.
return len(self.form_list)
def __call__(self, request, *args, **kwargs):
"""
Main method that does all the hard work, conforming to the Django view
interface.
"""
if 'extra_context' in kwargs:
self.extra_context.update(kwargs['extra_context'])
current_step = self.determine_step(request, *args, **kwargs)
self.parse_params(request, *args, **kwargs)
# Sanity check.
if current_step >= self.num_steps():
raise Http404('Step %s does not exist' % current_step)
# For each previous step, verify the hash and process.
# TODO: Move "hash_%d" to a method to make it configurable.
for i in range(current_step):
form = self.get_form(i, request.POST)
if request.POST.get("hash_%d" % i, '') != self.security_hash(request, form):
return self.render_hash_failure(request, i)
self.process_step(request, form, i)
# Process the current step. If it's valid, go to the next step or call
# done(), depending on whether any steps remain.
if request.method == 'POST':
form = self.get_form(current_step, request.POST)
else:
form = self.get_form(current_step)
if form.is_valid():
self.process_step(request, form, current_step)
next_step = current_step + 1
# If this was the last step, validate all of the forms one more
# time, as a sanity check, and call done().
num = self.num_steps()
if next_step == num:
final_form_list = [self.get_form(i, request.POST) for i in range(num)]
# Validate all the forms. If any of them fail validation, that
# must mean the validator relied on some other input, such as
# an external Web site.
for i, f in enumerate(final_form_list):
if not f.is_valid():
return self.render_revalidation_failure(request, i, f)
return self.done(request, final_form_list)
# Otherwise, move along to the next step.
else:
form = self.get_form(next_step)
current_step = next_step
return self.render(form, request, current_step)
def render(self, form, request, step, context=None):
"Renders the given Form object, returning an HttpResponse."
old_data = request.POST
prev_fields = []
if old_data:
hidden = forms.HiddenInput()
# Collect all data from previous steps and render it as HTML hidden fields.
for i in range(step):
old_form = self.get_form(i, old_data)
hash_name = 'hash_%s' % i
prev_fields.extend([bf.as_hidden() for bf in old_form])
prev_fields.append(hidden.render(hash_name, old_data.get(hash_name, self.security_hash(request, old_form))))
return self.render_template(request, form, ''.join(prev_fields), step, context)
# METHODS SUBCLASSES MIGHT OVERRIDE IF APPROPRIATE ########################
def prefix_for_step(self, step):
"Given the step, returns a Form prefix to use."
return str(step)
def render_hash_failure(self, request, step):
"""
Hook for rendering a template if a hash check failed.
step is the step that failed. Any previous step is guaranteed to be
valid.
This default implementation simply renders the form for the given step,
but subclasses may want to display an error message, etc.
"""
return self.render(self.get_form(step), request, step, context={'wizard_error': 'We apologize, but your form has expired. Please continue filling out the form from this page.'})
def render_revalidation_failure(self, request, step, form):
"""
Hook for rendering a template if final revalidation failed.
It is highly unlikely that this point would ever be reached, but See
the comment in __call__() for an explanation.
"""
return self.render(form, request, step)
def security_hash(self, request, form):
"""
Calculates the security hash for the given HttpRequest and Form instances.
This creates a list of the form field names/values in a deterministic
order, pickles the result with the SECRET_KEY setting and takes an md5
hash of that.
Subclasses may want to take into account request-specific information,
such as the IP address.
"""
data = [(bf.name, bf.data or '') for bf in form] + [settings.SECRET_KEY]
# Use HIGHEST_PROTOCOL because it's the most efficient. It requires
# Python 2.3, but Django requires 2.3 anyway, so that's OK.
pickled = pickle.dumps(data, protocol=pickle.HIGHEST_PROTOCOL)
return md5.new(pickled).hexdigest()
def determine_step(self, request, *args, **kwargs):
"""
Given the request object and whatever *args and **kwargs were passed to
__call__(), returns the current step (which is zero-based).
Note that the result should not be trusted. It may even be a completely
invalid number. It's not the job of this method to validate it.
"""
if not request.POST:
return 0
try:
step = int(request.POST.get(self.step_field_name, 0))
except ValueError:
return 0
return step
def parse_params(self, request, *args, **kwargs):
"""
Hook for setting some state, given the request object and whatever
*args and **kwargs were passed to __call__(), sets some state.
This is called at the beginning of __call__().
"""
pass
def get_template(self, step):
"""
Hook for specifying the name of the template to use for a given step.
Note that this can return a tuple of template names if you'd like to
use the template system's select_template() hook.
"""
return 'forms/wizard.html'
def render_template(self, request, form, previous_fields, step, context=None):
"""
Renders the template for the given step, returning an HttpResponse object.
Override this method if you want to add a custom context, return a
different MIME type, etc. If you only need to override the template
name, use get_template() instead.
The template will be rendered with the following context:
step_field -- The name of the hidden field containing the step.
step0 -- The current step (zero-based).
step -- The current step (one-based).
step_count -- The total number of steps.
form -- The Form instance for the current step (either empty
or with errors).
previous_fields -- A string representing every previous data field,
plus hashes for completed forms, all in the form of
hidden fields. Note that you'll need to run this
through the "safe" template filter, to prevent
auto-escaping, because it's raw HTML.
"""
context = context or {}
context.update(self.extra_context)
return render_to_response(self.get_template(self.step), dict(context,
step_field=self.step_field_name,
step0=step,
step=step + 1,
step_count=self.num_steps(),
form=form,
previous_fields=previous_fields
), context_instance=RequestContext(request))
def process_step(self, request, form, step):
"""
Hook for modifying the FormWizard's internal state, given a fully
validated Form object. The Form is guaranteed to have clean, valid
data.
This method should *not* modify any of that data. Rather, it might want
to set self.extra_context or dynamically alter self.form_list, based on
previously submitted forms.
Note that this method is called every time a page is rendered for *all*
submitted steps.
"""
pass
# METHODS SUBCLASSES MUST OVERRIDE ########################################
def done(self, request, form_list):
"""
Hook for doing something with the validated data. This is responsible
for the final processing.
form_list is a list of Form instances, each containing clean, valid
data.
"""
raise NotImplementedError("Your %s class has not defined a done() method, which is required." % self.__class__.__name__)
"""
FormWizard class -- implements a multi-page form, validating between each
step and storing the form's state as HTML hidden fields so that no state is
stored on the server side.
"""
from django import newforms as forms
from django.conf import settings
from django.http import Http404
from django.shortcuts import render_to_response
from django.template.context import RequestContext
import cPickle as pickle
import md5
class FormWizard(object):
# Dictionary of extra template context variables.
extra_context = {}
# The HTML (and POST data) field name for the "step" variable.
step_field_name="wizard_step"
# METHODS SUBCLASSES SHOULDN'T OVERRIDE ###################################
def __init__(self, form_list, initial=None):
"form_list should be a list of Form classes (not instances)."
self.form_list = form_list[:]
self.initial = initial or {}
self.step = 0 # A zero-based counter keeping track of which step we're in.
def __repr__(self):
return "step: %d\nform_list: %s\ninitial_data: %s" % (self.step, self.form_list, self.initial)
def get_form(self, step, data=None):
"Helper method that returns the Form instance for the given step."
return self.form_list[step](data, prefix=self.prefix_for_step(step), initial=self.initial.get(step, None))
def num_steps(self):
"Helper method that returns the number of steps."
# You might think we should just set "self.form_list = len(form_list)"
# in __init__(), but this calculation needs to be dynamic, because some
# hook methods might alter self.form_list.
return len(self.form_list)
def __call__(self, request, *args, **kwargs):
"""
Main method that does all the hard work, conforming to the Django view
interface.
"""
if 'extra_context' in kwargs:
self.extra_context.update(kwargs['extra_context'])
current_step = self.determine_step(request, *args, **kwargs)
self.parse_params(request, *args, **kwargs)
# Sanity check.
if current_step >= self.num_steps():
raise Http404('Step %s does not exist' % current_step)
# For each previous step, verify the hash and process.
# TODO: Move "hash_%d" to a method to make it configurable.
for i in range(current_step):
form = self.get_form(i, request.POST)
if request.POST.get("hash_%d" % i, '') != self.security_hash(request, form):
return self.render_hash_failure(request, i)
self.process_step(request, form, i)
# Process the current step. If it's valid, go to the next step or call
# done(), depending on whether any steps remain.
if request.method == 'POST':
form = self.get_form(current_step, request.POST)
else:
form = self.get_form(current_step)
if form.is_valid():
self.process_step(request, form, current_step)
next_step = current_step + 1
# If this was the last step, validate all of the forms one more
# time, as a sanity check, and call done().
num = self.num_steps()
if next_step == num:
final_form_list = [self.get_form(i, request.POST) for i in range(num)]
# Validate all the forms. If any of them fail validation, that
# must mean the validator relied on some other input, such as
# an external Web site.
for i, f in enumerate(final_form_list):
if not f.is_valid():
return self.render_revalidation_failure(request, i, f)
return self.done(request, final_form_list)
# Otherwise, move along to the next step.
else:
form = self.get_form(next_step)
current_step = next_step
return self.render(form, request, current_step)
def render(self, form, request, step, context=None):
"Renders the given Form object, returning an HttpResponse."
old_data = request.POST
prev_fields = []
if old_data:
hidden = forms.HiddenInput()
# Collect all data from previous steps and render it as HTML hidden fields.
for i in range(step):
old_form = self.get_form(i, old_data)
hash_name = 'hash_%s' % i
prev_fields.extend([bf.as_hidden() for bf in old_form])
prev_fields.append(hidden.render(hash_name, old_data.get(hash_name, self.security_hash(request, old_form))))
return self.render_template(request, form, ''.join(prev_fields), step, context)
# METHODS SUBCLASSES MIGHT OVERRIDE IF APPROPRIATE ########################
def prefix_for_step(self, step):
"Given the step, returns a Form prefix to use."
return str(step)
def render_hash_failure(self, request, step):
"""
Hook for rendering a template if a hash check failed.
step is the step that failed. Any previous step is guaranteed to be
valid.
This default implementation simply renders the form for the given step,
but subclasses may want to display an error message, etc.
"""
return self.render(self.get_form(step), request, step, context={'wizard_error': 'We apologize, but your form has expired. Please continue filling out the form from this page.'})
def render_revalidation_failure(self, request, step, form):
"""
Hook for rendering a template if final revalidation failed.
It is highly unlikely that this point would ever be reached, but See
the comment in __call__() for an explanation.
"""
return self.render(form, request, step)
def security_hash(self, request, form):
"""
Calculates the security hash for the given HttpRequest and Form instances.
This creates a list of the form field names/values in a deterministic
order, pickles the result with the SECRET_KEY setting and takes an md5
hash of that.
Subclasses may want to take into account request-specific information,
such as the IP address.
"""
data = [(bf.name, bf.data or '') for bf in form] + [settings.SECRET_KEY]
# Use HIGHEST_PROTOCOL because it's the most efficient. It requires
# Python 2.3, but Django requires 2.3 anyway, so that's OK.
pickled = pickle.dumps(data, protocol=pickle.HIGHEST_PROTOCOL)
return md5.new(pickled).hexdigest()
def determine_step(self, request, *args, **kwargs):
"""
Given the request object and whatever *args and **kwargs were passed to
__call__(), returns the current step (which is zero-based).
Note that the result should not be trusted. It may even be a completely
invalid number. It's not the job of this method to validate it.
"""
if not request.POST:
return 0
try:
step = int(request.POST.get(self.step_field_name, 0))
except ValueError:
return 0
return step
def parse_params(self, request, *args, **kwargs):
"""
Hook for setting some state, given the request object and whatever
*args and **kwargs were passed to __call__(), sets some state.
This is called at the beginning of __call__().
"""
pass
def get_template(self, step):
"""
Hook for specifying the name of the template to use for a given step.
Note that this can return a tuple of template names if you'd like to
use the template system's select_template() hook.
"""
return 'forms/wizard.html'
def render_template(self, request, form, previous_fields, step, context=None):
"""
Renders the template for the given step, returning an HttpResponse object.
Override this method if you want to add a custom context, return a
different MIME type, etc. If you only need to override the template
name, use get_template() instead.
The template will be rendered with the following context:
step_field -- The name of the hidden field containing the step.
step0 -- The current step (zero-based).
step -- The current step (one-based).
step_count -- The total number of steps.
form -- The Form instance for the current step (either empty
or with errors).
previous_fields -- A string representing every previous data field,
plus hashes for completed forms, all in the form of
hidden fields. Note that you'll need to run this
through the "safe" template filter, to prevent
auto-escaping, because it's raw HTML.
"""
context = context or {}
context.update(self.extra_context)
return render_to_response(self.get_template(self.step), dict(context,
step_field=self.step_field_name,
step0=step,
step=step + 1,
step_count=self.num_steps(),
form=form,
previous_fields=previous_fields
), context_instance=RequestContext(request))
def process_step(self, request, form, step):
"""
Hook for modifying the FormWizard's internal state, given a fully
validated Form object. The Form is guaranteed to have clean, valid
data.
This method should *not* modify any of that data. Rather, it might want
to set self.extra_context or dynamically alter self.form_list, based on
previously submitted forms.
Note that this method is called every time a page is rendered for *all*
submitted steps.
"""
pass
# METHODS SUBCLASSES MUST OVERRIDE ########################################
def done(self, request, form_list):
"""
Hook for doing something with the validated data. This is responsible
for the final processing.
form_list is a list of Form instances, each containing clean, valid
data.
"""
raise NotImplementedError("Your %s class has not defined a done() method, which is required." % self.__class__.__name__)

View File

@ -9,7 +9,9 @@ class SessionStore(SessionBase):
Implements a file based session store.
"""
def __init__(self, session_key=None):
self.storage_path = getattr(settings, "SESSION_FILE_PATH", tempfile.gettempdir())
self.storage_path = getattr(settings, "SESSION_FILE_PATH", None)
if not self.storage_path:
self.storage_path = tempfile.gettempdir()
# Make sure the storage path is valid.
if not os.path.isdir(self.storage_path):

View File

@ -3,7 +3,8 @@ from django.template import Context, loader, Template, TemplateDoesNotExist
from django.contrib.sites.models import Site, RequestSite
from django.utils import feedgenerator
from django.utils.encoding import smart_unicode, iri_to_uri
from django.conf import settings
from django.conf import settings
from django.template import RequestContext
def add_domain(domain, url):
if not (url.startswith('http://') or url.startswith('https://')):
@ -55,18 +56,23 @@ class Feed(object):
return attr()
return attr
def get_object(self, bits):
return None
def get_feed(self, url=None):
"""
Returns a feedgenerator.DefaultFeed object, fully populated, for
this feed. Raises FeedDoesNotExist for invalid parameters.
"""
if url:
try:
obj = self.get_object(url.split('/'))
except (AttributeError, ObjectDoesNotExist):
raise FeedDoesNotExist
bits = url.split('/')
else:
obj = None
bits = []
try:
obj = self.get_object(bits)
except ObjectDoesNotExist:
raise FeedDoesNotExist
if Site._meta.installed:
current_site = Site.objects.get_current()
@ -119,9 +125,9 @@ class Feed(object):
else:
author_email = author_link = None
feed.add_item(
title = title_tmp.render(Context({'obj': item, 'site': current_site})),
title = title_tmp.render(RequestContext(self.request, {'obj': item, 'site': current_site})),
link = link,
description = description_tmp.render(Context({'obj': item, 'site': current_site})),
description = description_tmp.render(RequestContext(self.request, {'obj': item, 'site': current_site})),
unique_id = self.__get_dynamic_attr('item_guid', item, link),
enclosure = enc,
pubdate = self.__get_dynamic_attr('item_pubdate', item),

View File

@ -2,20 +2,21 @@
Tools for sending email.
"""
from django.conf import settings
from django.utils.encoding import smart_str, force_unicode
from email import Charset, Encoders
from email.MIMEText import MIMEText
from email.MIMEMultipart import MIMEMultipart
from email.MIMEBase import MIMEBase
from email.Header import Header
from email.Utils import formatdate, parseaddr, formataddr
import mimetypes
import os
import smtplib
import socket
import time
import random
from email import Charset, Encoders
from email.MIMEText import MIMEText
from email.MIMEMultipart import MIMEMultipart
from email.MIMEBase import MIMEBase
from email.Header import Header
from email.Utils import formatdate, parseaddr, formataddr
from django.conf import settings
from django.utils.encoding import smart_str, force_unicode
# Don't BASE64-encode UTF-8 messages so that we avoid unwanted attention from
# some spam filters.
@ -38,8 +39,9 @@ class CachedDnsName(object):
DNS_NAME = CachedDnsName()
# Copied from Python standard library and modified to used the cached hostname
# for performance.
# Copied from Python standard library, with the following modifications:
# * Used cached hostname for performance.
# * Added try/except to support lack of getpid() in Jython (#5496).
def make_msgid(idstring=None):
"""Returns a string suitable for RFC 2822 compliant Message-ID, e.g:
@ -53,7 +55,7 @@ def make_msgid(idstring=None):
try:
pid = os.getpid()
except AttributeError:
# Not getpid() in Jython, for example.
# No getpid() in Jython, for example.
pid = 1
randint = random.randrange(100000)
if idstring is None:
@ -68,7 +70,7 @@ class BadHeaderError(ValueError):
pass
def forbid_multi_line_headers(name, val):
"Forbids multi-line headers, to prevent header injection."
"""Forbids multi-line headers, to prevent header injection."""
if '\n' in val or '\r' in val:
raise BadHeaderError("Header values can't contain newlines (got %r for header %r)" % (val, name))
try:
@ -101,7 +103,7 @@ class SMTPConnection(object):
"""
def __init__(self, host=None, port=None, username=None, password=None,
use_tls=None, fail_silently=False):
use_tls=None, fail_silently=False):
self.host = host or settings.EMAIL_HOST
self.port = port or settings.EMAIL_PORT
self.username = username or settings.EMAIL_HOST_USER
@ -112,14 +114,17 @@ class SMTPConnection(object):
def open(self):
"""
Ensure we have a connection to the email server. Returns whether or not
a new connection was required.
Ensures we have a connection to the email server. Returns whether or
not a new connection was required (True or False).
"""
if self.connection:
# Nothing to do if the connection is already open.
return False
try:
self.connection = smtplib.SMTP(self.host, self.port)
# If local_hostname is not specified, socket.getfqdn() gets used.
# For performance, we use the cached FQDN for local_hostname.
self.connection = smtplib.SMTP(self.host, self.port,
local_hostname=DNS_NAME.get_fqdn())
if self.use_tls:
self.connection.ehlo()
self.connection.starttls()
@ -132,7 +137,7 @@ class SMTPConnection(object):
raise
def close(self):
"""Close the connection to the email server."""
"""Closes the connection to the email server."""
try:
try:
self.connection.quit()
@ -149,7 +154,7 @@ class SMTPConnection(object):
def send_messages(self, email_messages):
"""
Send one or more EmailMessage objects and return the number of email
Sends one or more EmailMessage objects and returns the number of email
messages sent.
"""
if not email_messages:
@ -192,7 +197,7 @@ class EmailMessage(object):
def __init__(self, subject='', body='', from_email=None, to=None, bcc=None,
connection=None, attachments=None, headers=None):
"""
Initialise a single email message (which can be sent to multiple
Initialize a single email message (which can be sent to multiple
recipients).
All strings used to create the message can be unicode strings (or UTF-8
@ -221,7 +226,8 @@ class EmailMessage(object):
def message(self):
encoding = self.encoding or settings.DEFAULT_CHARSET
msg = SafeMIMEText(smart_str(self.body, settings.DEFAULT_CHARSET), self.content_subtype, encoding)
msg = SafeMIMEText(smart_str(self.body, settings.DEFAULT_CHARSET),
self.content_subtype, encoding)
if self.attachments:
body_msg = msg
msg = SafeMIMEMultipart(_subtype=self.multipart_subtype)
@ -237,8 +243,6 @@ class EmailMessage(object):
msg['To'] = ', '.join(self.to)
msg['Date'] = formatdate()
msg['Message-ID'] = make_msgid()
if self.bcc:
msg['Bcc'] = ', '.join(self.bcc)
for name, value in self.extra_headers.items():
msg[name] = value
return msg
@ -251,7 +255,7 @@ class EmailMessage(object):
return self.to + self.bcc
def send(self, fail_silently=False):
"""Send the email message."""
"""Sends the email message."""
return self.get_connection(fail_silently).send_messages([self])
def attach(self, filename=None, content=None, mimetype=None):
@ -278,7 +282,7 @@ class EmailMessage(object):
def _create_attachment(self, filename, content, mimetype=None):
"""
Convert the filename, content, mimetype triple into a MIME attachment
Converts the filename, content, mimetype triple into a MIME attachment
object.
"""
if mimetype is None:
@ -295,7 +299,8 @@ class EmailMessage(object):
attachment.set_payload(content)
Encoders.encode_base64(attachment)
if filename:
attachment.add_header('Content-Disposition', 'attachment', filename=filename)
attachment.add_header('Content-Disposition', 'attachment',
filename=filename)
return attachment
class EmailMultiAlternatives(EmailMessage):
@ -310,7 +315,8 @@ class EmailMultiAlternatives(EmailMessage):
"""Attach an alternative content representation."""
self.attach(content=content, mimetype=mimetype)
def send_mail(subject, message, from_email, recipient_list, fail_silently=False, auth_user=None, auth_password=None):
def send_mail(subject, message, from_email, recipient_list,
fail_silently=False, auth_user=None, auth_password=None):
"""
Easy wrapper for sending a single message to a recipient list. All members
of the recipient list will see the other recipients in the 'To' field.
@ -322,10 +328,12 @@ def send_mail(subject, message, from_email, recipient_list, fail_silently=False,
functionality should use the EmailMessage class directly.
"""
connection = SMTPConnection(username=auth_user, password=auth_password,
fail_silently=fail_silently)
return EmailMessage(subject, message, from_email, recipient_list, connection=connection).send()
fail_silently=fail_silently)
return EmailMessage(subject, message, from_email, recipient_list,
connection=connection).send()
def send_mass_mail(datatuple, fail_silently=False, auth_user=None, auth_password=None):
def send_mass_mail(datatuple, fail_silently=False, auth_user=None,
auth_password=None):
"""
Given a datatuple of (subject, message, from_email, recipient_list), sends
each message to each recipient list. Returns the number of e-mails sent.
@ -339,19 +347,19 @@ def send_mass_mail(datatuple, fail_silently=False, auth_user=None, auth_password
functionality should use the EmailMessage class directly.
"""
connection = SMTPConnection(username=auth_user, password=auth_password,
fail_silently=fail_silently)
messages = [EmailMessage(subject, message, sender, recipient) for subject, message, sender, recipient in datatuple]
fail_silently=fail_silently)
messages = [EmailMessage(subject, message, sender, recipient)
for subject, message, sender, recipient in datatuple]
return connection.send_messages(messages)
def mail_admins(subject, message, fail_silently=False):
"Sends a message to the admins, as defined by the ADMINS setting."
"""Sends a message to the admins, as defined by the ADMINS setting."""
EmailMessage(settings.EMAIL_SUBJECT_PREFIX + subject, message,
settings.SERVER_EMAIL, [a[1] for a in
settings.ADMINS]).send(fail_silently=fail_silently)
settings.SERVER_EMAIL, [a[1] for a in settings.ADMINS]
).send(fail_silently=fail_silently)
def mail_managers(subject, message, fail_silently=False):
"Sends a message to the managers, as defined by the MANAGERS setting."
"""Sends a message to the managers, as defined by the MANAGERS setting."""
EmailMessage(settings.EMAIL_SUBJECT_PREFIX + subject, message,
settings.SERVER_EMAIL, [a[1] for a in
settings.MANAGERS]).send(fail_silently=fail_silently)
settings.SERVER_EMAIL, [a[1] for a in settings.MANAGERS]
).send(fail_silently=fail_silently)

View File

@ -243,7 +243,7 @@ def setup_environ(settings_mod):
# way. For example, if this file (manage.py) lives in a directory
# "myproject", this code would add "/path/to/myproject" to sys.path.
project_directory, settings_filename = os.path.split(settings_mod.__file__)
if not project_directory:
if project_directory == os.curdir or not project_directory:
project_directory = os.getcwd()
project_name = os.path.basename(project_directory)
settings_name = os.path.splitext(settings_filename)[0]

View File

@ -20,8 +20,13 @@ class Command(LabelCommand):
# the parent directory.
directory = os.getcwd()
if project_name in INVALID_PROJECT_NAMES:
raise CommandError("%r conflicts with the name of an existing Python module and cannot be used as a project name. Please try another name." % project_name)
try:
proj_name = __import__(project_name)
if proj_name:
raise CommandError("%r conflicts with the name of an existing Python module and cannot be used as a project name. Please try another name." % project_name)
except ImportError:
if project_name in INVALID_PROJECT_NAMES:
raise CommandError("%r contains an invalid project name. Please try another name." % project_name)
copy_helper(self.style, 'project', project_name, directory)

View File

@ -1,46 +1,149 @@
class InvalidPage(Exception):
pass
class ObjectPaginator(object):
"""
This class makes pagination easy. Feed it a QuerySet or list, plus the number
of objects you want on each page. Then read the hits and pages properties to
see how many pages it involves. Call get_page with a page number (starting
at 0) to get back a list of objects for that page.
class Paginator(object):
def __init__(self, object_list, per_page, orphans=0, allow_empty_first_page=True):
self.object_list = object_list
self.per_page = per_page
self.orphans = orphans
self.allow_empty_first_page = allow_empty_first_page
self._num_pages = self._count = None
Finally, check if a page number has a next/prev page using
has_next_page(page_number) and has_previous_page(page_number).
Use orphans to avoid small final pages. For example:
13 records, num_per_page=10, orphans=2 --> pages==2, len(self.get_page(0))==10
12 records, num_per_page=10, orphans=2 --> pages==1, len(self.get_page(0))==12
def validate_number(self, number):
"Validates the given 1-based page number."
try:
number = int(number)
except ValueError:
raise InvalidPage('That page number is not an integer')
if number < 1:
raise InvalidPage('That page number is less than 1')
if number > self.num_pages:
if number == 1 and self.allow_empty_first_page:
pass
else:
raise InvalidPage('That page contains no results')
return number
def page(self, number):
"Returns a Page object for the given 1-based page number."
number = self.validate_number(number)
bottom = (number - 1) * self.per_page
top = bottom + self.per_page
if top + self.orphans >= self.count:
top = self.count
return Page(self.object_list[bottom:top], number, self)
def _get_count(self):
"Returns the total number of objects, across all pages."
if self._count is None:
self._count = len(self.object_list)
return self._count
count = property(_get_count)
def _get_num_pages(self):
"Returns the total number of pages."
if self._num_pages is None:
hits = self.count - 1 - self.orphans
if hits < 1:
hits = 0
if hits == 0 and not self.allow_empty_first_page:
self._num_pages = 0
else:
self._num_pages = hits // self.per_page + 1
return self._num_pages
num_pages = property(_get_num_pages)
def _get_page_range(self):
"""
Returns a 1-based range of pages for iterating through within
a template for loop.
"""
return range(1, self.num_pages + 1)
page_range = property(_get_page_range)
class QuerySetPaginator(Paginator):
"""
Like Paginator, but works on QuerySets.
"""
def _get_count(self):
if self._count is None:
self._count = self.object_list.count()
return self._count
count = property(_get_count)
class Page(object):
def __init__(self, object_list, number, paginator):
self.object_list = object_list
self.number = number
self.paginator = paginator
def __repr__(self):
return '<Page %s of %s>' % (self.number, self.paginator.num_pages)
def has_next(self):
return self.number < self.paginator.num_pages
def has_previous(self):
return self.number > 1
def has_other_pages(self):
return self.has_previous() or self.has_next()
def next_page_number(self):
return self.number + 1
def previous_page_number(self):
return self.number - 1
def start_index(self):
"""
Returns the 1-based index of the first object on this page,
relative to total objects in the paginator.
"""
return (self.paginator.per_page * (self.number - 1)) + 1
def end_index(self):
"""
Returns the 1-based index of the last object on this page,
relative to total objects found (hits).
"""
if self.number == self.paginator.num_pages:
return self.paginator.count
return self.number * self.paginator.per_page
class ObjectPaginator(Paginator):
"""
Legacy ObjectPaginator class, for backwards compatibility.
Note that each method on this class that takes page_number expects a
zero-based page number, whereas the new API (Paginator/Page) uses one-based
page numbers.
"""
def __init__(self, query_set, num_per_page, orphans=0):
Paginator.__init__(self, query_set, num_per_page, orphans)
import warnings
warnings.warn("The ObjectPaginator is deprecated. Use django.core.paginator.Paginator instead.", DeprecationWarning)
# Keep these attributes around for backwards compatibility.
self.query_set = query_set
self.num_per_page = num_per_page
self.orphans = orphans
self._hits = self._pages = None
self._page_range = None
def validate_page_number(self, page_number):
try:
page_number = int(page_number)
page_number = int(page_number) + 1
except ValueError:
raise InvalidPage
if page_number < 0 or page_number > self.pages - 1:
raise InvalidPage
return page_number
return self.validate_number(page_number)
def get_page(self, page_number):
page_number = self.validate_page_number(page_number)
bottom = page_number * self.num_per_page
top = bottom + self.num_per_page
if top + self.orphans >= self.hits:
top = self.hits
return self.query_set[bottom:top]
try:
page_number = int(page_number) + 1
except ValueError:
raise InvalidPage
return self.page(page_number).object_list
def has_next_page(self, page_number):
"Does page $page_number have a 'next' page?"
return page_number < self.pages - 1
def has_previous_page(self, page_number):
@ -52,7 +155,7 @@ class ObjectPaginator(object):
relative to total objects found (hits).
"""
page_number = self.validate_page_number(page_number)
return (self.num_per_page * page_number) + 1
return (self.num_per_page * (page_number - 1)) + 1
def last_on_page(self, page_number):
"""
@ -60,40 +163,23 @@ class ObjectPaginator(object):
relative to total objects found (hits).
"""
page_number = self.validate_page_number(page_number)
page_number += 1 # 1-base
if page_number == self.pages:
return self.hits
if page_number == self.num_pages:
return self.count
return page_number * self.num_per_page
def _get_hits(self):
if self._hits is None:
# Try .count() or fall back to len().
def _get_count(self):
# The old API allowed for self.object_list to be either a QuerySet or a
# list. Here, we handle both.
if self._count is None:
try:
self._hits = int(self.query_set.count())
except (AttributeError, TypeError, ValueError):
# AttributeError if query_set has no object count.
# TypeError if query_set.count() required arguments.
# ValueError if int() fails.
self._hits = len(self.query_set)
return self._hits
self._count = self.object_list.count()
except TypeError:
self._count = len(self.object_list)
return self._count
count = property(_get_count)
def _get_pages(self):
if self._pages is None:
hits = (self.hits - 1 - self.orphans)
if hits < 1:
hits = 0
self._pages = hits // self.num_per_page + 1
return self._pages
def _get_page_range(self):
"""
Returns a 1-based range of pages for iterating through within
a template for loop.
"""
if self._page_range is None:
self._page_range = range(1, self.pages + 1)
return self._page_range
# The old API called it "hits" instead of "count".
hits = count
hits = property(_get_hits)
pages = property(_get_pages)
page_range = property(_get_page_range)
# The old API called it "pages" instead of "num_pages".
pages = Paginator.num_pages

View File

@ -22,10 +22,10 @@ class Serializer(object):
Abstract serializer base class.
"""
# Indicates if the implemented serializer is only available for
# Indicates if the implemented serializer is only available for
# internal Django use.
internal_use_only = False
def serialize(self, queryset, **options):
"""
Serialize a queryset.
@ -60,8 +60,6 @@ class Serializer(object):
"""
if isinstance(field, models.DateTimeField):
value = getattr(obj, field.name).strftime("%Y-%m-%d %H:%M:%S")
elif isinstance(field, models.FileField):
value = getattr(obj, "get_%s_url" % field.name, lambda: None)()
else:
value = field.flatten_data(follow=None, obj=obj).get(field.name, "")
return smart_unicode(value)
@ -162,9 +160,9 @@ class DeserializedObject(object):
return "<DeserializedObject: %s>" % smart_str(self.object)
def save(self, save_m2m=True):
# Call save on the Model baseclass directly. This bypasses any
# Call save on the Model baseclass directly. This bypasses any
# model-defined save. The save is also forced to be raw.
# This ensures that the data that is deserialized is literally
# This ensures that the data that is deserialized is literally
# what came from the file, not post-processed by pre_save/save
# methods.
models.Model.save(self.object, raw=True)

View File

@ -37,7 +37,9 @@ Optional Fcgi settings: (setting=value)
maxchildren=NUMBER hard limit number of processes / threads
daemonize=BOOL whether to detach from terminal.
pidfile=FILE write the spawned process-id to this file.
workdir=DIRECTORY change to this directory when daemonizing
workdir=DIRECTORY change to this directory when daemonizing.
outlog=FILE write stdout to this file.
errlog=FILE write stderr to this file.
Examples:
Run a "standard" fastcgi process on a file-descriptor
@ -69,6 +71,8 @@ FASTCGI_OPTIONS = {
'minspare': 2,
'maxchildren': 50,
'maxrequests': 0,
'outlog': None,
'errlog': None,
}
def fastcgi_help(message=None):
@ -150,9 +154,15 @@ def runfastcgi(argset=[], **kwargs):
else:
return fastcgi_help("ERROR: Invalid option for daemonize parameter.")
daemon_kwargs = {}
if options['outlog']:
daemon_kwargs['out_log'] = options['outlog']
if options['errlog']:
daemon_kwargs['err_log'] = options['errlog']
if daemonize:
from django.utils.daemonize import become_daemon
become_daemon(our_home_dir=options["workdir"])
become_daemon(our_home_dir=options["workdir"], **daemon_kwargs)
if options["pidfile"]:
fp = open(options["pidfile"], "w")

View File

@ -846,6 +846,16 @@ class FilePathField(Field):
self.path, self.match, self.recursive = path, match, recursive
kwargs['max_length'] = kwargs.get('max_length', 100)
Field.__init__(self, verbose_name, name, **kwargs)
def formfield(self, **kwargs):
defaults = {
'path': self.path,
'match': self.match,
'recursive': self.recursive,
'form_class': forms.FilePathField,
}
defaults.update(kwargs)
return super(FilePathField, self).formfield(**defaults)
def get_manipulator_field_objs(self):
return [curry(oldforms.FilePathField, path=self.path, match=self.match, recursive=self.recursive)]

View File

@ -548,6 +548,13 @@ class ForeignKey(RelatedField, Field):
params['choices'] = self.get_choices_default()
return field_objs, params
def get_default(self):
"Here we check if the default value is an object and return the to_field if so."
field_default = super(ForeignKey, self).get_default()
if isinstance(field_default, self.rel.to):
return getattr(field_default, self.rel.get_related_field().attname)
return field_default
def get_manipulator_field_objs(self):
rel_field = self.rel.get_related_field()
if self.rel.raw_id_admin and not isinstance(rel_field, AutoField):

View File

@ -1,5 +1,5 @@
from django.conf import settings
from django.db import connection, transaction
from django.db import connection, transaction, IntegrityError
from django.db.models.fields import DateField, FieldDoesNotExist
from django.db.models import signals, loading
from django.dispatch import dispatcher
@ -285,11 +285,14 @@ class _QuerySet(object):
try:
return self.get(**kwargs), False
except self.model.DoesNotExist:
params = dict([(k, v) for k, v in kwargs.items() if '__' not in k])
params.update(defaults)
obj = self.model(**params)
obj.save()
return obj, True
try:
params = dict([(k, v) for k, v in kwargs.items() if '__' not in k])
params.update(defaults)
obj = self.model(**params)
obj.save()
return obj, True
except IntegrityError, e:
return self.get(**kwargs), False
def latest(self, field_name=None):
"""

View File

@ -82,6 +82,9 @@ class HttpRequest(object):
def is_secure(self):
return os.environ.get("HTTPS") == "on"
def is_ajax(self):
return self.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest'
def _set_encoding(self, val):
"""
Sets the encoding used for GET/POST accesses. If the GET or POST

View File

@ -3,6 +3,7 @@ Extra HTML Widget classes
"""
import datetime
import re
from django.newforms.widgets import Widget, Select
from django.utils.dates import MONTHS
@ -10,6 +11,8 @@ from django.utils.safestring import mark_safe
__all__ = ('SelectDateWidget',)
RE_DATE = re.compile(r'(\d{4})-(\d\d?)-(\d\d?)$')
class SelectDateWidget(Widget):
"""
A Widget that splits date input into three <select> boxes.
@ -32,28 +35,43 @@ class SelectDateWidget(Widget):
def render(self, name, value, attrs=None):
try:
value = datetime.date(*map(int, value.split('-')))
year_val, month_val, day_val = value.year, value.month, value.day
except (AttributeError, TypeError, ValueError):
except AttributeError:
year_val = month_val = day_val = None
if isinstance(value, basestring):
match = RE_DATE.match(value)
if match:
year_val, month_val, day_val = [int(v) for v in match.groups()]
output = []
if 'id' in self.attrs:
id_ = self.attrs['id']
else:
id_ = 'id_%s' % name
month_choices = MONTHS.items()
month_choices.sort()
select_html = Select(choices=month_choices).render(self.month_field % name, month_val)
local_attrs = self.build_attrs(id=self.month_field % id_)
select_html = Select(choices=month_choices).render(self.month_field % name, month_val, local_attrs)
output.append(select_html)
day_choices = [(i, i) for i in range(1, 32)]
select_html = Select(choices=day_choices).render(self.day_field % name, day_val)
local_attrs['id'] = self.day_field % id_
select_html = Select(choices=day_choices).render(self.day_field % name, day_val, local_attrs)
output.append(select_html)
year_choices = [(i, i) for i in self.years]
select_html = Select(choices=year_choices).render(self.year_field % name, year_val)
local_attrs['id'] = self.year_field % id_
select_html = Select(choices=year_choices).render(self.year_field % name, year_val, local_attrs)
output.append(select_html)
return mark_safe(u'\n'.join(output))
def id_for_label(self, id_):
return '%s_month' % id_
id_for_label = classmethod(id_for_label)
def value_from_datadict(self, data, files, name):
y, m, d = data.get(self.year_field % name), data.get(self.month_field % name), data.get(self.day_field % name)
if y and m and d:

View File

@ -4,6 +4,7 @@ Field classes.
import copy
import datetime
import os
import re
import time
# Python 2.3 fallbacks
@ -31,7 +32,7 @@ __all__ = (
'RegexField', 'EmailField', 'FileField', 'ImageField', 'URLField',
'BooleanField', 'NullBooleanField', 'ChoiceField', 'MultipleChoiceField',
'ComboField', 'MultiValueField', 'FloatField', 'DecimalField',
'SplitDateTimeField', 'IPAddressField',
'SplitDateTimeField', 'IPAddressField', 'FilePathField',
)
# These values, if given to to_python(), will trigger the self.required check.
@ -718,6 +719,33 @@ class MultiValueField(Field):
"""
raise NotImplementedError('Subclasses must implement this method.')
class FilePathField(ChoiceField):
def __init__(self, path, match=None, recursive=False, required=True,
widget=Select, label=None, initial=None, help_text=None,
*args, **kwargs):
self.path, self.match, self.recursive = path, match, recursive
super(FilePathField, self).__init__(choices=(), required=required,
widget=widget, label=label, initial=initial, help_text=help_text,
*args, **kwargs)
self.choices = []
if self.match is not None:
self.match_re = re.compile(self.match)
if recursive:
for root, dirs, files in os.walk(self.path):
for f in files:
if self.match is None or self.match_re.search(f):
f = os.path.join(root, f)
self.choices.append((f, f.replace(path, "", 1)))
else:
try:
for f in os.listdir(self.path):
full_file = os.path.join(self.path, f)
if os.path.isfile(full_file) and (self.match is None or self.match_re.search(f)):
self.choices.append((full_file, f))
except OSError:
pass
self.widget.choices = self.choices
class SplitDateTimeField(MultiValueField):
default_error_messages = {
'invalid_date': _(u'Enter a valid date.'),

View File

@ -277,19 +277,18 @@ class ModelForm(BaseModelForm):
# Fields #####################################################################
class QuerySetIterator(object):
def __init__(self, queryset, empty_label, cache_choices):
self.queryset = queryset
self.empty_label = empty_label
self.cache_choices = cache_choices
class ModelChoiceIterator(object):
def __init__(self, field):
self.field = field
self.queryset = field.queryset
def __iter__(self):
if self.empty_label is not None:
yield (u"", self.empty_label)
if self.field.empty_label is not None:
yield (u"", self.field.empty_label)
for obj in self.queryset:
yield (obj.pk, smart_unicode(obj))
yield (obj.pk, self.field.label_from_instance(obj))
# Clear the QuerySet cache if required.
if not self.cache_choices:
if not self.field.cache_choices:
self.queryset._result_cache = None
class ModelChoiceField(ChoiceField):
@ -306,6 +305,7 @@ class ModelChoiceField(ChoiceField):
help_text=None, *args, **kwargs):
self.empty_label = empty_label
self.cache_choices = cache_choices
# Call Field instead of ChoiceField __init__() because we don't need
# ChoiceField.__init__().
Field.__init__(self, required, widget, label, initial, help_text,
@ -321,19 +321,30 @@ class ModelChoiceField(ChoiceField):
queryset = property(_get_queryset, _set_queryset)
# this method will be used to create object labels by the QuerySetIterator.
# Override it to customize the label.
def label_from_instance(self, obj):
"""
This method is used to convert objects into strings; it's used to
generate the labels for the choices presented by this object. Subclasses
can override this method to customize the display of the choices.
"""
return smart_unicode(obj)
def _get_choices(self):
# If self._choices is set, then somebody must have manually set
# the property self.choices. In this case, just return self._choices.
if hasattr(self, '_choices'):
return self._choices
# Otherwise, execute the QuerySet in self.queryset to determine the
# choices dynamically. Return a fresh QuerySetIterator that has not
# been consumed. Note that we're instantiating a new QuerySetIterator
# *each* time _get_choices() is called (and, thus, each time
# self.choices is accessed) so that we can ensure the QuerySet has not
# been consumed.
return QuerySetIterator(self.queryset, self.empty_label,
self.cache_choices)
# choices dynamically. Return a fresh QuerySetIterator that has not been
# consumed. Note that we're instantiating a new QuerySetIterator *each*
# time _get_choices() is called (and, thus, each time self.choices is
# accessed) so that we can ensure the QuerySet has not been consumed. This
# construct might look complicated but it allows for lazy evaluation of
# the queryset.
return ModelChoiceIterator(self)
def _set_choices(self, value):
# This method is copied from ChoiceField._set_choices(). It's necessary

View File

@ -124,7 +124,10 @@ def floatformat(text, arg=-1):
d = int(arg)
except ValueError:
return force_unicode(f)
m = f - int(f)
try:
m = f - int(f)
except OverflowError:
return force_unicode(f)
if not m and d < 0:
return mark_safe(u'%d' % int(f))
else:

View File

@ -1,3 +1,4 @@
import urllib
import sys
from cStringIO import StringIO
from django.conf import settings
@ -208,7 +209,7 @@ class Client:
r = {
'CONTENT_LENGTH': None,
'CONTENT_TYPE': 'text/html; charset=utf-8',
'PATH_INFO': path,
'PATH_INFO': urllib.unquote(path),
'QUERY_STRING': urlencode(data, doseq=True),
'REQUEST_METHOD': 'GET',
}
@ -227,7 +228,7 @@ class Client:
r = {
'CONTENT_LENGTH': len(post_data),
'CONTENT_TYPE': content_type,
'PATH_INFO': path,
'PATH_INFO': urllib.unquote(path),
'REQUEST_METHOD': 'POST',
'wsgi.input': StringIO(post_data),
}

View File

@ -18,10 +18,10 @@ if os.name == 'posix':
# Second fork
try:
if os.fork() > 0:
sys.exit(0)
os._exit(0)
except OSError, e:
sys.stderr.write("fork #2 failed: (%d) %s\n" % (e.errno, e.strerror))
sys.exit(1)
os._exit(1)
si = open('/dev/null', 'r')
so = open(out_log, 'a+', 0)
@ -29,6 +29,8 @@ if os.name == 'posix':
os.dup2(si.fileno(), sys.stdin.fileno())
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno())
# Set custom file descriptors so that they get proper buffering.
sys.stdout, sys.stderr = so, se
else:
def become_daemon(our_home_dir='.', out_log=None, err_log=None):
"""

View File

@ -1,11 +1,12 @@
import os
import re
import sys
import datetime
from django.conf import settings
from django.template import Template, Context, TemplateDoesNotExist
from django.utils.html import escape
from django.http import HttpResponseServerError, HttpResponseNotFound
from django.http import HttpResponse, HttpResponseServerError, HttpResponseNotFound
from django.utils.encoding import smart_unicode
HIDDEN_SETTINGS = re.compile('SECRET|PASSWORD|PROFANITIES_LIST')
@ -70,6 +71,11 @@ def technical_500_response(request, exc_type, exc_value, tb):
Create a technical server error response. The last three arguments are
the values returned from sys.exc_info() and friends.
"""
html = get_traceback_html(request, exc_type, exc_value, tb)
return HttpResponseServerError(html, mimetype='text/html')
def get_traceback_html(request, exc_type, exc_value, tb):
"Return HTML code for traceback."
template_info = None
template_does_not_exist = False
loader_debug_info = None
@ -153,13 +159,14 @@ def technical_500_response(request, exc_type, exc_value, tb):
'settings': get_safe_settings(),
'sys_executable': sys.executable,
'sys_version_info': '%d.%d.%d' % sys.version_info[0:3],
'server_time': datetime.datetime.now(),
'django_version_info': get_version(),
'sys_path' : sys.path,
'template_info': template_info,
'template_does_not_exist': template_does_not_exist,
'loader_debug_info': loader_debug_info,
})
return HttpResponseServerError(t.render(c), mimetype='text/html')
return t.render(c)
def technical_404_response(request, exception):
"Create a technical 404 error response. The exception should be the Http404."
@ -190,7 +197,7 @@ def empty_urlconf(request):
c = Context({
'project_name': settings.SETTINGS_MODULE.split('.')[0]
})
return HttpResponseNotFound(t.render(c), mimetype='text/html')
return HttpResponse(t.render(c), mimetype='text/html')
def _get_lines_from_file(filename, lineno, context_lines, loader=None, module_name=None):
"""
@ -384,6 +391,10 @@ TECHNICAL_500_TEMPLATE = """
<th>Python Path:</th>
<td>{{ sys_path }}</td>
</tr>
<tr>
<th>Server time:</th>
<td>{{server_time|date:"r"}}</td>
</tr>
</table>
</div>
{% if unicode_hint %}

View File

@ -1,7 +1,7 @@
from django.template import loader, RequestContext
from django.http import Http404, HttpResponse
from django.core.xheaders import populate_xheaders
from django.core.paginator import ObjectPaginator, InvalidPage
from django.core.paginator import QuerySetPaginator, InvalidPage
from django.core.exceptions import ObjectDoesNotExist
def object_list(request, queryset, paginate_by=None, page=None,
@ -45,43 +45,47 @@ def object_list(request, queryset, paginate_by=None, page=None,
if extra_context is None: extra_context = {}
queryset = queryset._clone()
if paginate_by:
paginator = ObjectPaginator(queryset, paginate_by)
paginator = QuerySetPaginator(queryset, paginate_by, allow_empty_first_page=allow_empty)
if not page:
page = request.GET.get('page', 1)
try:
page_number = int(page)
except ValueError:
if page == 'last':
page_number = paginator.pages
page_number = paginator.num_pages
else:
# Page is not 'last', nor can it be converted to an int
# Page is not 'last', nor can it be converted to an int.
raise Http404
try:
object_list = paginator.get_page(page_number - 1)
page_obj = paginator.page(page_number)
except InvalidPage:
if page_number == 1 and allow_empty:
object_list = []
else:
raise Http404
raise Http404
c = RequestContext(request, {
'%s_list' % template_object_name: object_list,
'is_paginated': paginator.pages > 1,
'results_per_page': paginate_by,
'has_next': paginator.has_next_page(page_number - 1),
'has_previous': paginator.has_previous_page(page_number - 1),
'page': page_number,
'next': page_number + 1,
'previous': page_number - 1,
'last_on_page': paginator.last_on_page(page_number - 1),
'first_on_page': paginator.first_on_page(page_number - 1),
'pages': paginator.pages,
'hits' : paginator.hits,
'page_range' : paginator.page_range
'%s_list' % template_object_name: page_obj.object_list,
'paginator': paginator,
'page_obj': page_obj,
# Legacy template context stuff. New templates should use page_obj
# to access this instead.
'is_paginated': page_obj.has_other_pages(),
'results_per_page': paginator.per_page,
'has_next': page_obj.has_next(),
'has_previous': page_obj.has_previous(),
'page': page_obj.number,
'next': page_obj.next_page_number(),
'previous': page_obj.previous_page_number(),
'first_on_page': page_obj.start_index(),
'last_on_page': page_obj.end_index(),
'pages': paginator.num_pages,
'hits': paginator.count,
'page_range': paginator.page_range,
}, context_processors)
else:
c = RequestContext(request, {
'%s_list' % template_object_name: queryset,
'is_paginated': False
'paginator': None,
'page_obj': None,
'is_paginated': False,
}, context_processors)
if not allow_empty and len(queryset) == 0:
raise Http404

View File

@ -401,8 +401,8 @@ For example::
# ...
def get_db_prep_save(self, value):
return ''.join([''.join(l) for l in (self.north,
self.east, self.south, self.west)])
return ''.join([''.join(l) for l in (value.north,
value.east, value.south, value.west)])
``pre_save(self, model_instance, add)``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -1372,7 +1372,7 @@ equivalent::
Entry.objects.filter(blog__pk=3) # __pk implies __id__exact
Lookups that span relationships
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-------------------------------
Django offers a powerful and intuitive way to "follow" relationships in
lookups, taking care of the SQL ``JOIN``\s for you automatically, behind the
@ -1396,7 +1396,7 @@ whose ``headline`` contains ``'Lennon'``::
Blog.objects.filter(entry__headline__contains='Lennon')
Escaping percent signs and underscores in LIKE statements
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
---------------------------------------------------------
The field lookups that equate to ``LIKE`` SQL statements (``iexact``,
``contains``, ``icontains``, ``startswith``, ``istartswith``, ``endswith``

View File

@ -751,6 +751,19 @@ In addition to ``extra_context``, the template's context will be:
If the results are paginated, the context will contain these extra variables:
* **New in Django development version:** ``paginator``: An instance of
``django.core.paginator.Paginator``.
* **New in Django development version:** ``page_obj``: An instance of
``django.core.paginator.Page``.
In older versions of Django, before ``paginator`` and ``page_obj`` were added
to this template's context, the template included several other variables
related to pagination. Note that you should *NOT* use these variables anymore;
use ``paginator`` and ``page_obj`` instead, because they let you do everything
these old variables let you do (and more!). But for legacy installations,
here's a list of those old template variables:
* ``results_per_page``: The number of objects per page. (Same as the
``paginate_by`` parameter.)
@ -777,8 +790,8 @@ If the results are paginated, the context will contain these extra variables:
* ``hits``: The total number of objects across *all* pages, not just this
page.
* **New in Django development version:** ``page_range``: A list of the
page numbers that are available. This is 1-based.
* ``page_range``: A list of the page numbers that are available. This is
1-based.
Notes on pagination
~~~~~~~~~~~~~~~~~~~

View File

@ -167,6 +167,20 @@ These commands will install Django in your Python installation's
Installing the development version
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. admonition:: Tracking Django development
If you decide to use the latest development version of Django,
you'll want to pay close attention to `the development timeline`_,
and you'll want to keep an eye on `the list of
backwards-incompatible changes`_; this will help you stay on top
of any new features you might want to use, as well as any changes
you'll need to make to your code when updating your copy of Django
(for stable releases, any necessary changes are documented in the
release notes).
.. _the development timeline: http://code.djangoproject.com/timeline
.. _the list of backwards-incompatible changes: http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges
If you'd like to be able to update your Django code occasionally with the
latest bug fixes and improvements, follow these instructions:

View File

@ -626,7 +626,8 @@ option is ignored.
``default``
~~~~~~~~~~~
The default value for the field.
The default value for the field. This can be a value or a callable object. If
callable it will be called every time a new object is created.
``editable``
~~~~~~~~~~~~
@ -1788,14 +1789,15 @@ For example::
This example allows you to request ``Person.men.all()``, ``Person.women.all()``,
and ``Person.people.all()``, yielding predictable results.
If you use custom ``Manager`` objects, take note that the first ``Manager``
Django encounters (in order by which they're defined in the model) has a
special status. Django interprets the first ``Manager`` defined in a class as
the "default" ``Manager``. Certain operations -- such as Django's admin site --
use the default ``Manager`` to obtain lists of objects, so it's generally a
good idea for the first ``Manager`` to be relatively unfiltered. In the last
example, the ``people`` ``Manager`` is defined first -- so it's the default
``Manager``.
If you use custom ``Manager`` objects, take note that the first
``Manager`` Django encounters (in the order in which they're defined
in the model) has a special status. Django interprets this first
``Manager`` defined in a class as the "default" ``Manager``, and
several parts of Django (though not the admin application) will use
that ``Manager`` exclusively for that model. As a result, it's often a
good idea to be careful in your choice of default manager, in order to
avoid a situation where overriding of ``get_query_set()`` results in
an inability to retrieve objects you'd like to work with.
Model methods
=============

View File

@ -231,6 +231,16 @@ For example::
# Create and save the new author instance. There's no need to do anything else.
>>> new_author = f.save()
Other than the ``save()`` and ``save_m2m()`` methods, a ``ModelForm``
works exactly the same way as any other ``newforms`` form. For
example, the ``is_valid()`` method is used to check for validity, the
``is_multipart()`` method is used to determine whether a form requires
multipart file upload (and hence whether ``request.FILES`` must be
passed to the form), etc.; see `the standard newforms documentation`_
for more information.
.. _the standard newforms documentation: ../newforms/
Using a subset of fields on the form
------------------------------------

View File

@ -1333,13 +1333,14 @@ given length.
An ``UploadedFile`` object has two attributes:
====================== =====================================================
Argument Description
====================== =====================================================
====================== ====================================================
Attribute Description
====================== ====================================================
``filename`` The name of the file, provided by the uploading
client.
``content`` The array of bytes comprising the file content.
====================== =====================================================
====================== ====================================================
The string representation of an ``UploadedFile`` is the same as the filename
attribute.
@ -1349,6 +1350,38 @@ When you use a ``FileField`` on a form, you must also remember to
.. _`bind the file data to the form`: `Binding uploaded files to a form`_
``FilePathField``
~~~~~~~~~~~~~~~~~
**New in Django development version**
* Default widget: ``Select``
* Empty value: ``None``
* Normalizes to: A unicode object
* Validates that the selected choice exists in the list of choices.
* Error message keys: ``required``, ``invalid_choice``
The field allows choosing from files inside a certain directory. It takes three
extra arguments:
============== ========== ===============================================
Argument Required? Description
============== ========== ===============================================
``path`` Yes The absolute path to the directory whose
contents you want listed. This directory must
exist.
``recursive`` No If ``False`` (the default) only the direct
contents of ``path`` will be offered as choices.
If ``True``, the directory will be descended
into recursively and all descendants will be
listed as choices.
``match`` No A regular expression pattern; only files with
names matching this expression will be allowed
as choices.
============== ========== ===============================================
``ImageField``
~~~~~~~~~~~~~~
@ -1499,6 +1532,41 @@ the bottom of this document, for examples of their use.
``SplitDateTimeField``
~~~~~~~~~~~~~~~~~~~~~~
Fields which handle relationships
---------------------------------
For representing relationships between models, two fields are
provided which can derive their choices from a ``QuerySet``, and which
place one or more model objects into the ``cleaned_data`` dictionary
of forms in which they're used. Both of these fields have an
additional required argument:
``queryset``
A ``QuerySet`` of model objects from which the choices for the
field will be derived, and which will be used to validate the
user's selection.
``ModelChoiceField``
~~~~~~~~~~~~~~~~~~~~
Allows the selection of a single model object, suitable for representing a
foreign key. The method receives an object as an argument and must return a
string to represent it.
The labels for the choice field call the ``__unicode__`` method of the model to
generate string representations. To provide custom labels, subclass ``ModelChoiceField`` and override ``label_for_model``::
class MyModelChoiceField(ModelChoiceField):
def label_from_instance(self, obj):
return "My Object #%i" % obj.id
``ModelMultipleChoiceField``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Allows the selection of one or more model objects, suitable for representing a
many-to-many relation. As with ``ModelChoiceField``, you can use
``label_from_instance`` to customize the object labels.
Creating custom fields
----------------------
@ -1569,9 +1637,9 @@ The three types of cleaning methods are:
These methods are run in the order given above, one field at a time. That is,
for each field in the form (in the order they are declared in the form
definition), the ``Field.clean()`` method (or it's override) is run, then
definition), the ``Field.clean()`` method (or its override) is run, then
``clean_<fieldname>()``. Finally, once those two methods are run for every
field, the ``Form.clean()`` method, or it's override, is executed.
field, the ``Form.clean()`` method, or its override, is executed.
As mentioned above, any of these methods can raise a ``ValidationError``. For
any field, if the ``Field.clean()`` method raises a ``ValidationError``, any
@ -1693,7 +1761,7 @@ For example, take the following simple form::
comment = forms.CharField()
This form will include three default TextInput widgets, with default rendering -
no CSS class, no extra attributes. This means that the inputs boxes provided for
no CSS class, no extra attributes. This means that the input boxes provided for
each widget will be rendered exactly the same::
>>> f = CommentForm(auto_id=False)

133
docs/pagination.txt Normal file
View File

@ -0,0 +1,133 @@
==========
Pagination
==========
**New in Django development version**
Django provides a few classes that help you manage paginated data -- that is,
data that's split across several pages, with "Previous/Next" links. These
classes live in the module ``django/core/paginator.py``.
Example
=======
Give ``Paginator`` a list of objects, plus the number of items you'd like to
have on each page, and it gives you methods for accessing the items for each
page::
>>> from django.core.paginator import Paginator
>>> objects = ['john', 'paul', 'george', 'ringo']
>>> p = Paginator(objects, 2)
>>> p.count
4
>>> p.num_pages
2
>>> p.page_range
[1, 2]
>>> page1 = p.page(1)
>>> page1
<Page 1 of 2>
>>> page1.object_list
['john', 'paul']
>>> page2 = p.page(2)
>>> page2.object_list
['george', 'ringo']
>>> page2.has_next()
False
>>> page2.has_previous()
True
>>> page2.has_other_pages()
True
>>> page2.next_page_number()
3
>>> page2.previous_page_number()
1
>>> page2.start_index() # The 1-based index of the first item on this page
3
>>> page2.end_index() # The 1-based index of the last item on this page
4
>>> p.page(0)
Traceback (most recent call last):
...
InvalidPage
>>> p.page(3)
Traceback (most recent call last):
...
InvalidPage
``Paginator`` objects
=====================
Methods
-------
``page(number)`` -- Returns a ``Page`` object with the given 1-based index.
Raises ``InvalidPage`` if the given page number doesn't exist.
Attributes
----------
``count`` -- The total number of objects, across all pages.
``num_pages`` -- The total number of pages.
``page_range`` -- A 1-based range of page numbers, e.g., ``[1, 2, 3, 4]``.
``Page`` objects
================
Methods
-------
``has_next()`` -- Returns ``True`` if there's a next page.
``has_previous()`` -- Returns ``True`` if there's a previous page.
``has_other_pages()`` -- Returns ``True`` if there's a next *or* previous page.
``next_page_number()`` -- Returns the next page number. Note that this is
"dumb" and will return the next page number regardless of whether a subsequent
page exists.
``previous_page_number()`` -- Returns the previous page number. Note that this
is "dumb" and will return the previous page number regardless of whether a
previous page exists.
``start_index()`` -- Returns the 1-based index of the first object on the page,
relative to all of the objects in the paginator's list. For example, when
paginating a list of 5 objects with 2 objects per page, the second page's
``start_index()`` would return ``3``.
``end_index()`` -- Returns the 1-based index of the last object on the page,
relative to all of the objects in the paginator's list. For example, when
paginating a list of 5 objects with 2 objects per page, the second page's
``end_index()`` would return ``4``.
Attributes
----------
``object_list`` -- The list of objects on this page.
``number`` -- The 1-based page number for this page.
``paginator`` -- The associated ``Paginator`` object.
``QuerySetPaginator`` objects
=============================
Use ``QuerySetPaginator`` instead of ``Paginator`` if you're paginating across
a ``QuerySet`` from Django's database API. This is slightly more efficient, and
there are no API differences between the two classes.
The legacy ``ObjectPaginator`` class
====================================
The ``Paginator`` and ``Page`` classes are new in the Django development
version, as of revision 7306. In previous versions, Django provided an
``ObjectPaginator`` class that offered similar functionality but wasn't as
convenient. This class still exists, for backwards compatibility, but Django
now issues a ``DeprecationWarning`` if you try to use it.

View File

@ -141,6 +141,16 @@ All attributes except ``session`` should be considered read-only.
The raw HTTP POST data. This is only useful for advanced processing. Use
``POST`` instead.
``urlconf``
Not defined by Django itself, but will be read if other code
(e.g., a custom middleware class) sets it; when present, this will
be used as the root URLConf for the current request, overriding
the ``ROOT_URLCONF`` setting. See `How Django processes a
request`_ for details.
.. _How Django processes a request: ../url_dispatch/#how-django-processes-a-request
Methods
-------
@ -189,6 +199,23 @@ Methods
Returns ``True`` if the request is secure; that is, if it was made with
HTTPS.
``is_ajax()``
**New in Django development version**
Returns ``True`` if the request was made via an XMLHttpRequest by checking
the ``HTTP_X_REQUESTED_WITH`` header for the string *'XMLHttpRequest'*. The
following major Javascript libraries all send this header:
* jQuery
* Dojo
* MochiKit
* MooTools
* Prototype
* YUI
If you write your own XMLHttpRequest call (on the browser side), you will
have to set this header manually to use this method.
QueryDict objects
-----------------

View File

@ -48,10 +48,10 @@ Using file-based sessions
To use file-based sessions, set the ``SESSION_ENGINE`` setting to
``"django.contrib.sessions.backends.file"``.
You might also want to set the ``SESSION_FILE_PATH`` setting (which
defaults to ``/tmp``) to control where Django stores session files. Be
sure to check that your Web server has permissions to read and write to
this location.
You might also want to set the ``SESSION_FILE_PATH`` setting (which defaults
to output from ``tempfile.gettempdir()``, most likely ``/tmp``) to control
where Django stores session files. Be sure to check that your Web server has
permissions to read and write to this location.
Using cache-based sessions
--------------------------

View File

@ -185,8 +185,11 @@ ADMIN_MEDIA_PREFIX
Default: ``'/media/'``
The URL prefix for admin media -- CSS, JavaScript and images. Make sure to use
a trailing slash.
The URL prefix for admin media -- CSS, JavaScript and images used by
the Django administrative interface. Make sure to use a trailing
slash, and to have this be different from the ``MEDIA_URL`` setting
(since the same URL cannot be mapped onto two different sets of
files).
ADMINS
------
@ -754,7 +757,9 @@ ROOT_URLCONF
Default: Not defined
A string representing the full Python import path to your root URLconf. For example:
``"mydjangoapps.urls"``. See `How Django processes a request`_.
``"mydjangoapps.urls"``. Can be overridden on a per-request basis by
setting the attribute ``urlconf`` on the incoming ``HttpRequest``
object. See `How Django processes a request`_ for details.
.. _How Django processes a request: ../url_dispatch/#how-django-processes-a-request

View File

@ -245,6 +245,13 @@ request to the URL ``/rss/beats/0613/``:
subclass of ``ObjectDoesNotExist``. Raising ``ObjectDoesNotExist`` in
``get_object()`` tells Django to produce a 404 error for that request.
**New in Django development version:** The ``get_object()`` method also
has a chance to handle the ``/rss/beats/`` url. In this case, ``bits``
will be an empty list. In our example, ``len(bits) != 1`` and an
``ObjectDoesNotExist`` exception will be raised, so ``/rss/beats/`` will
generate a 404 page. But you can handle this case however you like. For
example you could generate a combined feed for all beats.
* To generate the feed's ``<title>``, ``<link>`` and ``<description>``,
Django uses the ``title()``, ``link()`` and ``description()`` methods. In
the previous example, they were simple string class attributes, but this

View File

@ -429,8 +429,9 @@ all block tags. For example::
# base.html
{% autoescape off %}
<h1>{% block title %}</h1>
<h1>{% block title %}{% endblock %}</h1>
{% block content %}
{% endblock %}
{% endautoescape %}
@ -438,10 +439,11 @@ all block tags. For example::
{% extends "base.html" %}
{% block title %}This & that{% endblock %}
{% block content %}<b>Hello!</b>{% endblock %}
{% block content %}{{ greeting }}{% endblock %}
Because auto-escaping is turned off in the base template, it will also be
turned off in the child template, resulting in the following rendered HTML::
turned off in the child template, resulting in the following rendered
HTML when the ``greeting`` variable contains the string ``<b>Hello!</b>``::
<h1>This & that</h1>
<b>Hello!</b>
@ -1222,20 +1224,20 @@ Built-in filter reference
add
~~~
Adds the arg to the value.
Adds the argument to the value.
For example::
{{ value|add:2 }}
{{ value|add:"2" }}
If ``value`` is 4, then the output will be 6.
If ``value`` is ``4``, then the output will be ``6``.
addslashes
~~~~~~~~~~
Adds slashes before quotes. Useful for escaping strings in CSV, for example.
**New in Django development version**: for escaping data in JavaScript strings,
**New in Django development version**: For escaping data in JavaScript strings,
use the `escapejs`_ filter instead.
capfirst
@ -1257,7 +1259,7 @@ For example::
{{ value|cut:" "}}
If ``value`` is "String with spaces", the output will be ``Stringwithspaces``.
If ``value`` is ``"String with spaces"``, the output will be ``"Stringwithspaces"``.
date
~~~~
@ -1268,35 +1270,40 @@ For example::
{{ value|date:"D d M Y" }}
If ``value`` is a datetime object (ie. datetime.datetime.now()), the output
would be formatted like ``Wed 09 Jan 2008``.
If ``value`` is a ``datetime`` object (e.g., the result of
``datetime.datetime.now()``), the output will be the string
``'Wed 09 Jan 2008'``.
default
~~~~~~~
If value is unavailable, use given default.
If value evaluates to ``False``, use given default. Otherwise, use the value.
For example::
{{ value|default:"nothing" }}
If ``value`` is ``Undefined``, the output would be ``nothing``.
If ``value`` is ``""`` (the empty string), the output will be ``nothing``.
default_if_none
~~~~~~~~~~~~~~~
If value is ``None``, use given default.
If (and only if) value is ``None``, use given default. Otherwise, use the
value.
Note that if an empty string is given, the default value will *not* be used.
Use the ``default`` filter if you want to fallback for empty strings.
For example::
{{ value|default_if_none:"nothing" }}
If ``value`` is ``None``, the output would be ``nothing``.
If ``value`` is ``None``, the output will be the string ``"nothing"``.
dictsort
~~~~~~~~
Takes a list of dictionaries, returns that list sorted by the key given in
Takes a list of dictionaries and returns that list sorted by the key given in
the argument.
For example::
@ -1304,46 +1311,42 @@ For example::
{{ value|dictsort:"name" }}
If ``value`` is::
[
{'name': 'zed', 'age': 19}
{'name': 'amy', 'age': 22},
{'name': 'joe', 'age': 31},
{'name': 'zed', 'age': 19},
{'name': 'amy', 'age': 22},
{'name': 'joe', 'age': 31},
]
then the output would be::
[
{'name': 'amy', 'age': 22},
{'name': 'joe', 'age': 31},
{'name': 'zed', 'age': 19}
{'name': 'amy', 'age': 22},
{'name': 'joe', 'age': 31},
{'name': 'zed', 'age': 19},
]
dictsortreversed
~~~~~~~~~~~~~~~~
Takes a list of dictionaries, returns that list sorted in reverse order by the
key given in the argument. This works exactly the same as the above filter, but
the returned value will be in reverse order.
Takes a list of dictionaries and returns that list sorted in reverse order by
the key given in the argument. This works exactly the same as the above filter,
but the returned value will be in reverse order.
divisibleby
~~~~~~~~~~~
Returns true if the value is divisible by the argument.
Returns ``True`` if the value is divisible by the argument.
For example::
{{ value|divisibleby:3 }}
{{ value|divisibleby:"3" }}
If ``value`` is ``21``, the output would be ``True``.
escape
~~~~~~
**New in Django development version:** The behaviour of this filter has
changed slightly in the development version (the affects are only applied
once, after all other filters).
Escapes a string's HTML. Specifically, it makes these replacements:
* ``<`` is converted to ``&lt;``
@ -1362,6 +1365,10 @@ applied to the result will only result in one round of escaping being done. So
it is safe to use this function even in auto-escaping environments. If you want
multiple escaping passes to be applied, use the ``force_escape`` filter.
**New in Django development version:** Due to auto-escaping, the behavior of
this filter has changed slightly. The replacements are only made once, after
all other filters are applied -- including filters before and after it.
escapejs
~~~~~~~~
@ -1392,7 +1399,7 @@ For example::
{{ value|first }}
If ``value`` is ``['a', 'b', 'c']``, the output would be `a`.
If ``value`` is the list ``['a', 'b', 'c']``, the output will be ``'a'``.
fix_ampersands
~~~~~~~~~~~~~~
@ -1403,11 +1410,11 @@ For example::
{{ value|fix_ampersands }}
If ``value`` is ``Tom & Jerry``, the output would be ``Tom &amp; Jerry``.
If ``value`` is ``Tom & Jerry``, the output will be ``Tom &amp; Jerry``.
**New in Django development version**: you probably don't need to use this
filter since ampersands will be automatically escaped. See escape_ for more on
how auto-escaping works.
**New in Django development version**: This filter generally is no longer
useful, because ampersands are automatically escaped in templates. See escape_
for more on how auto-escaping works.
floatformat
~~~~~~~~~~~
@ -1463,16 +1470,16 @@ filter.
get_digit
~~~~~~~~~
Given a whole number, returns the requested digit of it, where 1 is the
right-most digit, 2 is the second-right-most digit, etc. Returns the original
value for invalid input (if input or argument is not an integer, or if argument
is less than 1). Otherwise, output is always an integer.
Given a whole number, returns the requested digit, where 1 is the right-most
digit, 2 is the second-right-most digit, etc. Returns the original value for
invalid input (if input or argument is not an integer, or if argument is less
than 1). Otherwise, output is always an integer.
For example::
{{ value|get_digit:2 }}
If ``value`` is 123456789, the output would be ``8``.
{{ value|get_digit:"2" }}
If ``value`` is ``123456789``, the output will be ``8``.
iriencode
~~~~~~~~~
@ -1493,7 +1500,8 @@ For example::
{{ value|join:" // " }}
If ``value`` is ``['a', 'b', 'c']``, the output would be ``a // b // c``.
If ``value`` is the list ``['a', 'b', 'c']``, the output will be the string
``"a // b // c"``.
last
~~~~
@ -1506,29 +1514,30 @@ For example::
{{ value|last }}
If ``value`` is ``['a', 'b', 'c', 'd']``, the output would be ``d``.
If ``value`` is the list ``['a', 'b', 'c', 'd']``, the output will be the string
``"d"``.
length
~~~~~~
Returns the length of the value. Useful for lists.
Returns the length of the value. This works for both strings and lists.
For example::
{{ value|length }}
If ``value`` is ``['a', 'b', 'c', 'd']``, the output would be ``4``.
If ``value`` is ``['a', 'b', 'c', 'd']``, the output will be ``4``.
length_is
~~~~~~~~~
Returns a boolean of whether the value's length is the argument.
Returns ``True`` if the value's length is the argument, or ``False`` otherwise.
For example::
{{ value|length_is:4 }}
{{ value|length_is:"4" }}
If ``value`` is ``['a', 'b', 'c', 'd']``, the output would be ``True``.
If ``value`` is ``['a', 'b', 'c', 'd']``, the output will be ``True``.
linebreaks
~~~~~~~~~~
@ -1541,7 +1550,7 @@ For example::
{{ value|linebreaks }}
If ``value`` is ``Joel\nis a slug``, the output would be ``<p>Joe<br>is a
If ``value`` is ``Joel\nis a slug``, the output will be ``<p>Joe<br>is a
slug</p>``.
linebreaksbr
@ -1571,7 +1580,7 @@ For example::
{{ value|lower }}
If ``value`` is ``Joel Is a Slug``, the output would be ``joel is a slug``.
If ``value`` is ``Still MAD At Yoko``, the output will be ``still mad at yoko``.
make_list
~~~~~~~~~
@ -1583,8 +1592,9 @@ For example::
{{ value|make_list }}
If ``value`` is "Joe", the output would be ``[u'J', u'o', u'e']. If ``value`` is
123, the output would be ``[1, 2, 3]``.
If ``value`` is the string ``"Joe"``, the output would be the list
``[u'J', u'o', u'e']``. If ``value`` is ``123``, the output will be the list
``[1, 2, 3]``.
phone2numeric
~~~~~~~~~~~~~
@ -1629,25 +1639,25 @@ __ http://www.python.org/doc/2.5/lib/module-pprint.html
random
~~~~~~
Returns a random item from the list.
Returns a random item from the given list.
For example::
{{ value|random }}
If ``value`` is ``['a', 'b', 'c', 'd']``, the output could be ``b``.
If ``value`` is the list ``['a', 'b', 'c', 'd']``, the output could be ``"b"``.
removetags
~~~~~~~~~~
Removes a space separated list of [X]HTML tags from the output.
Removes a space-separated list of [X]HTML tags from the output.
For example::
{{ value|removetags:"b span"|safe }}
If ``value`` is ``<b>Joel</b> <button>is</button> a <span>slug</span>`` the
output would be ``Joel <button>is</button> a slug``.
If ``value`` is ``"<b>Joel</b> <button>is</button> a <span>slug</span>"`` the
output will be ``"Joel <button>is</button> a slug"``.
rjust
~~~~~
@ -1684,7 +1694,7 @@ For example::
{{ value|slugify }}
If ``value`` is ``Joel is a slug``, the output would be ``joel-is-a-slug``.
If ``value`` is ``"Joel is a slug"``, the output will be ``"joel-is-a-slug"``.
stringformat
~~~~~~~~~~~~
@ -1700,7 +1710,7 @@ For example::
{{ value|stringformat:"s" }}
If ``value`` is ``Joel is a slug``, the output would be ``Joel is a slug``.
If ``value`` is ``"Joel is a slug"``, the output will be ``"Joel is a slug"``.
striptags
~~~~~~~~~
@ -1711,8 +1721,8 @@ For example::
{{ value|striptags }}
If ``value`` is ``<b>Joel</b> <button>is</button> a <span>slug</span>`` the
output would be ``Joel is a slug``.
If ``value`` is ``"<b>Joel</b> <button>is</button> a <span>slug</span>"``, the
output will be ``"Joel is a slug"``.
time
~~~~
@ -1726,13 +1736,13 @@ For example::
{{ value|time:"H:i" }}
If ``value`` is ``datetime.datetime.now()``, the output would be formatted
like ``01:23``.
If ``value`` is equivalent to ``datetime.datetime.now()``, the output will be
the string ``"01:23"``.
timesince
~~~~~~~~~
Formats a date as the time since that date (i.e. "4 days, 6 hours").
Formats a date as the time since that date (e.g., "4 days, 6 hours").
Takes an optional argument that is a variable containing the date to use as
the comparison point (without the argument, the comparison point is *now*).
@ -1774,7 +1784,7 @@ For example::
{{ value|truncatewords:2 }}
If ``value`` is ``Joel is a slug``, the output would be ``Joel is ...``.
If ``value`` is ``"Joel is a slug"``, the output will be ``"Joel is ..."``.
truncatewords_html
~~~~~~~~~~~~~~~~~~
@ -1792,10 +1802,8 @@ unordered_list
Recursively takes a self-nested list and returns an HTML unordered list --
WITHOUT opening and closing <ul> tags.
**Changed in Django development version**
The format accepted by ``unordered_list`` has changed to an easier to
understand format.
**New in Django development version:** The format accepted by
``unordered_list`` has changed to be easier to understand.
The list is assumed to be in the proper format. For example, if ``var`` contains
``['States', ['Kansas', ['Lawrence', 'Topeka'], 'Illinois']]``, then
@ -1825,7 +1833,7 @@ For example::
{{ value|upper }}
If ``value`` is ``Joel is a slug``, the output would be ``JOEL IS A SLUG``.
If ``value`` is ``"Joel is a slug"``, the output will be ``"JOEL IS A SLUG"``.
urlencode
~~~~~~~~~
@ -1844,9 +1852,9 @@ For example::
{{ value|urlize }}
If ``value`` is ``Check out www.djangoproject.com``, the output would be
``Check out <a
href="http://www.djangoproject.com">www.djangoproject.com</a>``.
If ``value`` is ``"Check out www.djangoproject.com"``, the output will be
``"Check out <a
href="http://www.djangoproject.com">www.djangoproject.com</a>"``.
urlizetrunc
~~~~~~~~~~~
@ -1862,9 +1870,9 @@ For example::
{{ value|urlizetrunc:15 }}
If ``value`` is ``Check out www.djangoproject.com``, the output would be
``Check out <a
href="http://www.djangoproject.com">www.djangopr...</a>``.
If ``value`` is ``"Check out www.djangoproject.com"``, the output would be
``'Check out <a
href="http://www.djangoproject.com">www.djangopr...</a>'``.
wordcount
~~~~~~~~~

View File

@ -37,6 +37,12 @@ A quick rundown:
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 ``for`` tag has
gone through its loop; for more information, see `the
documentation for the "for" tag`_.
.. _the documentation for the "for" tag: ../templates/#for
Now, let's create a Django view that handles the submitted data and does
something with it. Remember, in `Tutorial 3`_, we created a URLconf for the

View File

@ -32,9 +32,11 @@ How Django processes a request
When a user requests a page from your Django-powered site, this is the
algorithm the system follows to determine which Python code to execute:
1. Django looks at the ``ROOT_URLCONF`` setting in your `settings file`_.
This should be a string representing the full Python import path to your
URLconf. For example: ``"mydjangoapps.urls"``.
1. Django determines the root URLConf module to use; ordinarily
this is the value of the ``ROOT_URLCONF`` setting in your
`settings file`_, but if the incoming ``HttpRequest`` object
has an attribute called ``urlconf``, its value will be used in
place of the ``ROOT_URLCONF`` setting.
2. Django loads that Python module and looks for the variable
``urlpatterns``. This should be a Python list, in the format returned by
the function ``django.conf.urls.defaults.patterns()``.

View File

@ -71,8 +71,9 @@ u'ABC123'
>>> fran.save()
>>> Employee.objects.filter(last_name__exact='Jones')
[<Employee: Dan Jones>, <Employee: Fran Jones>]
>>> Employee.objects.in_bulk(['ABC123', 'XYZ456'])
{u'XYZ456': <Employee: Fran Jones>, u'ABC123': <Employee: Dan Jones>}
>>> emps = Employee.objects.in_bulk(['ABC123', 'XYZ456'])
>>> emps['ABC123']
<Employee: Dan Jones>
>>> b = Business(name='Sears')
>>> b.save()

View File

@ -76,8 +76,11 @@ Article 4
# in_bulk() takes a list of IDs and returns a dictionary mapping IDs
# to objects.
>>> Article.objects.in_bulk([1, 2])
{1: <Article: Article 1>, 2: <Article: Article 2>}
>>> arts = Article.objects.in_bulk([1, 2])
>>> arts[1]
<Article: Article 1>
>>> arts[2]
<Article: Article 2>
>>> Article.objects.in_bulk([3])
{3: <Article: Article 3>}
>>> Article.objects.in_bulk([1000])

View File

@ -41,25 +41,33 @@ __test__ = {'API_TESTS':u"""
True
# Attempt to add a Musician without a first_name.
>>> man.get_validation_errors(MultiValueDict({'last_name': ['Blakey']}))
{'first_name': [u'This field is required.']}
>>> man.get_validation_errors(MultiValueDict({'last_name': ['Blakey']}))['first_name']
[u'This field is required.']
# Attempt to add a Musician without a first_name and last_name.
>>> man.get_validation_errors(MultiValueDict({}))
{'first_name': [u'This field is required.'], 'last_name': [u'This field is required.']}
>>> errors = man.get_validation_errors(MultiValueDict({}))
>>> errors['first_name']
[u'This field is required.']
>>> errors['last_name']
[u'This field is required.']
# Attempt to create an Album without a name or musician.
>>> man = Album.AddManipulator()
>>> man.get_validation_errors(MultiValueDict({}))
{'musician': [u'This field is required.'], 'name': [u'This field is required.']}
>>> errors = man.get_validation_errors(MultiValueDict({}))
>>> errors['musician']
[u'This field is required.']
>>> errors['name']
[u'This field is required.']
# Attempt to create an Album with an invalid musician.
>>> man.get_validation_errors(MultiValueDict({'name': ['Sallies Fforth'], 'musician': ['foo']}))
{'musician': [u"Select a valid choice; 'foo' is not in [u'', u'1']."]}
>>> errors = man.get_validation_errors(MultiValueDict({'name': ['Sallies Fforth'], 'musician': ['foo']}))
>>> errors['musician']
[u"Select a valid choice; 'foo' is not in [u'', u'1']."]
# Attempt to create an Album with an invalid release_date.
>>> man.get_validation_errors(MultiValueDict({'name': ['Sallies Fforth'], 'musician': ['1'], 'release_date': 'today'}))
{'release_date': [u'Enter a valid date in YYYY-MM-DD format.']}
>>> errors = man.get_validation_errors(MultiValueDict({'name': ['Sallies Fforth'], 'musician': ['1'], 'release_date': 'today'}))
>>> errors['release_date']
[u'Enter a valid date in YYYY-MM-DD format.']
# Create an Album without a release_date (because it's optional).
>>> data = MultiValueDict({'name': ['Ella and Basie'], 'musician': ['1']})

View File

@ -234,8 +234,12 @@ We can also subclass the Meta inner class to change the fields list.
>>> f = CategoryForm({'name': 'Entertainment', 'slug': 'entertainment', 'url': 'entertainment'})
>>> f.is_valid()
True
>>> f.cleaned_data
{'url': u'entertainment', 'name': u'Entertainment', 'slug': u'entertainment'}
>>> f.cleaned_data['url']
u'entertainment'
>>> f.cleaned_data['name']
u'Entertainment'
>>> f.cleaned_data['slug']
u'entertainment'
>>> obj = f.save()
>>> obj
<Category: Entertainment>
@ -245,8 +249,12 @@ True
>>> f = CategoryForm({'name': "It's a test", 'slug': 'its-test', 'url': 'test'})
>>> f.is_valid()
True
>>> f.cleaned_data
{'url': u'test', 'name': u"It's a test", 'slug': u'its-test'}
>>> f.cleaned_data['url']
u'test'
>>> f.cleaned_data['name']
u"It's a test"
>>> f.cleaned_data['slug']
u'its-test'
>>> obj = f.save()
>>> obj
<Category: It's a test>
@ -259,8 +267,12 @@ save() on the resulting model instance.
>>> f = CategoryForm({'name': 'Third test', 'slug': 'third-test', 'url': 'third'})
>>> f.is_valid()
True
>>> f.cleaned_data
{'url': u'third', 'name': u'Third test', 'slug': u'third-test'}
>>> f.cleaned_data['url']
u'third'
>>> f.cleaned_data['name']
u'Third test'
>>> f.cleaned_data['slug']
u'third-test'
>>> obj = f.save(commit=False)
>>> obj
<Category: Third test>
@ -272,8 +284,10 @@ True
If you call save() with invalid data, you'll get a ValueError.
>>> f = CategoryForm({'name': '', 'slug': '', 'url': 'foo'})
>>> f.errors
{'name': [u'This field is required.'], 'slug': [u'This field is required.']}
>>> f.errors['name']
[u'This field is required.']
>>> f.errors['slug']
[u'This field is required.']
>>> f.cleaned_data
Traceback (most recent call last):
...
@ -645,6 +659,19 @@ Traceback (most recent call last):
...
ValidationError: [u'Select a valid choice. That choice is not one of the available choices.']
# check that we can safely iterate choices repeatedly
>>> gen_one = list(f.choices)
>>> gen_two = f.choices
>>> gen_one[2]
(2L, u"It's a test")
>>> list(gen_two)
[(u'', u'---------'), (1L, u'Entertainment'), (2L, u"It's a test"), (3L, u'Third')]
# check that we can override the label_from_instance method to print custom labels (#4620)
>>> f.queryset = Category.objects.all()
>>> f.label_from_instance = lambda obj: "category " + str(obj)
>>> list(f.choices)
[(u'', u'---------'), (1L, 'category Entertainment'), (2L, "category It's a test"), (3L, 'category Third'), (4L, 'category Fourth')]
# ModelMultipleChoiceField ####################################################
@ -730,6 +757,10 @@ Traceback (most recent call last):
...
ValidationError: [u'Select a valid choice. 4 is not one of the available choices.']
>>> f.queryset = Category.objects.all()
>>> f.label_from_instance = lambda obj: "multicategory " + str(obj)
>>> list(f.choices)
[(1L, 'multicategory Entertainment'), (2L, "multicategory It's a test"), (3L, 'multicategory Third'), (4L, 'multicategory Fourth')]
# PhoneNumberField ############################################################
@ -739,8 +770,10 @@ ValidationError: [u'Select a valid choice. 4 is not one of the available choices
>>> f = PhoneNumberForm({'phone': '(312) 555-1212', 'description': 'Assistance'})
>>> f.is_valid()
True
>>> f.cleaned_data
{'phone': u'312-555-1212', 'description': u'Assistance'}
>>> f.cleaned_data['phone']
u'312-555-1212'
>>> f.cleaned_data['description']
u'Assistance'
# FileField ###################################################################
@ -766,7 +799,7 @@ True
<class 'django.newforms.fields.UploadedFile'>
>>> instance = f.save()
>>> instance.file
u'.../test1.txt'
u'...test1.txt'
# Edit an instance that already has the file defined in the model. This will not
# save the file again, but leave it exactly as it is.
@ -775,10 +808,10 @@ u'.../test1.txt'
>>> f.is_valid()
True
>>> f.cleaned_data['file']
u'.../test1.txt'
u'...test1.txt'
>>> instance = f.save()
>>> instance.file
u'.../test1.txt'
u'...test1.txt'
# Delete the current file since this is not done by Django.
@ -791,7 +824,7 @@ u'.../test1.txt'
True
>>> instance = f.save()
>>> instance.file
u'.../test2.txt'
u'...test2.txt'
>>> instance.delete()
@ -810,7 +843,7 @@ True
True
>>> instance = f.save()
>>> instance.file
u'.../test3.txt'
u'...test3.txt'
>>> instance.delete()
# ImageField ###################################################################
@ -832,7 +865,7 @@ True
<class 'django.newforms.fields.UploadedFile'>
>>> instance = f.save()
>>> instance.image
u'.../test.png'
u'...test.png'
# Edit an instance that already has the image defined in the model. This will not
# save the image again, but leave it exactly as it is.
@ -841,10 +874,10 @@ u'.../test.png'
>>> f.is_valid()
True
>>> f.cleaned_data['image']
u'.../test.png'
u'...test.png'
>>> instance = f.save()
>>> instance.image
u'.../test.png'
u'...test.png'
# Delete the current image since this is not done by Django.
@ -857,7 +890,7 @@ u'.../test.png'
True
>>> instance = f.save()
>>> instance.image
u'.../test2.png'
u'...test2.png'
>>> instance.delete()
@ -876,7 +909,7 @@ True
True
>>> instance = f.save()
>>> instance.image
u'.../test3.png'
u'...test3.png'
>>> instance.delete()
"""}

View File

@ -4,6 +4,11 @@
Django provides a framework for paginating a list of objects in a few lines
of code. This is often useful for dividing search results or long lists of
objects into easily readable pages.
In Django 0.96 and earlier, a single ObjectPaginator class implemented this
functionality. In the Django development version, the behavior is split across
two classes -- Paginator and Page -- that are more easier to use. The legacy
ObjectPaginator class is deprecated.
"""
from django.db import models
@ -16,70 +21,210 @@ class Article(models.Model):
return self.headline
__test__ = {'API_TESTS':"""
# prepare a list of objects for pagination
# Prepare a list of objects for pagination.
>>> from datetime import datetime
>>> for x in range(1, 10):
... a = Article(headline='Article %s' % x, pub_date=datetime(2005, 7, 29))
... a.save()
# create a basic paginator, 5 articles per page
####################################
# New/current API (Paginator/Page) #
####################################
>>> from django.core.paginator import Paginator, InvalidPage
>>> paginator = Paginator(Article.objects.all(), 5)
>>> paginator.count
9
>>> paginator.num_pages
2
>>> paginator.page_range
[1, 2]
# Get the first page.
>>> p = paginator.page(1)
>>> p
<Page 1 of 2>
>>> p.object_list
[<Article: Article 1>, <Article: Article 2>, <Article: Article 3>, <Article: Article 4>, <Article: Article 5>]
>>> p.has_next()
True
>>> p.has_previous()
False
>>> p.has_other_pages()
True
>>> p.next_page_number()
2
>>> p.previous_page_number()
0
>>> p.start_index()
1
>>> p.end_index()
5
# Get the second page.
>>> p = paginator.page(2)
>>> p
<Page 2 of 2>
>>> p.object_list
[<Article: Article 6>, <Article: Article 7>, <Article: Article 8>, <Article: Article 9>]
>>> p.has_next()
False
>>> p.has_previous()
True
>>> p.has_other_pages()
True
>>> p.next_page_number()
3
>>> p.previous_page_number()
1
>>> p.start_index()
6
>>> p.end_index()
9
# Invalid pages raise InvalidPage.
>>> paginator.page(0)
Traceback (most recent call last):
...
InvalidPage: ...
>>> paginator.page(3)
Traceback (most recent call last):
...
InvalidPage: ...
# Empty paginators with allow_empty_first_page=True.
>>> paginator = Paginator(Article.objects.filter(id=0), 5, allow_empty_first_page=True)
>>> paginator.count
0
>>> paginator.num_pages
1
>>> paginator.page_range
[1]
# Empty paginators with allow_empty_first_page=False.
>>> paginator = Paginator(Article.objects.filter(id=0), 5, allow_empty_first_page=False)
>>> paginator.count
0
>>> paginator.num_pages
0
>>> paginator.page_range
[]
# Paginators work with regular lists/tuples, too -- not just with QuerySets.
>>> paginator = Paginator([1, 2, 3, 4, 5, 6, 7, 8, 9], 5)
>>> paginator.count
9
>>> paginator.num_pages
2
>>> paginator.page_range
[1, 2]
# Get the first page.
>>> p = paginator.page(1)
>>> p
<Page 1 of 2>
>>> p.object_list
[1, 2, 3, 4, 5]
>>> p.has_next()
True
>>> p.has_previous()
False
>>> p.has_other_pages()
True
>>> p.next_page_number()
2
>>> p.previous_page_number()
0
>>> p.start_index()
1
>>> p.end_index()
5
################################
# Legacy API (ObjectPaginator) #
################################
# Don't print out the deprecation warnings during testing.
>>> from warnings import filterwarnings
>>> filterwarnings("ignore")
>>> from django.core.paginator import ObjectPaginator, InvalidPage
>>> paginator = ObjectPaginator(Article.objects.all(), 5)
# the paginator knows how many hits and pages it contains
>>> paginator.hits
9
>>> paginator.pages
2
>>> paginator.page_range
[1, 2]
# get the first page (zero-based)
# Get the first page.
>>> paginator.get_page(0)
[<Article: Article 1>, <Article: Article 2>, <Article: Article 3>, <Article: Article 4>, <Article: Article 5>]
# get the second page
>>> paginator.get_page(1)
[<Article: Article 6>, <Article: Article 7>, <Article: Article 8>, <Article: Article 9>]
# does the first page have a next or previous page?
>>> paginator.has_next_page(0)
True
>>> paginator.has_previous_page(0)
False
# check the second page
>>> paginator.has_next_page(1)
False
>>> paginator.has_previous_page(1)
True
>>> paginator.first_on_page(0)
1
>>> paginator.first_on_page(1)
6
>>> paginator.last_on_page(0)
5
# Get the second page.
>>> paginator.get_page(1)
[<Article: Article 6>, <Article: Article 7>, <Article: Article 8>, <Article: Article 9>]
>>> paginator.has_next_page(1)
False
>>> paginator.has_previous_page(1)
True
>>> paginator.first_on_page(1)
6
>>> paginator.last_on_page(1)
9
# Invalid pages raise InvalidPage.
>>> paginator.get_page(-1)
Traceback (most recent call last):
...
InvalidPage: ...
>>> paginator.get_page(2)
Traceback (most recent call last):
...
InvalidPage: ...
# Empty paginators with allow_empty_first_page=True.
>>> paginator = ObjectPaginator(Article.objects.filter(id=0), 5)
>>> paginator.count
0
>>> paginator.num_pages
1
>>> paginator.page_range
[1]
##################
# Orphan support #
##################
# Add a few more records to test out the orphans feature.
>>> for x in range(10, 13):
... Article(headline="Article %s" % x, pub_date=datetime(2006, 10, 6)).save()
# With orphans set to 3 and 10 items per page, we should get all 12 items on a single page:
# With orphans set to 3 and 10 items per page, we should get all 12 items on a single page.
>>> paginator = Paginator(Article.objects.all(), 10, orphans=3)
>>> paginator.num_pages
1
# With orphans only set to 1, we should get two pages.
>>> paginator = ObjectPaginator(Article.objects.all(), 10, orphans=1)
>>> paginator.num_pages
2
# LEGACY: With orphans set to 3 and 10 items per page, we should get all 12 items on a single page.
>>> paginator = ObjectPaginator(Article.objects.all(), 10, orphans=3)
>>> paginator.pages
1
# With orphans only set to 1, we should get two pages:
# LEGACY: With orphans only set to 1, we should get two pages.
>>> paginator = ObjectPaginator(Article.objects.all(), 10, orphans=1)
>>> paginator.pages
2
# The paginator can provide a list of all available pages.
>>> paginator = ObjectPaginator(Article.objects.all(), 10)
>>> paginator.page_range
[1, 2]
"""}

View File

@ -41,8 +41,8 @@ __test__ = {'API_TESTS':"""
23
>>> p = Person(**dict(valid_params, id='foo'))
>>> p.validate()
{'id': [u'This value must be an integer.']}
>>> p.validate()['id']
[u'This value must be an integer.']
>>> p = Person(**dict(valid_params, id=None))
>>> p.validate()
@ -75,8 +75,8 @@ True
False
>>> p = Person(**dict(valid_params, is_child='foo'))
>>> p.validate()
{'is_child': [u'This value must be either True or False.']}
>>> p.validate()['is_child']
[u'This value must be either True or False.']
>>> p = Person(**dict(valid_params, name=u'Jose'))
>>> p.validate()
@ -115,8 +115,8 @@ datetime.date(2000, 5, 3)
datetime.date(2000, 5, 3)
>>> p = Person(**dict(valid_params, birthdate='foo'))
>>> p.validate()
{'birthdate': [u'Enter a valid date in YYYY-MM-DD format.']}
>>> p.validate()['birthdate']
[u'Enter a valid date in YYYY-MM-DD format.']
>>> p = Person(**dict(valid_params, favorite_moment=datetime.datetime(2002, 4, 3, 13, 23)))
>>> p.validate()
@ -143,11 +143,15 @@ datetime.datetime(2002, 4, 3, 0, 0)
u'john@example.com'
>>> p = Person(**dict(valid_params, email=22))
>>> p.validate()
{'email': [u'Enter a valid e-mail address.']}
>>> p.validate()['email']
[u'Enter a valid e-mail address.']
# Make sure that Date and DateTime return validation errors and don't raise Python errors.
>>> Person(name='John Doe', is_child=True, email='abc@def.com').validate()
{'favorite_moment': [u'This field is required.'], 'birthdate': [u'This field is required.']}
>>> p = Person(name='John Doe', is_child=True, email='abc@def.com')
>>> errors = p.validate()
>>> errors['favorite_moment']
[u'This field is required.']
>>> errors['birthdate']
[u'This field is required.']
"""}

View File

@ -22,7 +22,7 @@ classes that demonstrate some of the library's abilities.
>>> from django.newforms.extras import SelectDateWidget
>>> w = SelectDateWidget(years=('2007','2008','2009','2010','2011','2012','2013','2014','2015','2016'))
>>> print w.render('mydate', '')
<select name="mydate_month">
<select name="mydate_month" id="id_mydate_month">
<option value="1">January</option>
<option value="2">February</option>
<option value="3">March</option>
@ -36,7 +36,7 @@ classes that demonstrate some of the library's abilities.
<option value="11">November</option>
<option value="12">December</option>
</select>
<select name="mydate_day">
<select name="mydate_day" id="id_mydate_day">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
@ -69,7 +69,7 @@ classes that demonstrate some of the library's abilities.
<option value="30">30</option>
<option value="31">31</option>
</select>
<select name="mydate_year">
<select name="mydate_year" id="id_mydate_year">
<option value="2007">2007</option>
<option value="2008">2008</option>
<option value="2009">2009</option>
@ -84,7 +84,7 @@ classes that demonstrate some of the library's abilities.
>>> w.render('mydate', None) == w.render('mydate', '')
True
>>> print w.render('mydate', '2010-04-15')
<select name="mydate_month">
<select name="mydate_month" id="id_mydate_month">
<option value="1">January</option>
<option value="2">February</option>
<option value="3">March</option>
@ -98,7 +98,7 @@ True
<option value="11">November</option>
<option value="12">December</option>
</select>
<select name="mydate_day">
<select name="mydate_day" id="id_mydate_day">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
@ -131,7 +131,74 @@ True
<option value="30">30</option>
<option value="31">31</option>
</select>
<select name="mydate_year">
<select name="mydate_year" id="id_mydate_year">
<option value="2007">2007</option>
<option value="2008">2008</option>
<option value="2009">2009</option>
<option value="2010" selected="selected">2010</option>
<option value="2011">2011</option>
<option value="2012">2012</option>
<option value="2013">2013</option>
<option value="2014">2014</option>
<option value="2015">2015</option>
<option value="2016">2016</option>
</select>
Accepts a datetime or a string:
>>> w.render('mydate', datetime.date(2010, 4, 15)) == w.render('mydate', '2010-04-15')
True
Invalid dates still render the failed date:
>>> print w.render('mydate', '2010-02-31')
<select name="mydate_month" id="id_mydate_month">
<option value="1">January</option>
<option value="2" selected="selected">February</option>
<option value="3">March</option>
<option value="4">April</option>
<option value="5">May</option>
<option value="6">June</option>
<option value="7">July</option>
<option value="8">August</option>
<option value="9">September</option>
<option value="10">October</option>
<option value="11">November</option>
<option value="12">December</option>
</select>
<select name="mydate_day" id="id_mydate_day">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
<option value="7">7</option>
<option value="8">8</option>
<option value="9">9</option>
<option value="10">10</option>
<option value="11">11</option>
<option value="12">12</option>
<option value="13">13</option>
<option value="14">14</option>
<option value="15">15</option>
<option value="16">16</option>
<option value="17">17</option>
<option value="18">18</option>
<option value="19">19</option>
<option value="20">20</option>
<option value="21">21</option>
<option value="22">22</option>
<option value="23">23</option>
<option value="24">24</option>
<option value="25">25</option>
<option value="26">26</option>
<option value="27">27</option>
<option value="28">28</option>
<option value="29">29</option>
<option value="30">30</option>
<option value="31" selected="selected">31</option>
</select>
<select name="mydate_year" id="id_mydate_year">
<option value="2007">2007</option>
<option value="2008">2008</option>
<option value="2009">2009</option>
@ -252,8 +319,8 @@ ValidationError: [u'This field is required.']
</select>
<input type="text" name="field1_2_0" value="2007-04-25" id="id_field1_2_0" /><input type="text" name="field1_2_1" value="06:24:00" id="id_field1_2_1" /></td></tr>
>>> f.cleaned_data
{'field1': u'some text,JP,2007-04-25 06:24:00'}
>>> f.cleaned_data['field1']
u'some text,JP,2007-04-25 06:24:00'
# IPAddressField ##################################################################

View File

@ -1133,6 +1133,33 @@ u''
>>> f.clean(None)
u''
# FilePathField ###############################################################
>>> import os
>>> from django import newforms as forms
>>> path = forms.__file__
>>> path = os.path.dirname(path) + '/'
>>> path
'.../django/newforms/'
>>> f = forms.FilePathField(path=path)
>>> f.choices.sort()
>>> f.choices
[('.../django/newforms/__init__.py', '__init__.py'), ('.../django/newforms/__init__.pyc', '__init__.pyc'), ('.../django/newforms/fields.py', 'fields.py'), ('.../django/newforms/fields.pyc', 'fields.pyc'), ('.../django/newforms/forms.py', 'forms.py'), ('.../django/newforms/forms.pyc', 'forms.pyc'), ('.../django/newforms/models.py', 'models.py'), ('.../django/newforms/models.pyc', 'models.pyc'), ('.../django/newforms/util.py', 'util.py'), ('.../django/newforms/util.pyc', 'util.pyc'), ('.../django/newforms/widgets.py', 'widgets.py'), ('.../django/newforms/widgets.pyc', 'widgets.pyc')]
>>> f.clean('fields.py')
Traceback (most recent call last):
...
ValidationError: [u'Select a valid choice. That choice is not one of the available choices.']
>>> f.clean(path + 'fields.py')
u'.../django/newforms/fields.py'
>>> f = forms.FilePathField(path=path, match='^.*?\.py$')
>>> f.choices.sort()
>>> f.choices
[('.../django/newforms/__init__.py', '__init__.py'), ('.../django/newforms/fields.py', 'fields.py'), ('.../django/newforms/forms.py', 'forms.py'), ('.../django/newforms/models.py', 'models.py'), ('.../django/newforms/util.py', 'util.py'), ('.../django/newforms/widgets.py', 'widgets.py')]
>>> f = forms.FilePathField(path=path, recursive=True, match='^.*?\.py$')
>>> f.choices.sort()
>>> f.choices
[('.../django/newforms/__init__.py', '__init__.py'), ('.../django/newforms/extras/__init__.py', 'extras/__init__.py'), ('.../django/newforms/extras/widgets.py', 'extras/widgets.py'), ('.../django/newforms/fields.py', 'fields.py'), ('.../django/newforms/forms.py', 'forms.py'), ('.../django/newforms/models.py', 'models.py'), ('.../django/newforms/util.py', 'util.py'), ('.../django/newforms/widgets.py', 'widgets.py')]
# SplitDateTimeField ##########################################################
>>> f = SplitDateTimeField()

View File

@ -36,8 +36,8 @@ True
u''
>>> p.errors.as_text()
u''
>>> p.cleaned_data
{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)}
>>> p.cleaned_data["first_name"], p.cleaned_data["last_name"], p.cleaned_data["birthday"]
(u'John', u'Lennon', datetime.date(1940, 10, 9))
>>> print p['first_name']
<input type="text" name="first_name" value="John" id="id_first_name" />
>>> print p['last_name']
@ -68,8 +68,12 @@ Empty dictionaries are valid, too.
>>> p = Person({})
>>> p.is_bound
True
>>> p.errors
{'first_name': [u'This field is required.'], 'last_name': [u'This field is required.'], 'birthday': [u'This field is required.']}
>>> p.errors['first_name']
[u'This field is required.']
>>> p.errors['last_name']
[u'This field is required.']
>>> p.errors['birthday']
[u'This field is required.']
>>> p.is_valid()
False
>>> p.cleaned_data
@ -137,8 +141,10 @@ u'<li><label for="id_first_name">First name:</label> <input type="text" name="fi
u'<p><label for="id_first_name">First name:</label> <input type="text" name="first_name" value="John" id="id_first_name" /></p>\n<p><label for="id_last_name">Last name:</label> <input type="text" name="last_name" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" id="id_last_name" /></p>\n<p><label for="id_birthday">Birthday:</label> <input type="text" name="birthday" value="1940-10-9" id="id_birthday" /></p>'
>>> p = Person({'last_name': u'Lennon'})
>>> p.errors
{'first_name': [u'This field is required.'], 'birthday': [u'This field is required.']}
>>> p.errors['first_name']
[u'This field is required.']
>>> p.errors['birthday']
[u'This field is required.']
>>> p.is_valid()
False
>>> p.errors.as_ul()
@ -175,8 +181,13 @@ but cleaned_data contains only the form's fields.
>>> p = Person(data)
>>> p.is_valid()
True
>>> p.cleaned_data
{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)}
>>> p.cleaned_data['first_name']
u'John'
>>> p.cleaned_data['last_name']
u'Lennon'
>>> p.cleaned_data['birthday']
datetime.date(1940, 10, 9)
cleaned_data will include a key and value for *all* fields defined in the Form,
even if the Form's data didn't include a value for fields that are not
@ -191,8 +202,12 @@ empty string.
>>> f = OptionalPersonForm(data)
>>> f.is_valid()
True
>>> f.cleaned_data
{'nick_name': u'', 'first_name': u'John', 'last_name': u'Lennon'}
>>> f.cleaned_data['nick_name']
u''
>>> f.cleaned_data['first_name']
u'John'
>>> f.cleaned_data['last_name']
u'Lennon'
For DateFields, it's set to None.
>>> class OptionalPersonForm(Form):
@ -203,8 +218,12 @@ For DateFields, it's set to None.
>>> f = OptionalPersonForm(data)
>>> f.is_valid()
True
>>> f.cleaned_data
{'birth_date': None, 'first_name': u'John', 'last_name': u'Lennon'}
>>> print f.cleaned_data['birth_date']
None
>>> f.cleaned_data['first_name']
u'John'
>>> f.cleaned_data['last_name']
u'Lennon'
"auto_id" tells the Form to add an "id" attribute to each form element.
If it's a string that contains '%s', Django will use that as a format string
@ -549,18 +568,22 @@ The MultipleHiddenInput widget renders multiple values as hidden fields.
When using CheckboxSelectMultiple, the framework expects a list of input and
returns a list of input.
>>> f = SongForm({'name': 'Yesterday'}, auto_id=False)
>>> f.errors
{'composers': [u'This field is required.']}
>>> f.errors['composers']
[u'This field is required.']
>>> f = SongForm({'name': 'Yesterday', 'composers': ['J']}, auto_id=False)
>>> f.errors
{}
>>> f.cleaned_data
{'composers': [u'J'], 'name': u'Yesterday'}
>>> f.cleaned_data['composers']
[u'J']
>>> f.cleaned_data['name']
u'Yesterday'
>>> f = SongForm({'name': 'Yesterday', 'composers': ['J', 'P']}, auto_id=False)
>>> f.errors
{}
>>> f.cleaned_data
{'composers': [u'J', u'P'], 'name': u'Yesterday'}
>>> f.cleaned_data['composers']
[u'J', u'P']
>>> f.cleaned_data['name']
u'Yesterday'
Validation errors are HTML-escaped when output as HTML.
>>> class EscapingForm(Form):
@ -598,16 +621,24 @@ including the current field (e.g., the field XXX if you're in clean_XXX()).
>>> f.errors
{}
>>> f = UserRegistration({}, auto_id=False)
>>> f.errors
{'username': [u'This field is required.'], 'password1': [u'This field is required.'], 'password2': [u'This field is required.']}
>>> f.errors['username']
[u'This field is required.']
>>> f.errors['password1']
[u'This field is required.']
>>> f.errors['password2']
[u'This field is required.']
>>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'bar'}, auto_id=False)
>>> f.errors
{'password2': [u'Please make sure your passwords match.']}
>>> f.errors['password2']
[u'Please make sure your passwords match.']
>>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'}, auto_id=False)
>>> f.errors
{}
>>> f.cleaned_data
{'username': u'adrian', 'password1': u'foo', 'password2': u'foo'}
>>> f.cleaned_data['username']
u'adrian'
>>> f.cleaned_data['password1']
u'foo'
>>> f.cleaned_data['password2']
u'foo'
Another way of doing multiple-field validation is by implementing the
Form's clean() method. If you do this, any ValidationError raised by that
@ -632,11 +663,15 @@ Form.clean() is required to return a dictionary of all clean data.
<tr><th>Username:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="username" maxlength="10" /></td></tr>
<tr><th>Password1:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="password" name="password1" /></td></tr>
<tr><th>Password2:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="password" name="password2" /></td></tr>
>>> f.errors
{'username': [u'This field is required.'], 'password1': [u'This field is required.'], 'password2': [u'This field is required.']}
>>> f.errors['username']
[u'This field is required.']
>>> f.errors['password1']
[u'This field is required.']
>>> f.errors['password2']
[u'This field is required.']
>>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'bar'}, auto_id=False)
>>> f.errors
{'__all__': [u'Please make sure your passwords match.']}
>>> f.errors['__all__']
[u'Please make sure your passwords match.']
>>> print f.as_table()
<tr><td colspan="2"><ul class="errorlist"><li>Please make sure your passwords match.</li></ul></td></tr>
<tr><th>Username:</th><td><input type="text" name="username" value="adrian" maxlength="10" /></td></tr>
@ -650,8 +685,12 @@ Form.clean() is required to return a dictionary of all clean data.
>>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'}, auto_id=False)
>>> f.errors
{}
>>> f.cleaned_data
{'username': u'adrian', 'password1': u'foo', 'password2': u'foo'}
>>> f.cleaned_data['username']
u'adrian'
>>> f.cleaned_data['password1']
u'foo'
>>> f.cleaned_data['password2']
u'foo'
# Dynamic construction ########################################################
@ -1024,8 +1063,8 @@ An 'initial' value is *not* used as a fallback if data is not provided. In this
example, we don't provide a value for 'username', and the form raises a
validation error rather than using the initial value for 'username'.
>>> p = UserRegistration({'password': 'secret'})
>>> p.errors
{'username': [u'This field is required.']}
>>> p.errors['username']
[u'This field is required.']
>>> p.is_valid()
False
@ -1069,8 +1108,8 @@ A dynamic 'initial' value is *not* used as a fallback if data is not provided.
In this example, we don't provide a value for 'username', and the form raises a
validation error rather than using the initial value for 'username'.
>>> p = UserRegistration({'password': 'secret'}, initial={'username': 'django'})
>>> p.errors
{'username': [u'This field is required.']}
>>> p.errors['username']
[u'This field is required.']
>>> p.is_valid()
False
@ -1123,8 +1162,8 @@ A callable 'initial' value is *not* used as a fallback if data is not provided.
In this example, we don't provide a value for 'username', and the form raises a
validation error rather than using the initial value for 'username'.
>>> p = UserRegistration({'password': 'secret'}, initial={'username': initial_django})
>>> p.errors
{'username': [u'This field is required.']}
>>> p.errors['username']
[u'This field is required.']
>>> p.is_valid()
False
@ -1258,8 +1297,12 @@ actual field name.
{}
>>> p.is_valid()
True
>>> p.cleaned_data
{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)}
>>> p.cleaned_data['first_name']
u'John'
>>> p.cleaned_data['last_name']
u'Lennon'
>>> p.cleaned_data['birthday']
datetime.date(1940, 10, 9)
Let's try submitting some bad data to make sure form.errors and field.errors
work as expected.
@ -1269,8 +1312,12 @@ work as expected.
... 'person1-birthday': u''
... }
>>> p = Person(data, prefix='person1')
>>> p.errors
{'first_name': [u'This field is required.'], 'last_name': [u'This field is required.'], 'birthday': [u'This field is required.']}
>>> p.errors['first_name']
[u'This field is required.']
>>> p.errors['last_name']
[u'This field is required.']
>>> p.errors['birthday']
[u'This field is required.']
>>> p['first_name'].errors
[u'This field is required.']
>>> p['person1-first_name'].errors
@ -1286,8 +1333,12 @@ the form doesn't "see" the fields.
... 'birthday': u'1940-10-9'
... }
>>> p = Person(data, prefix='person1')
>>> p.errors
{'first_name': [u'This field is required.'], 'last_name': [u'This field is required.'], 'birthday': [u'This field is required.']}
>>> p.errors['first_name']
[u'This field is required.']
>>> p.errors['last_name']
[u'This field is required.']
>>> p.errors['birthday']
[u'This field is required.']
With prefixes, a single data dictionary can hold data for multiple instances
of the same form.
@ -1302,13 +1353,21 @@ of the same form.
>>> p1 = Person(data, prefix='person1')
>>> p1.is_valid()
True
>>> p1.cleaned_data
{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)}
>>> p1.cleaned_data['first_name']
u'John'
>>> p1.cleaned_data['last_name']
u'Lennon'
>>> p1.cleaned_data['birthday']
datetime.date(1940, 10, 9)
>>> p2 = Person(data, prefix='person2')
>>> p2.is_valid()
True
>>> p2.cleaned_data
{'first_name': u'Jim', 'last_name': u'Morrison', 'birthday': datetime.date(1943, 12, 8)}
>>> p2.cleaned_data['first_name']
u'Jim'
>>> p2.cleaned_data['last_name']
u'Morrison'
>>> p2.cleaned_data['birthday']
datetime.date(1943, 12, 8)
By default, forms append a hyphen between the prefix and the field name, but a
form can alter that behavior by implementing the add_prefix() method. This
@ -1333,8 +1392,12 @@ self.prefix.
>>> p = Person(data, prefix='foo')
>>> p.is_valid()
True
>>> p.cleaned_data
{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)}
>>> p.cleaned_data['first_name']
u'John'
>>> p.cleaned_data['last_name']
u'Lennon'
>>> p.cleaned_data['birthday']
datetime.date(1940, 10, 9)
# Forms with NullBooleanFields ################################################

View File

@ -0,0 +1,24 @@
from django.db import models
class Foo(models.Model):
a = models.CharField(max_length=10)
def get_foo():
return Foo.objects.get(id=1)
class Bar(models.Model):
b = models.CharField(max_length=10)
a = models.ForeignKey(Foo, default=get_foo)
__test__ = {'API_TESTS':"""
# Create a couple of Places.
>>> f = Foo.objects.create(a='abc')
>>> f.id
1
>>> b = Bar(b = "bcd")
>>> b.a
<Foo: Foo object>
>>> b.save()
"""}

View File

@ -0,0 +1,14 @@
# -*- coding: utf-8 -*-
from django.test import TestCase
from django.test.client import Client
class SyndicationFeedTest(TestCase):
def test_complex_base_url(self):
"""
Tests that that the base url for a complex feed doesn't raise a 500
exception.
"""
c = Client()
response = c.get('/syndication/feeds/complex/')
self.assertEquals(response.status_code, 404)

View File

@ -0,0 +1,18 @@
from django.conf.urls.defaults import patterns
from django.core.exceptions import ObjectDoesNotExist
from django.contrib.syndication import feeds
class ComplexFeed(feeds.Feed):
def get_object(self, bits):
if len(bits) != 1:
raise ObjectDoesNotExist
return None
urlpatterns = patterns('',
(r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed', {
'feed_dict': dict(
complex = ComplexFeed,
)}),
)

View File

@ -3,7 +3,7 @@ Regression tests for the Test Client, especially the customized assertions.
"""
from django.test import Client, TestCase
from django.core import mail
from django.core.urlresolvers import reverse
import os
class AssertContainsTests(TestCase):
@ -261,3 +261,31 @@ class LoginTests(TestCase):
# Check that assertRedirects uses the original client, not the
# default client.
self.assertRedirects(response, "http://testserver/test_client_regress/get_view/")
class URLEscapingTests(TestCase):
def test_simple_argument_get(self):
"Get a view that has a simple string argument"
response = self.client.get(reverse('arg_view', args=['Slartibartfast']))
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, 'Howdy, Slartibartfast')
def test_argument_with_space_get(self):
"Get a view that has a string argument that requires escaping"
response = self.client.get(reverse('arg_view', args=['Arthur Dent']))
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, 'Hi, Arthur')
def test_simple_argument_post(self):
"Post for a view that has a simple string argument"
response = self.client.post(reverse('arg_view', args=['Slartibartfast']))
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, 'Howdy, Slartibartfast')
def test_argument_with_space_post(self):
"Post for a view that has a string argument that requires escaping"
response = self.client.post(reverse('arg_view', args=['Arthur Dent']))
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, 'Hi, Arthur')

View File

@ -5,5 +5,6 @@ urlpatterns = patterns('',
(r'^no_template_view/$', views.no_template_view),
(r'^file_upload/$', views.file_upload_view),
(r'^get_view/$', views.get_view),
url(r'^arg_view/(?P<name>.+)/$', views.view_with_argument, name='arg_view'),
(r'^login_protected_redirect_view/$', views.login_protected_redirect_view)
)

View File

@ -1,7 +1,5 @@
from django.contrib.auth.decorators import login_required
from django.core.mail import EmailMessage, SMTPConnection
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseServerError
from django.shortcuts import render_to_response
def no_template_view(request):
"A simple view that expects a GET request, and returns a rendered template"
@ -20,10 +18,22 @@ def file_upload_view(request):
return HttpResponseServerError()
def get_view(request):
"A simple login protected view"
"A simple login protected view"
return HttpResponse("Hello world")
get_view = login_required(get_view)
def view_with_argument(request, name):
"""A view that takes a string argument
The purpose of this view is to check that if a space is provided in
the argument, the test framework unescapes the %20 before passing
the value to the view.
"""
if name == 'Arthur Dent':
return HttpResponse('Hi, Arthur')
else:
return HttpResponse('Howdy, %s' % name)
def login_protected_redirect_view(request):
"A view that redirects all requests to the GET view"
return HttpResponseRedirect('/test_client_regress/get_view/')

View File

@ -19,4 +19,7 @@ urlpatterns = patterns('',
(r'^middleware/', include('regressiontests.middleware.urls')),
(r'^utils/', include('regressiontests.utils.urls')),
# test urlconf for syndication tests
(r'^syndication/', include('regressiontests.syndication.urls')),
)