mirror of
https://github.com/django/django.git
synced 2025-07-04 01:39:20 +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:
parent
b0a895f9e8
commit
2efc34dc5e
6
AUTHORS
6
AUTHORS
@ -59,6 +59,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
Arthur <avandorp@gmail.com>
|
Arthur <avandorp@gmail.com>
|
||||||
David Avsajanishvili <avsd05@gmail.com>
|
David Avsajanishvili <avsd05@gmail.com>
|
||||||
axiak@mit.edu
|
axiak@mit.edu
|
||||||
|
Niran Babalola <niran@niran.org>
|
||||||
Morten Bagai <m@bagai.com>
|
Morten Bagai <m@bagai.com>
|
||||||
Mikaël Barbero <mikael.barbero nospam at nospam free.fr>
|
Mikaël Barbero <mikael.barbero nospam at nospam free.fr>
|
||||||
Jiri Barton
|
Jiri Barton
|
||||||
@ -145,6 +146,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
Jorge Gajon <gajon@gajon.org>
|
Jorge Gajon <gajon@gajon.org>
|
||||||
gandalf@owca.info
|
gandalf@owca.info
|
||||||
Marc Garcia <marc.garcia@accopensys.com>
|
Marc Garcia <marc.garcia@accopensys.com>
|
||||||
|
Alex Gaynor <alex.gaynor@gmail.com>
|
||||||
Andy Gayton <andy-django@thecablelounge.com>
|
Andy Gayton <andy-django@thecablelounge.com>
|
||||||
Baishampayan Ghose
|
Baishampayan Ghose
|
||||||
Dimitris Glezos <dimitris@glezos.com>
|
Dimitris Glezos <dimitris@glezos.com>
|
||||||
@ -243,6 +245,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
michael.mcewan@gmail.com
|
michael.mcewan@gmail.com
|
||||||
michal@plovarna.cz
|
michal@plovarna.cz
|
||||||
Mikko Hellsing <mikko@sorl.net>
|
Mikko Hellsing <mikko@sorl.net>
|
||||||
|
Daniel Lindsley <polarcowz@gmail.com>
|
||||||
Orestis Markou <orestis@orestis.gr>
|
Orestis Markou <orestis@orestis.gr>
|
||||||
Slawek Mikula <slawek dot mikula at gmail dot com>
|
Slawek Mikula <slawek dot mikula at gmail dot com>
|
||||||
mitakummaa@gmail.com
|
mitakummaa@gmail.com
|
||||||
@ -256,6 +259,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
Robin Munn <http://www.geekforgod.com/>
|
Robin Munn <http://www.geekforgod.com/>
|
||||||
Robert Myers <myer0052@gmail.com>
|
Robert Myers <myer0052@gmail.com>
|
||||||
Nebojša Dorđević
|
Nebojša Dorđević
|
||||||
|
Doug Napoleone <doug@dougma.com>
|
||||||
Gopal Narayanan <gopastro@gmail.com>
|
Gopal Narayanan <gopastro@gmail.com>
|
||||||
Fraser Nevett <mail@nevett.org>
|
Fraser Nevett <mail@nevett.org>
|
||||||
Sam Newman <http://www.magpiebrain.com/>
|
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>
|
Barry Pederson <bp@barryp.org>
|
||||||
permonik@mesias.brnonet.cz
|
permonik@mesias.brnonet.cz
|
||||||
petr.marhoun@gmail.com
|
petr.marhoun@gmail.com
|
||||||
|
peter@mymart.com
|
||||||
pgross@thoughtworks.com
|
pgross@thoughtworks.com
|
||||||
phaedo <http://phaedo.cx/>
|
phaedo <http://phaedo.cx/>
|
||||||
phil@produxion.net
|
phil@produxion.net
|
||||||
@ -307,6 +312,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
serbaut@gmail.com
|
serbaut@gmail.com
|
||||||
John Shaffer <jshaffer2112@gmail.com>
|
John Shaffer <jshaffer2112@gmail.com>
|
||||||
Pete Shinners <pete@shinners.org>
|
Pete Shinners <pete@shinners.org>
|
||||||
|
Leo Shklovskii
|
||||||
jason.sidabras@gmail.com
|
jason.sidabras@gmail.com
|
||||||
Jozko Skrablin <jozko.skrablin@gmail.com>
|
Jozko Skrablin <jozko.skrablin@gmail.com>
|
||||||
Ben Slavin <benjamin.slavin@gmail.com>
|
Ben Slavin <benjamin.slavin@gmail.com>
|
||||||
|
@ -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_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_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_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 #
|
# CACHE #
|
||||||
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -1,27 +1,27 @@
|
|||||||
{% extends "admin/base_site.html" %}
|
{% extends "admin/base_site.html" %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block breadcrumbs %}<div class="breadcrumbs"><a href="../">Home</a> › Documentation</div>{% endblock %}
|
{% block breadcrumbs %}<div class="breadcrumbs"><a href="../">Home</a> › Documentation</div>{% endblock %}
|
||||||
{% block title %}Documentation{% endblock %}
|
{% block title %}Documentation{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<h1>Documentation</h1>
|
<h1>Documentation</h1>
|
||||||
|
|
||||||
<div id="content-main">
|
<div id="content-main">
|
||||||
<h3><a href="tags/">Tags</a></h3>
|
<h3><a href="tags/">Tags</a></h3>
|
||||||
<p>List of all the template tags and their functions.</p>
|
<p>List of all the template tags and their functions.</p>
|
||||||
|
|
||||||
<h3><a href="filters/">Filters</a></h3>
|
<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>
|
<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>
|
<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>
|
<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>
|
<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>
|
<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>
|
<h3><a href="bookmarklets/">Bookmarklets</a></h3>
|
||||||
<p>Tools for your browser to quickly access admin functionality.</p>
|
<p>Tools for your browser to quickly access admin functionality.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,42 +1,42 @@
|
|||||||
{% extends "admin/base_site.html" %}
|
{% extends "admin/base_site.html" %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block coltype %}colSM{% endblock %}
|
{% block coltype %}colSM{% endblock %}
|
||||||
{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">Home</a> › <a href="../">Documentation</a> › Views</div>{% endblock %}
|
{% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">Home</a> › <a href="../">Documentation</a> › Views</div>{% endblock %}
|
||||||
{% block title %}Views{% endblock %}
|
{% block title %}Views{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<h1>View documentation</h1>
|
<h1>View documentation</h1>
|
||||||
|
|
||||||
{% regroup views|dictsort:"site_id" by site as views_by_site %}
|
{% regroup views|dictsort:"site_id" by site as views_by_site %}
|
||||||
|
|
||||||
<div id="content-related" class="sidebar">
|
<div id="content-related" class="sidebar">
|
||||||
<div class="module">
|
<div class="module">
|
||||||
<h2>Jump to site</h2>
|
<h2>Jump to site</h2>
|
||||||
<ul>
|
<ul>
|
||||||
{% for site_views in views_by_site %}
|
{% for site_views in views_by_site %}
|
||||||
<li><a href="#site{{ site_views.grouper.id }}">{{ site_views.grouper.name }}</a></li>
|
<li><a href="#site{{ site_views.grouper.id }}">{{ site_views.grouper.name }}</a></li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="content-main">
|
<div id="content-main">
|
||||||
|
|
||||||
{% for site_views in views_by_site %}
|
{% for site_views in views_by_site %}
|
||||||
<div class="module">
|
<div class="module">
|
||||||
<h2 id="site{{ site_views.grouper.id }}">Views by URL on {{ site_views.grouper.name }}</h2>
|
<h2 id="site{{ site_views.grouper.id }}">Views by URL on {{ site_views.grouper.name }}</h2>
|
||||||
|
|
||||||
{% for view in site_views.list|dictsort:"url" %}
|
{% for view in site_views.list|dictsort:"url" %}
|
||||||
{% ifchanged %}
|
{% ifchanged %}
|
||||||
<h3><a href="{{ view.module }}.{{ view.name }}/">{{ view.url|escape }}</a></h3>
|
<h3><a href="{{ view.module }}.{{ view.name }}/">{{ view.url|escape }}</a></h3>
|
||||||
<p class="small quiet">View function: {{ view.module }}.{{ view.name }}</p>
|
<p class="small quiet">View function: {{ view.module }}.{{ view.name }}</p>
|
||||||
<p>{{ view.title }}</p>
|
<p>{{ view.title }}</p>
|
||||||
<hr />
|
<hr />
|
||||||
{% endifchanged %}
|
{% endifchanged %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
@ -1,246 +1,246 @@
|
|||||||
"""
|
"""
|
||||||
FormWizard class -- implements a multi-page form, validating between each
|
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
|
step and storing the form's state as HTML hidden fields so that no state is
|
||||||
stored on the server side.
|
stored on the server side.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django import newforms as forms
|
from django import newforms as forms
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
from django.shortcuts import render_to_response
|
from django.shortcuts import render_to_response
|
||||||
from django.template.context import RequestContext
|
from django.template.context import RequestContext
|
||||||
import cPickle as pickle
|
import cPickle as pickle
|
||||||
import md5
|
import md5
|
||||||
|
|
||||||
class FormWizard(object):
|
class FormWizard(object):
|
||||||
# Dictionary of extra template context variables.
|
# Dictionary of extra template context variables.
|
||||||
extra_context = {}
|
extra_context = {}
|
||||||
|
|
||||||
# The HTML (and POST data) field name for the "step" variable.
|
# The HTML (and POST data) field name for the "step" variable.
|
||||||
step_field_name="wizard_step"
|
step_field_name="wizard_step"
|
||||||
|
|
||||||
# METHODS SUBCLASSES SHOULDN'T OVERRIDE ###################################
|
# METHODS SUBCLASSES SHOULDN'T OVERRIDE ###################################
|
||||||
|
|
||||||
def __init__(self, form_list, initial=None):
|
def __init__(self, form_list, initial=None):
|
||||||
"form_list should be a list of Form classes (not instances)."
|
"form_list should be a list of Form classes (not instances)."
|
||||||
self.form_list = form_list[:]
|
self.form_list = form_list[:]
|
||||||
self.initial = initial or {}
|
self.initial = initial or {}
|
||||||
self.step = 0 # A zero-based counter keeping track of which step we're in.
|
self.step = 0 # A zero-based counter keeping track of which step we're in.
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "step: %d\nform_list: %s\ninitial_data: %s" % (self.step, self.form_list, self.initial)
|
return "step: %d\nform_list: %s\ninitial_data: %s" % (self.step, self.form_list, self.initial)
|
||||||
|
|
||||||
def get_form(self, step, data=None):
|
def get_form(self, step, data=None):
|
||||||
"Helper method that returns the Form instance for the given step."
|
"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))
|
return self.form_list[step](data, prefix=self.prefix_for_step(step), initial=self.initial.get(step, None))
|
||||||
|
|
||||||
def num_steps(self):
|
def num_steps(self):
|
||||||
"Helper method that returns the number of steps."
|
"Helper method that returns the number of steps."
|
||||||
# You might think we should just set "self.form_list = len(form_list)"
|
# You might think we should just set "self.form_list = len(form_list)"
|
||||||
# in __init__(), but this calculation needs to be dynamic, because some
|
# in __init__(), but this calculation needs to be dynamic, because some
|
||||||
# hook methods might alter self.form_list.
|
# hook methods might alter self.form_list.
|
||||||
return len(self.form_list)
|
return len(self.form_list)
|
||||||
|
|
||||||
def __call__(self, request, *args, **kwargs):
|
def __call__(self, request, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Main method that does all the hard work, conforming to the Django view
|
Main method that does all the hard work, conforming to the Django view
|
||||||
interface.
|
interface.
|
||||||
"""
|
"""
|
||||||
if 'extra_context' in kwargs:
|
if 'extra_context' in kwargs:
|
||||||
self.extra_context.update(kwargs['extra_context'])
|
self.extra_context.update(kwargs['extra_context'])
|
||||||
current_step = self.determine_step(request, *args, **kwargs)
|
current_step = self.determine_step(request, *args, **kwargs)
|
||||||
self.parse_params(request, *args, **kwargs)
|
self.parse_params(request, *args, **kwargs)
|
||||||
|
|
||||||
# Sanity check.
|
# Sanity check.
|
||||||
if current_step >= self.num_steps():
|
if current_step >= self.num_steps():
|
||||||
raise Http404('Step %s does not exist' % current_step)
|
raise Http404('Step %s does not exist' % current_step)
|
||||||
|
|
||||||
# For each previous step, verify the hash and process.
|
# For each previous step, verify the hash and process.
|
||||||
# TODO: Move "hash_%d" to a method to make it configurable.
|
# TODO: Move "hash_%d" to a method to make it configurable.
|
||||||
for i in range(current_step):
|
for i in range(current_step):
|
||||||
form = self.get_form(i, request.POST)
|
form = self.get_form(i, request.POST)
|
||||||
if request.POST.get("hash_%d" % i, '') != self.security_hash(request, form):
|
if request.POST.get("hash_%d" % i, '') != self.security_hash(request, form):
|
||||||
return self.render_hash_failure(request, i)
|
return self.render_hash_failure(request, i)
|
||||||
self.process_step(request, form, i)
|
self.process_step(request, form, i)
|
||||||
|
|
||||||
# Process the current step. If it's valid, go to the next step or call
|
# Process the current step. If it's valid, go to the next step or call
|
||||||
# done(), depending on whether any steps remain.
|
# done(), depending on whether any steps remain.
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
form = self.get_form(current_step, request.POST)
|
form = self.get_form(current_step, request.POST)
|
||||||
else:
|
else:
|
||||||
form = self.get_form(current_step)
|
form = self.get_form(current_step)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
self.process_step(request, form, current_step)
|
self.process_step(request, form, current_step)
|
||||||
next_step = current_step + 1
|
next_step = current_step + 1
|
||||||
|
|
||||||
# If this was the last step, validate all of the forms one more
|
# If this was the last step, validate all of the forms one more
|
||||||
# time, as a sanity check, and call done().
|
# time, as a sanity check, and call done().
|
||||||
num = self.num_steps()
|
num = self.num_steps()
|
||||||
if next_step == num:
|
if next_step == num:
|
||||||
final_form_list = [self.get_form(i, request.POST) for i in range(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
|
# Validate all the forms. If any of them fail validation, that
|
||||||
# must mean the validator relied on some other input, such as
|
# must mean the validator relied on some other input, such as
|
||||||
# an external Web site.
|
# an external Web site.
|
||||||
for i, f in enumerate(final_form_list):
|
for i, f in enumerate(final_form_list):
|
||||||
if not f.is_valid():
|
if not f.is_valid():
|
||||||
return self.render_revalidation_failure(request, i, f)
|
return self.render_revalidation_failure(request, i, f)
|
||||||
return self.done(request, final_form_list)
|
return self.done(request, final_form_list)
|
||||||
|
|
||||||
# Otherwise, move along to the next step.
|
# Otherwise, move along to the next step.
|
||||||
else:
|
else:
|
||||||
form = self.get_form(next_step)
|
form = self.get_form(next_step)
|
||||||
current_step = next_step
|
current_step = next_step
|
||||||
|
|
||||||
return self.render(form, request, current_step)
|
return self.render(form, request, current_step)
|
||||||
|
|
||||||
def render(self, form, request, step, context=None):
|
def render(self, form, request, step, context=None):
|
||||||
"Renders the given Form object, returning an HttpResponse."
|
"Renders the given Form object, returning an HttpResponse."
|
||||||
old_data = request.POST
|
old_data = request.POST
|
||||||
prev_fields = []
|
prev_fields = []
|
||||||
if old_data:
|
if old_data:
|
||||||
hidden = forms.HiddenInput()
|
hidden = forms.HiddenInput()
|
||||||
# Collect all data from previous steps and render it as HTML hidden fields.
|
# Collect all data from previous steps and render it as HTML hidden fields.
|
||||||
for i in range(step):
|
for i in range(step):
|
||||||
old_form = self.get_form(i, old_data)
|
old_form = self.get_form(i, old_data)
|
||||||
hash_name = 'hash_%s' % i
|
hash_name = 'hash_%s' % i
|
||||||
prev_fields.extend([bf.as_hidden() for bf in old_form])
|
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))))
|
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)
|
return self.render_template(request, form, ''.join(prev_fields), step, context)
|
||||||
|
|
||||||
# METHODS SUBCLASSES MIGHT OVERRIDE IF APPROPRIATE ########################
|
# METHODS SUBCLASSES MIGHT OVERRIDE IF APPROPRIATE ########################
|
||||||
|
|
||||||
def prefix_for_step(self, step):
|
def prefix_for_step(self, step):
|
||||||
"Given the step, returns a Form prefix to use."
|
"Given the step, returns a Form prefix to use."
|
||||||
return str(step)
|
return str(step)
|
||||||
|
|
||||||
def render_hash_failure(self, request, step):
|
def render_hash_failure(self, request, step):
|
||||||
"""
|
"""
|
||||||
Hook for rendering a template if a hash check failed.
|
Hook for rendering a template if a hash check failed.
|
||||||
|
|
||||||
step is the step that failed. Any previous step is guaranteed to be
|
step is the step that failed. Any previous step is guaranteed to be
|
||||||
valid.
|
valid.
|
||||||
|
|
||||||
This default implementation simply renders the form for the given step,
|
This default implementation simply renders the form for the given step,
|
||||||
but subclasses may want to display an error message, etc.
|
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.'})
|
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):
|
def render_revalidation_failure(self, request, step, form):
|
||||||
"""
|
"""
|
||||||
Hook for rendering a template if final revalidation failed.
|
Hook for rendering a template if final revalidation failed.
|
||||||
|
|
||||||
It is highly unlikely that this point would ever be reached, but See
|
It is highly unlikely that this point would ever be reached, but See
|
||||||
the comment in __call__() for an explanation.
|
the comment in __call__() for an explanation.
|
||||||
"""
|
"""
|
||||||
return self.render(form, request, step)
|
return self.render(form, request, step)
|
||||||
|
|
||||||
def security_hash(self, request, form):
|
def security_hash(self, request, form):
|
||||||
"""
|
"""
|
||||||
Calculates the security hash for the given HttpRequest and Form instances.
|
Calculates the security hash for the given HttpRequest and Form instances.
|
||||||
|
|
||||||
This creates a list of the form field names/values in a deterministic
|
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
|
order, pickles the result with the SECRET_KEY setting and takes an md5
|
||||||
hash of that.
|
hash of that.
|
||||||
|
|
||||||
Subclasses may want to take into account request-specific information,
|
Subclasses may want to take into account request-specific information,
|
||||||
such as the IP address.
|
such as the IP address.
|
||||||
"""
|
"""
|
||||||
data = [(bf.name, bf.data or '') for bf in form] + [settings.SECRET_KEY]
|
data = [(bf.name, bf.data or '') for bf in form] + [settings.SECRET_KEY]
|
||||||
# Use HIGHEST_PROTOCOL because it's the most efficient. It requires
|
# Use HIGHEST_PROTOCOL because it's the most efficient. It requires
|
||||||
# Python 2.3, but Django requires 2.3 anyway, so that's OK.
|
# Python 2.3, but Django requires 2.3 anyway, so that's OK.
|
||||||
pickled = pickle.dumps(data, protocol=pickle.HIGHEST_PROTOCOL)
|
pickled = pickle.dumps(data, protocol=pickle.HIGHEST_PROTOCOL)
|
||||||
return md5.new(pickled).hexdigest()
|
return md5.new(pickled).hexdigest()
|
||||||
|
|
||||||
def determine_step(self, request, *args, **kwargs):
|
def determine_step(self, request, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Given the request object and whatever *args and **kwargs were passed to
|
Given the request object and whatever *args and **kwargs were passed to
|
||||||
__call__(), returns the current step (which is zero-based).
|
__call__(), returns the current step (which is zero-based).
|
||||||
|
|
||||||
Note that the result should not be trusted. It may even be a completely
|
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.
|
invalid number. It's not the job of this method to validate it.
|
||||||
"""
|
"""
|
||||||
if not request.POST:
|
if not request.POST:
|
||||||
return 0
|
return 0
|
||||||
try:
|
try:
|
||||||
step = int(request.POST.get(self.step_field_name, 0))
|
step = int(request.POST.get(self.step_field_name, 0))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return 0
|
return 0
|
||||||
return step
|
return step
|
||||||
|
|
||||||
def parse_params(self, request, *args, **kwargs):
|
def parse_params(self, request, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Hook for setting some state, given the request object and whatever
|
Hook for setting some state, given the request object and whatever
|
||||||
*args and **kwargs were passed to __call__(), sets some state.
|
*args and **kwargs were passed to __call__(), sets some state.
|
||||||
|
|
||||||
This is called at the beginning of __call__().
|
This is called at the beginning of __call__().
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def get_template(self, step):
|
def get_template(self, step):
|
||||||
"""
|
"""
|
||||||
Hook for specifying the name of the template to use for a given 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
|
Note that this can return a tuple of template names if you'd like to
|
||||||
use the template system's select_template() hook.
|
use the template system's select_template() hook.
|
||||||
"""
|
"""
|
||||||
return 'forms/wizard.html'
|
return 'forms/wizard.html'
|
||||||
|
|
||||||
def render_template(self, request, form, previous_fields, step, context=None):
|
def render_template(self, request, form, previous_fields, step, context=None):
|
||||||
"""
|
"""
|
||||||
Renders the template for the given step, returning an HttpResponse object.
|
Renders the template for the given step, returning an HttpResponse object.
|
||||||
|
|
||||||
Override this method if you want to add a custom context, return a
|
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
|
different MIME type, etc. If you only need to override the template
|
||||||
name, use get_template() instead.
|
name, use get_template() instead.
|
||||||
|
|
||||||
The template will be rendered with the following context:
|
The template will be rendered with the following context:
|
||||||
step_field -- The name of the hidden field containing the step.
|
step_field -- The name of the hidden field containing the step.
|
||||||
step0 -- The current step (zero-based).
|
step0 -- The current step (zero-based).
|
||||||
step -- The current step (one-based).
|
step -- The current step (one-based).
|
||||||
step_count -- The total number of steps.
|
step_count -- The total number of steps.
|
||||||
form -- The Form instance for the current step (either empty
|
form -- The Form instance for the current step (either empty
|
||||||
or with errors).
|
or with errors).
|
||||||
previous_fields -- A string representing every previous data field,
|
previous_fields -- A string representing every previous data field,
|
||||||
plus hashes for completed forms, all in the form of
|
plus hashes for completed forms, all in the form of
|
||||||
hidden fields. Note that you'll need to run this
|
hidden fields. Note that you'll need to run this
|
||||||
through the "safe" template filter, to prevent
|
through the "safe" template filter, to prevent
|
||||||
auto-escaping, because it's raw HTML.
|
auto-escaping, because it's raw HTML.
|
||||||
"""
|
"""
|
||||||
context = context or {}
|
context = context or {}
|
||||||
context.update(self.extra_context)
|
context.update(self.extra_context)
|
||||||
return render_to_response(self.get_template(self.step), dict(context,
|
return render_to_response(self.get_template(self.step), dict(context,
|
||||||
step_field=self.step_field_name,
|
step_field=self.step_field_name,
|
||||||
step0=step,
|
step0=step,
|
||||||
step=step + 1,
|
step=step + 1,
|
||||||
step_count=self.num_steps(),
|
step_count=self.num_steps(),
|
||||||
form=form,
|
form=form,
|
||||||
previous_fields=previous_fields
|
previous_fields=previous_fields
|
||||||
), context_instance=RequestContext(request))
|
), context_instance=RequestContext(request))
|
||||||
|
|
||||||
def process_step(self, request, form, step):
|
def process_step(self, request, form, step):
|
||||||
"""
|
"""
|
||||||
Hook for modifying the FormWizard's internal state, given a fully
|
Hook for modifying the FormWizard's internal state, given a fully
|
||||||
validated Form object. The Form is guaranteed to have clean, valid
|
validated Form object. The Form is guaranteed to have clean, valid
|
||||||
data.
|
data.
|
||||||
|
|
||||||
This method should *not* modify any of that data. Rather, it might want
|
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
|
to set self.extra_context or dynamically alter self.form_list, based on
|
||||||
previously submitted forms.
|
previously submitted forms.
|
||||||
|
|
||||||
Note that this method is called every time a page is rendered for *all*
|
Note that this method is called every time a page is rendered for *all*
|
||||||
submitted steps.
|
submitted steps.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# METHODS SUBCLASSES MUST OVERRIDE ########################################
|
# METHODS SUBCLASSES MUST OVERRIDE ########################################
|
||||||
|
|
||||||
def done(self, request, form_list):
|
def done(self, request, form_list):
|
||||||
"""
|
"""
|
||||||
Hook for doing something with the validated data. This is responsible
|
Hook for doing something with the validated data. This is responsible
|
||||||
for the final processing.
|
for the final processing.
|
||||||
|
|
||||||
form_list is a list of Form instances, each containing clean, valid
|
form_list is a list of Form instances, each containing clean, valid
|
||||||
data.
|
data.
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError("Your %s class has not defined a done() method, which is required." % self.__class__.__name__)
|
raise NotImplementedError("Your %s class has not defined a done() method, which is required." % self.__class__.__name__)
|
||||||
|
@ -9,7 +9,9 @@ class SessionStore(SessionBase):
|
|||||||
Implements a file based session store.
|
Implements a file based session store.
|
||||||
"""
|
"""
|
||||||
def __init__(self, session_key=None):
|
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.
|
# Make sure the storage path is valid.
|
||||||
if not os.path.isdir(self.storage_path):
|
if not os.path.isdir(self.storage_path):
|
||||||
|
@ -3,7 +3,8 @@ from django.template import Context, loader, Template, TemplateDoesNotExist
|
|||||||
from django.contrib.sites.models import Site, RequestSite
|
from django.contrib.sites.models import Site, RequestSite
|
||||||
from django.utils import feedgenerator
|
from django.utils import feedgenerator
|
||||||
from django.utils.encoding import smart_unicode, iri_to_uri
|
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):
|
def add_domain(domain, url):
|
||||||
if not (url.startswith('http://') or url.startswith('https://')):
|
if not (url.startswith('http://') or url.startswith('https://')):
|
||||||
@ -55,18 +56,23 @@ class Feed(object):
|
|||||||
return attr()
|
return attr()
|
||||||
return attr
|
return attr
|
||||||
|
|
||||||
|
def get_object(self, bits):
|
||||||
|
return None
|
||||||
|
|
||||||
def get_feed(self, url=None):
|
def get_feed(self, url=None):
|
||||||
"""
|
"""
|
||||||
Returns a feedgenerator.DefaultFeed object, fully populated, for
|
Returns a feedgenerator.DefaultFeed object, fully populated, for
|
||||||
this feed. Raises FeedDoesNotExist for invalid parameters.
|
this feed. Raises FeedDoesNotExist for invalid parameters.
|
||||||
"""
|
"""
|
||||||
if url:
|
if url:
|
||||||
try:
|
bits = url.split('/')
|
||||||
obj = self.get_object(url.split('/'))
|
|
||||||
except (AttributeError, ObjectDoesNotExist):
|
|
||||||
raise FeedDoesNotExist
|
|
||||||
else:
|
else:
|
||||||
obj = None
|
bits = []
|
||||||
|
|
||||||
|
try:
|
||||||
|
obj = self.get_object(bits)
|
||||||
|
except ObjectDoesNotExist:
|
||||||
|
raise FeedDoesNotExist
|
||||||
|
|
||||||
if Site._meta.installed:
|
if Site._meta.installed:
|
||||||
current_site = Site.objects.get_current()
|
current_site = Site.objects.get_current()
|
||||||
@ -119,9 +125,9 @@ class Feed(object):
|
|||||||
else:
|
else:
|
||||||
author_email = author_link = None
|
author_email = author_link = None
|
||||||
feed.add_item(
|
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,
|
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),
|
unique_id = self.__get_dynamic_attr('item_guid', item, link),
|
||||||
enclosure = enc,
|
enclosure = enc,
|
||||||
pubdate = self.__get_dynamic_attr('item_pubdate', item),
|
pubdate = self.__get_dynamic_attr('item_pubdate', item),
|
||||||
|
@ -2,20 +2,21 @@
|
|||||||
Tools for sending email.
|
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 mimetypes
|
||||||
import os
|
import os
|
||||||
import smtplib
|
import smtplib
|
||||||
import socket
|
import socket
|
||||||
import time
|
import time
|
||||||
import random
|
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
|
# Don't BASE64-encode UTF-8 messages so that we avoid unwanted attention from
|
||||||
# some spam filters.
|
# some spam filters.
|
||||||
@ -38,8 +39,9 @@ class CachedDnsName(object):
|
|||||||
|
|
||||||
DNS_NAME = CachedDnsName()
|
DNS_NAME = CachedDnsName()
|
||||||
|
|
||||||
# Copied from Python standard library and modified to used the cached hostname
|
# Copied from Python standard library, with the following modifications:
|
||||||
# for performance.
|
# * Used cached hostname for performance.
|
||||||
|
# * Added try/except to support lack of getpid() in Jython (#5496).
|
||||||
def make_msgid(idstring=None):
|
def make_msgid(idstring=None):
|
||||||
"""Returns a string suitable for RFC 2822 compliant Message-ID, e.g:
|
"""Returns a string suitable for RFC 2822 compliant Message-ID, e.g:
|
||||||
|
|
||||||
@ -53,7 +55,7 @@ def make_msgid(idstring=None):
|
|||||||
try:
|
try:
|
||||||
pid = os.getpid()
|
pid = os.getpid()
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
# Not getpid() in Jython, for example.
|
# No getpid() in Jython, for example.
|
||||||
pid = 1
|
pid = 1
|
||||||
randint = random.randrange(100000)
|
randint = random.randrange(100000)
|
||||||
if idstring is None:
|
if idstring is None:
|
||||||
@ -68,7 +70,7 @@ class BadHeaderError(ValueError):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def forbid_multi_line_headers(name, val):
|
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:
|
if '\n' in val or '\r' in val:
|
||||||
raise BadHeaderError("Header values can't contain newlines (got %r for header %r)" % (val, name))
|
raise BadHeaderError("Header values can't contain newlines (got %r for header %r)" % (val, name))
|
||||||
try:
|
try:
|
||||||
@ -101,7 +103,7 @@ class SMTPConnection(object):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, host=None, port=None, username=None, password=None,
|
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.host = host or settings.EMAIL_HOST
|
||||||
self.port = port or settings.EMAIL_PORT
|
self.port = port or settings.EMAIL_PORT
|
||||||
self.username = username or settings.EMAIL_HOST_USER
|
self.username = username or settings.EMAIL_HOST_USER
|
||||||
@ -112,14 +114,17 @@ class SMTPConnection(object):
|
|||||||
|
|
||||||
def open(self):
|
def open(self):
|
||||||
"""
|
"""
|
||||||
Ensure we have a connection to the email server. Returns whether or not
|
Ensures we have a connection to the email server. Returns whether or
|
||||||
a new connection was required.
|
not a new connection was required (True or False).
|
||||||
"""
|
"""
|
||||||
if self.connection:
|
if self.connection:
|
||||||
# Nothing to do if the connection is already open.
|
# Nothing to do if the connection is already open.
|
||||||
return False
|
return False
|
||||||
try:
|
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:
|
if self.use_tls:
|
||||||
self.connection.ehlo()
|
self.connection.ehlo()
|
||||||
self.connection.starttls()
|
self.connection.starttls()
|
||||||
@ -132,7 +137,7 @@ class SMTPConnection(object):
|
|||||||
raise
|
raise
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
"""Close the connection to the email server."""
|
"""Closes the connection to the email server."""
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
self.connection.quit()
|
self.connection.quit()
|
||||||
@ -149,7 +154,7 @@ class SMTPConnection(object):
|
|||||||
|
|
||||||
def send_messages(self, email_messages):
|
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.
|
messages sent.
|
||||||
"""
|
"""
|
||||||
if not email_messages:
|
if not email_messages:
|
||||||
@ -192,7 +197,7 @@ class EmailMessage(object):
|
|||||||
def __init__(self, subject='', body='', from_email=None, to=None, bcc=None,
|
def __init__(self, subject='', body='', from_email=None, to=None, bcc=None,
|
||||||
connection=None, attachments=None, headers=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).
|
recipients).
|
||||||
|
|
||||||
All strings used to create the message can be unicode strings (or UTF-8
|
All strings used to create the message can be unicode strings (or UTF-8
|
||||||
@ -221,7 +226,8 @@ class EmailMessage(object):
|
|||||||
|
|
||||||
def message(self):
|
def message(self):
|
||||||
encoding = self.encoding or settings.DEFAULT_CHARSET
|
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:
|
if self.attachments:
|
||||||
body_msg = msg
|
body_msg = msg
|
||||||
msg = SafeMIMEMultipart(_subtype=self.multipart_subtype)
|
msg = SafeMIMEMultipart(_subtype=self.multipart_subtype)
|
||||||
@ -237,8 +243,6 @@ class EmailMessage(object):
|
|||||||
msg['To'] = ', '.join(self.to)
|
msg['To'] = ', '.join(self.to)
|
||||||
msg['Date'] = formatdate()
|
msg['Date'] = formatdate()
|
||||||
msg['Message-ID'] = make_msgid()
|
msg['Message-ID'] = make_msgid()
|
||||||
if self.bcc:
|
|
||||||
msg['Bcc'] = ', '.join(self.bcc)
|
|
||||||
for name, value in self.extra_headers.items():
|
for name, value in self.extra_headers.items():
|
||||||
msg[name] = value
|
msg[name] = value
|
||||||
return msg
|
return msg
|
||||||
@ -251,7 +255,7 @@ class EmailMessage(object):
|
|||||||
return self.to + self.bcc
|
return self.to + self.bcc
|
||||||
|
|
||||||
def send(self, fail_silently=False):
|
def send(self, fail_silently=False):
|
||||||
"""Send the email message."""
|
"""Sends the email message."""
|
||||||
return self.get_connection(fail_silently).send_messages([self])
|
return self.get_connection(fail_silently).send_messages([self])
|
||||||
|
|
||||||
def attach(self, filename=None, content=None, mimetype=None):
|
def attach(self, filename=None, content=None, mimetype=None):
|
||||||
@ -278,7 +282,7 @@ class EmailMessage(object):
|
|||||||
|
|
||||||
def _create_attachment(self, filename, content, mimetype=None):
|
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.
|
object.
|
||||||
"""
|
"""
|
||||||
if mimetype is None:
|
if mimetype is None:
|
||||||
@ -295,7 +299,8 @@ class EmailMessage(object):
|
|||||||
attachment.set_payload(content)
|
attachment.set_payload(content)
|
||||||
Encoders.encode_base64(attachment)
|
Encoders.encode_base64(attachment)
|
||||||
if filename:
|
if filename:
|
||||||
attachment.add_header('Content-Disposition', 'attachment', filename=filename)
|
attachment.add_header('Content-Disposition', 'attachment',
|
||||||
|
filename=filename)
|
||||||
return attachment
|
return attachment
|
||||||
|
|
||||||
class EmailMultiAlternatives(EmailMessage):
|
class EmailMultiAlternatives(EmailMessage):
|
||||||
@ -310,7 +315,8 @@ class EmailMultiAlternatives(EmailMessage):
|
|||||||
"""Attach an alternative content representation."""
|
"""Attach an alternative content representation."""
|
||||||
self.attach(content=content, mimetype=mimetype)
|
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
|
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.
|
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.
|
functionality should use the EmailMessage class directly.
|
||||||
"""
|
"""
|
||||||
connection = SMTPConnection(username=auth_user, password=auth_password,
|
connection = SMTPConnection(username=auth_user, password=auth_password,
|
||||||
fail_silently=fail_silently)
|
fail_silently=fail_silently)
|
||||||
return EmailMessage(subject, message, from_email, recipient_list, connection=connection).send()
|
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
|
Given a datatuple of (subject, message, from_email, recipient_list), sends
|
||||||
each message to each recipient list. Returns the number of e-mails sent.
|
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.
|
functionality should use the EmailMessage class directly.
|
||||||
"""
|
"""
|
||||||
connection = SMTPConnection(username=auth_user, password=auth_password,
|
connection = SMTPConnection(username=auth_user, password=auth_password,
|
||||||
fail_silently=fail_silently)
|
fail_silently=fail_silently)
|
||||||
messages = [EmailMessage(subject, message, sender, recipient) for subject, message, sender, recipient in datatuple]
|
messages = [EmailMessage(subject, message, sender, recipient)
|
||||||
|
for subject, message, sender, recipient in datatuple]
|
||||||
return connection.send_messages(messages)
|
return connection.send_messages(messages)
|
||||||
|
|
||||||
def mail_admins(subject, message, fail_silently=False):
|
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,
|
EmailMessage(settings.EMAIL_SUBJECT_PREFIX + subject, message,
|
||||||
settings.SERVER_EMAIL, [a[1] for a in
|
settings.SERVER_EMAIL, [a[1] for a in settings.ADMINS]
|
||||||
settings.ADMINS]).send(fail_silently=fail_silently)
|
).send(fail_silently=fail_silently)
|
||||||
|
|
||||||
def mail_managers(subject, message, fail_silently=False):
|
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,
|
EmailMessage(settings.EMAIL_SUBJECT_PREFIX + subject, message,
|
||||||
settings.SERVER_EMAIL, [a[1] for a in
|
settings.SERVER_EMAIL, [a[1] for a in settings.MANAGERS]
|
||||||
settings.MANAGERS]).send(fail_silently=fail_silently)
|
).send(fail_silently=fail_silently)
|
||||||
|
|
||||||
|
@ -243,7 +243,7 @@ def setup_environ(settings_mod):
|
|||||||
# way. For example, if this file (manage.py) lives in a directory
|
# way. For example, if this file (manage.py) lives in a directory
|
||||||
# "myproject", this code would add "/path/to/myproject" to sys.path.
|
# "myproject", this code would add "/path/to/myproject" to sys.path.
|
||||||
project_directory, settings_filename = os.path.split(settings_mod.__file__)
|
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_directory = os.getcwd()
|
||||||
project_name = os.path.basename(project_directory)
|
project_name = os.path.basename(project_directory)
|
||||||
settings_name = os.path.splitext(settings_filename)[0]
|
settings_name = os.path.splitext(settings_filename)[0]
|
||||||
|
@ -20,8 +20,13 @@ class Command(LabelCommand):
|
|||||||
# the parent directory.
|
# the parent directory.
|
||||||
directory = os.getcwd()
|
directory = os.getcwd()
|
||||||
|
|
||||||
if project_name in INVALID_PROJECT_NAMES:
|
try:
|
||||||
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)
|
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)
|
copy_helper(self.style, 'project', project_name, directory)
|
||||||
|
|
||||||
|
@ -1,46 +1,149 @@
|
|||||||
class InvalidPage(Exception):
|
class InvalidPage(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class ObjectPaginator(object):
|
class Paginator(object):
|
||||||
"""
|
def __init__(self, object_list, per_page, orphans=0, allow_empty_first_page=True):
|
||||||
This class makes pagination easy. Feed it a QuerySet or list, plus the number
|
self.object_list = object_list
|
||||||
of objects you want on each page. Then read the hits and pages properties to
|
self.per_page = per_page
|
||||||
see how many pages it involves. Call get_page with a page number (starting
|
self.orphans = orphans
|
||||||
at 0) to get back a list of objects for that page.
|
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
|
def validate_number(self, number):
|
||||||
has_next_page(page_number) and has_previous_page(page_number).
|
"Validates the given 1-based page number."
|
||||||
|
try:
|
||||||
Use orphans to avoid small final pages. For example:
|
number = int(number)
|
||||||
13 records, num_per_page=10, orphans=2 --> pages==2, len(self.get_page(0))==10
|
except ValueError:
|
||||||
12 records, num_per_page=10, orphans=2 --> pages==1, len(self.get_page(0))==12
|
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):
|
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.query_set = query_set
|
||||||
self.num_per_page = num_per_page
|
self.num_per_page = num_per_page
|
||||||
self.orphans = orphans
|
|
||||||
self._hits = self._pages = None
|
self._hits = self._pages = None
|
||||||
self._page_range = None
|
|
||||||
|
|
||||||
def validate_page_number(self, page_number):
|
def validate_page_number(self, page_number):
|
||||||
try:
|
try:
|
||||||
page_number = int(page_number)
|
page_number = int(page_number) + 1
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise InvalidPage
|
raise InvalidPage
|
||||||
if page_number < 0 or page_number > self.pages - 1:
|
return self.validate_number(page_number)
|
||||||
raise InvalidPage
|
|
||||||
return page_number
|
|
||||||
|
|
||||||
def get_page(self, page_number):
|
def get_page(self, page_number):
|
||||||
page_number = self.validate_page_number(page_number)
|
try:
|
||||||
bottom = page_number * self.num_per_page
|
page_number = int(page_number) + 1
|
||||||
top = bottom + self.num_per_page
|
except ValueError:
|
||||||
if top + self.orphans >= self.hits:
|
raise InvalidPage
|
||||||
top = self.hits
|
return self.page(page_number).object_list
|
||||||
return self.query_set[bottom:top]
|
|
||||||
|
|
||||||
def has_next_page(self, page_number):
|
def has_next_page(self, page_number):
|
||||||
"Does page $page_number have a 'next' page?"
|
|
||||||
return page_number < self.pages - 1
|
return page_number < self.pages - 1
|
||||||
|
|
||||||
def has_previous_page(self, page_number):
|
def has_previous_page(self, page_number):
|
||||||
@ -52,7 +155,7 @@ class ObjectPaginator(object):
|
|||||||
relative to total objects found (hits).
|
relative to total objects found (hits).
|
||||||
"""
|
"""
|
||||||
page_number = self.validate_page_number(page_number)
|
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):
|
def last_on_page(self, page_number):
|
||||||
"""
|
"""
|
||||||
@ -60,40 +163,23 @@ class ObjectPaginator(object):
|
|||||||
relative to total objects found (hits).
|
relative to total objects found (hits).
|
||||||
"""
|
"""
|
||||||
page_number = self.validate_page_number(page_number)
|
page_number = self.validate_page_number(page_number)
|
||||||
page_number += 1 # 1-base
|
if page_number == self.num_pages:
|
||||||
if page_number == self.pages:
|
return self.count
|
||||||
return self.hits
|
|
||||||
return page_number * self.num_per_page
|
return page_number * self.num_per_page
|
||||||
|
|
||||||
def _get_hits(self):
|
def _get_count(self):
|
||||||
if self._hits is None:
|
# The old API allowed for self.object_list to be either a QuerySet or a
|
||||||
# Try .count() or fall back to len().
|
# list. Here, we handle both.
|
||||||
|
if self._count is None:
|
||||||
try:
|
try:
|
||||||
self._hits = int(self.query_set.count())
|
self._count = self.object_list.count()
|
||||||
except (AttributeError, TypeError, ValueError):
|
except TypeError:
|
||||||
# AttributeError if query_set has no object count.
|
self._count = len(self.object_list)
|
||||||
# TypeError if query_set.count() required arguments.
|
return self._count
|
||||||
# ValueError if int() fails.
|
count = property(_get_count)
|
||||||
self._hits = len(self.query_set)
|
|
||||||
return self._hits
|
|
||||||
|
|
||||||
def _get_pages(self):
|
# The old API called it "hits" instead of "count".
|
||||||
if self._pages is None:
|
hits = count
|
||||||
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
|
|
||||||
|
|
||||||
hits = property(_get_hits)
|
# The old API called it "pages" instead of "num_pages".
|
||||||
pages = property(_get_pages)
|
pages = Paginator.num_pages
|
||||||
page_range = property(_get_page_range)
|
|
||||||
|
@ -22,10 +22,10 @@ class Serializer(object):
|
|||||||
Abstract serializer base class.
|
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 Django use.
|
||||||
internal_use_only = False
|
internal_use_only = False
|
||||||
|
|
||||||
def serialize(self, queryset, **options):
|
def serialize(self, queryset, **options):
|
||||||
"""
|
"""
|
||||||
Serialize a queryset.
|
Serialize a queryset.
|
||||||
@ -60,8 +60,6 @@ class Serializer(object):
|
|||||||
"""
|
"""
|
||||||
if isinstance(field, models.DateTimeField):
|
if isinstance(field, models.DateTimeField):
|
||||||
value = getattr(obj, field.name).strftime("%Y-%m-%d %H:%M:%S")
|
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:
|
else:
|
||||||
value = field.flatten_data(follow=None, obj=obj).get(field.name, "")
|
value = field.flatten_data(follow=None, obj=obj).get(field.name, "")
|
||||||
return smart_unicode(value)
|
return smart_unicode(value)
|
||||||
@ -162,9 +160,9 @@ class DeserializedObject(object):
|
|||||||
return "<DeserializedObject: %s>" % smart_str(self.object)
|
return "<DeserializedObject: %s>" % smart_str(self.object)
|
||||||
|
|
||||||
def save(self, save_m2m=True):
|
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.
|
# 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
|
# what came from the file, not post-processed by pre_save/save
|
||||||
# methods.
|
# methods.
|
||||||
models.Model.save(self.object, raw=True)
|
models.Model.save(self.object, raw=True)
|
||||||
|
@ -37,7 +37,9 @@ Optional Fcgi settings: (setting=value)
|
|||||||
maxchildren=NUMBER hard limit number of processes / threads
|
maxchildren=NUMBER hard limit number of processes / threads
|
||||||
daemonize=BOOL whether to detach from terminal.
|
daemonize=BOOL whether to detach from terminal.
|
||||||
pidfile=FILE write the spawned process-id to this file.
|
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:
|
Examples:
|
||||||
Run a "standard" fastcgi process on a file-descriptor
|
Run a "standard" fastcgi process on a file-descriptor
|
||||||
@ -69,6 +71,8 @@ FASTCGI_OPTIONS = {
|
|||||||
'minspare': 2,
|
'minspare': 2,
|
||||||
'maxchildren': 50,
|
'maxchildren': 50,
|
||||||
'maxrequests': 0,
|
'maxrequests': 0,
|
||||||
|
'outlog': None,
|
||||||
|
'errlog': None,
|
||||||
}
|
}
|
||||||
|
|
||||||
def fastcgi_help(message=None):
|
def fastcgi_help(message=None):
|
||||||
@ -150,9 +154,15 @@ def runfastcgi(argset=[], **kwargs):
|
|||||||
else:
|
else:
|
||||||
return fastcgi_help("ERROR: Invalid option for daemonize parameter.")
|
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:
|
if daemonize:
|
||||||
from django.utils.daemonize import become_daemon
|
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"]:
|
if options["pidfile"]:
|
||||||
fp = open(options["pidfile"], "w")
|
fp = open(options["pidfile"], "w")
|
||||||
|
@ -846,6 +846,16 @@ class FilePathField(Field):
|
|||||||
self.path, self.match, self.recursive = path, match, recursive
|
self.path, self.match, self.recursive = path, match, recursive
|
||||||
kwargs['max_length'] = kwargs.get('max_length', 100)
|
kwargs['max_length'] = kwargs.get('max_length', 100)
|
||||||
Field.__init__(self, verbose_name, name, **kwargs)
|
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):
|
def get_manipulator_field_objs(self):
|
||||||
return [curry(oldforms.FilePathField, path=self.path, match=self.match, recursive=self.recursive)]
|
return [curry(oldforms.FilePathField, path=self.path, match=self.match, recursive=self.recursive)]
|
||||||
|
@ -548,6 +548,13 @@ class ForeignKey(RelatedField, Field):
|
|||||||
params['choices'] = self.get_choices_default()
|
params['choices'] = self.get_choices_default()
|
||||||
return field_objs, params
|
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):
|
def get_manipulator_field_objs(self):
|
||||||
rel_field = self.rel.get_related_field()
|
rel_field = self.rel.get_related_field()
|
||||||
if self.rel.raw_id_admin and not isinstance(rel_field, AutoField):
|
if self.rel.raw_id_admin and not isinstance(rel_field, AutoField):
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
from django.conf import settings
|
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.fields import DateField, FieldDoesNotExist
|
||||||
from django.db.models import signals, loading
|
from django.db.models import signals, loading
|
||||||
from django.dispatch import dispatcher
|
from django.dispatch import dispatcher
|
||||||
@ -285,11 +285,14 @@ class _QuerySet(object):
|
|||||||
try:
|
try:
|
||||||
return self.get(**kwargs), False
|
return self.get(**kwargs), False
|
||||||
except self.model.DoesNotExist:
|
except self.model.DoesNotExist:
|
||||||
params = dict([(k, v) for k, v in kwargs.items() if '__' not in k])
|
try:
|
||||||
params.update(defaults)
|
params = dict([(k, v) for k, v in kwargs.items() if '__' not in k])
|
||||||
obj = self.model(**params)
|
params.update(defaults)
|
||||||
obj.save()
|
obj = self.model(**params)
|
||||||
return obj, True
|
obj.save()
|
||||||
|
return obj, True
|
||||||
|
except IntegrityError, e:
|
||||||
|
return self.get(**kwargs), False
|
||||||
|
|
||||||
def latest(self, field_name=None):
|
def latest(self, field_name=None):
|
||||||
"""
|
"""
|
||||||
|
@ -82,6 +82,9 @@ class HttpRequest(object):
|
|||||||
def is_secure(self):
|
def is_secure(self):
|
||||||
return os.environ.get("HTTPS") == "on"
|
return os.environ.get("HTTPS") == "on"
|
||||||
|
|
||||||
|
def is_ajax(self):
|
||||||
|
return self.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest'
|
||||||
|
|
||||||
def _set_encoding(self, val):
|
def _set_encoding(self, val):
|
||||||
"""
|
"""
|
||||||
Sets the encoding used for GET/POST accesses. If the GET or POST
|
Sets the encoding used for GET/POST accesses. If the GET or POST
|
||||||
|
@ -3,6 +3,7 @@ Extra HTML Widget classes
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
import re
|
||||||
|
|
||||||
from django.newforms.widgets import Widget, Select
|
from django.newforms.widgets import Widget, Select
|
||||||
from django.utils.dates import MONTHS
|
from django.utils.dates import MONTHS
|
||||||
@ -10,6 +11,8 @@ from django.utils.safestring import mark_safe
|
|||||||
|
|
||||||
__all__ = ('SelectDateWidget',)
|
__all__ = ('SelectDateWidget',)
|
||||||
|
|
||||||
|
RE_DATE = re.compile(r'(\d{4})-(\d\d?)-(\d\d?)$')
|
||||||
|
|
||||||
class SelectDateWidget(Widget):
|
class SelectDateWidget(Widget):
|
||||||
"""
|
"""
|
||||||
A Widget that splits date input into three <select> boxes.
|
A Widget that splits date input into three <select> boxes.
|
||||||
@ -32,28 +35,43 @@ class SelectDateWidget(Widget):
|
|||||||
|
|
||||||
def render(self, name, value, attrs=None):
|
def render(self, name, value, attrs=None):
|
||||||
try:
|
try:
|
||||||
value = datetime.date(*map(int, value.split('-')))
|
|
||||||
year_val, month_val, day_val = value.year, value.month, value.day
|
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
|
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 = []
|
output = []
|
||||||
|
|
||||||
|
if 'id' in self.attrs:
|
||||||
|
id_ = self.attrs['id']
|
||||||
|
else:
|
||||||
|
id_ = 'id_%s' % name
|
||||||
|
|
||||||
month_choices = MONTHS.items()
|
month_choices = MONTHS.items()
|
||||||
month_choices.sort()
|
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)
|
output.append(select_html)
|
||||||
|
|
||||||
day_choices = [(i, i) for i in range(1, 32)]
|
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)
|
output.append(select_html)
|
||||||
|
|
||||||
year_choices = [(i, i) for i in self.years]
|
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)
|
output.append(select_html)
|
||||||
|
|
||||||
return mark_safe(u'\n'.join(output))
|
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):
|
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)
|
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:
|
if y and m and d:
|
||||||
|
@ -4,6 +4,7 @@ Field classes.
|
|||||||
|
|
||||||
import copy
|
import copy
|
||||||
import datetime
|
import datetime
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
# Python 2.3 fallbacks
|
# Python 2.3 fallbacks
|
||||||
@ -31,7 +32,7 @@ __all__ = (
|
|||||||
'RegexField', 'EmailField', 'FileField', 'ImageField', 'URLField',
|
'RegexField', 'EmailField', 'FileField', 'ImageField', 'URLField',
|
||||||
'BooleanField', 'NullBooleanField', 'ChoiceField', 'MultipleChoiceField',
|
'BooleanField', 'NullBooleanField', 'ChoiceField', 'MultipleChoiceField',
|
||||||
'ComboField', 'MultiValueField', 'FloatField', 'DecimalField',
|
'ComboField', 'MultiValueField', 'FloatField', 'DecimalField',
|
||||||
'SplitDateTimeField', 'IPAddressField',
|
'SplitDateTimeField', 'IPAddressField', 'FilePathField',
|
||||||
)
|
)
|
||||||
|
|
||||||
# These values, if given to to_python(), will trigger the self.required check.
|
# 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.')
|
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):
|
class SplitDateTimeField(MultiValueField):
|
||||||
default_error_messages = {
|
default_error_messages = {
|
||||||
'invalid_date': _(u'Enter a valid date.'),
|
'invalid_date': _(u'Enter a valid date.'),
|
||||||
|
@ -277,19 +277,18 @@ class ModelForm(BaseModelForm):
|
|||||||
|
|
||||||
# Fields #####################################################################
|
# Fields #####################################################################
|
||||||
|
|
||||||
class QuerySetIterator(object):
|
class ModelChoiceIterator(object):
|
||||||
def __init__(self, queryset, empty_label, cache_choices):
|
def __init__(self, field):
|
||||||
self.queryset = queryset
|
self.field = field
|
||||||
self.empty_label = empty_label
|
self.queryset = field.queryset
|
||||||
self.cache_choices = cache_choices
|
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
if self.empty_label is not None:
|
if self.field.empty_label is not None:
|
||||||
yield (u"", self.empty_label)
|
yield (u"", self.field.empty_label)
|
||||||
for obj in self.queryset:
|
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.
|
# Clear the QuerySet cache if required.
|
||||||
if not self.cache_choices:
|
if not self.field.cache_choices:
|
||||||
self.queryset._result_cache = None
|
self.queryset._result_cache = None
|
||||||
|
|
||||||
class ModelChoiceField(ChoiceField):
|
class ModelChoiceField(ChoiceField):
|
||||||
@ -306,6 +305,7 @@ class ModelChoiceField(ChoiceField):
|
|||||||
help_text=None, *args, **kwargs):
|
help_text=None, *args, **kwargs):
|
||||||
self.empty_label = empty_label
|
self.empty_label = empty_label
|
||||||
self.cache_choices = cache_choices
|
self.cache_choices = cache_choices
|
||||||
|
|
||||||
# Call Field instead of ChoiceField __init__() because we don't need
|
# Call Field instead of ChoiceField __init__() because we don't need
|
||||||
# ChoiceField.__init__().
|
# ChoiceField.__init__().
|
||||||
Field.__init__(self, required, widget, label, initial, help_text,
|
Field.__init__(self, required, widget, label, initial, help_text,
|
||||||
@ -321,19 +321,30 @@ class ModelChoiceField(ChoiceField):
|
|||||||
|
|
||||||
queryset = property(_get_queryset, _set_queryset)
|
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):
|
def _get_choices(self):
|
||||||
# If self._choices is set, then somebody must have manually set
|
# If self._choices is set, then somebody must have manually set
|
||||||
# the property self.choices. In this case, just return self._choices.
|
# the property self.choices. In this case, just return self._choices.
|
||||||
if hasattr(self, '_choices'):
|
if hasattr(self, '_choices'):
|
||||||
return self._choices
|
return self._choices
|
||||||
|
|
||||||
# Otherwise, execute the QuerySet in self.queryset to determine the
|
# Otherwise, execute the QuerySet in self.queryset to determine the
|
||||||
# choices dynamically. Return a fresh QuerySetIterator that has not
|
# choices dynamically. Return a fresh QuerySetIterator that has not been
|
||||||
# been consumed. Note that we're instantiating a new QuerySetIterator
|
# consumed. Note that we're instantiating a new QuerySetIterator *each*
|
||||||
# *each* time _get_choices() is called (and, thus, each time
|
# time _get_choices() is called (and, thus, each time self.choices is
|
||||||
# self.choices is accessed) so that we can ensure the QuerySet has not
|
# accessed) so that we can ensure the QuerySet has not been consumed. This
|
||||||
# been consumed.
|
# construct might look complicated but it allows for lazy evaluation of
|
||||||
return QuerySetIterator(self.queryset, self.empty_label,
|
# the queryset.
|
||||||
self.cache_choices)
|
return ModelChoiceIterator(self)
|
||||||
|
|
||||||
def _set_choices(self, value):
|
def _set_choices(self, value):
|
||||||
# This method is copied from ChoiceField._set_choices(). It's necessary
|
# This method is copied from ChoiceField._set_choices(). It's necessary
|
||||||
|
@ -124,7 +124,10 @@ def floatformat(text, arg=-1):
|
|||||||
d = int(arg)
|
d = int(arg)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return force_unicode(f)
|
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:
|
if not m and d < 0:
|
||||||
return mark_safe(u'%d' % int(f))
|
return mark_safe(u'%d' % int(f))
|
||||||
else:
|
else:
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import urllib
|
||||||
import sys
|
import sys
|
||||||
from cStringIO import StringIO
|
from cStringIO import StringIO
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
@ -208,7 +209,7 @@ class Client:
|
|||||||
r = {
|
r = {
|
||||||
'CONTENT_LENGTH': None,
|
'CONTENT_LENGTH': None,
|
||||||
'CONTENT_TYPE': 'text/html; charset=utf-8',
|
'CONTENT_TYPE': 'text/html; charset=utf-8',
|
||||||
'PATH_INFO': path,
|
'PATH_INFO': urllib.unquote(path),
|
||||||
'QUERY_STRING': urlencode(data, doseq=True),
|
'QUERY_STRING': urlencode(data, doseq=True),
|
||||||
'REQUEST_METHOD': 'GET',
|
'REQUEST_METHOD': 'GET',
|
||||||
}
|
}
|
||||||
@ -227,7 +228,7 @@ class Client:
|
|||||||
r = {
|
r = {
|
||||||
'CONTENT_LENGTH': len(post_data),
|
'CONTENT_LENGTH': len(post_data),
|
||||||
'CONTENT_TYPE': content_type,
|
'CONTENT_TYPE': content_type,
|
||||||
'PATH_INFO': path,
|
'PATH_INFO': urllib.unquote(path),
|
||||||
'REQUEST_METHOD': 'POST',
|
'REQUEST_METHOD': 'POST',
|
||||||
'wsgi.input': StringIO(post_data),
|
'wsgi.input': StringIO(post_data),
|
||||||
}
|
}
|
||||||
|
@ -18,10 +18,10 @@ if os.name == 'posix':
|
|||||||
# Second fork
|
# Second fork
|
||||||
try:
|
try:
|
||||||
if os.fork() > 0:
|
if os.fork() > 0:
|
||||||
sys.exit(0)
|
os._exit(0)
|
||||||
except OSError, e:
|
except OSError, e:
|
||||||
sys.stderr.write("fork #2 failed: (%d) %s\n" % (e.errno, e.strerror))
|
sys.stderr.write("fork #2 failed: (%d) %s\n" % (e.errno, e.strerror))
|
||||||
sys.exit(1)
|
os._exit(1)
|
||||||
|
|
||||||
si = open('/dev/null', 'r')
|
si = open('/dev/null', 'r')
|
||||||
so = open(out_log, 'a+', 0)
|
so = open(out_log, 'a+', 0)
|
||||||
@ -29,6 +29,8 @@ if os.name == 'posix':
|
|||||||
os.dup2(si.fileno(), sys.stdin.fileno())
|
os.dup2(si.fileno(), sys.stdin.fileno())
|
||||||
os.dup2(so.fileno(), sys.stdout.fileno())
|
os.dup2(so.fileno(), sys.stdout.fileno())
|
||||||
os.dup2(se.fileno(), sys.stderr.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:
|
else:
|
||||||
def become_daemon(our_home_dir='.', out_log=None, err_log=None):
|
def become_daemon(our_home_dir='.', out_log=None, err_log=None):
|
||||||
"""
|
"""
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
import datetime
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.template import Template, Context, TemplateDoesNotExist
|
from django.template import Template, Context, TemplateDoesNotExist
|
||||||
from django.utils.html import escape
|
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
|
from django.utils.encoding import smart_unicode
|
||||||
|
|
||||||
HIDDEN_SETTINGS = re.compile('SECRET|PASSWORD|PROFANITIES_LIST')
|
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
|
Create a technical server error response. The last three arguments are
|
||||||
the values returned from sys.exc_info() and friends.
|
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_info = None
|
||||||
template_does_not_exist = False
|
template_does_not_exist = False
|
||||||
loader_debug_info = None
|
loader_debug_info = None
|
||||||
@ -153,13 +159,14 @@ def technical_500_response(request, exc_type, exc_value, tb):
|
|||||||
'settings': get_safe_settings(),
|
'settings': get_safe_settings(),
|
||||||
'sys_executable': sys.executable,
|
'sys_executable': sys.executable,
|
||||||
'sys_version_info': '%d.%d.%d' % sys.version_info[0:3],
|
'sys_version_info': '%d.%d.%d' % sys.version_info[0:3],
|
||||||
|
'server_time': datetime.datetime.now(),
|
||||||
'django_version_info': get_version(),
|
'django_version_info': get_version(),
|
||||||
'sys_path' : sys.path,
|
'sys_path' : sys.path,
|
||||||
'template_info': template_info,
|
'template_info': template_info,
|
||||||
'template_does_not_exist': template_does_not_exist,
|
'template_does_not_exist': template_does_not_exist,
|
||||||
'loader_debug_info': loader_debug_info,
|
'loader_debug_info': loader_debug_info,
|
||||||
})
|
})
|
||||||
return HttpResponseServerError(t.render(c), mimetype='text/html')
|
return t.render(c)
|
||||||
|
|
||||||
def technical_404_response(request, exception):
|
def technical_404_response(request, exception):
|
||||||
"Create a technical 404 error response. The exception should be the Http404."
|
"Create a technical 404 error response. The exception should be the Http404."
|
||||||
@ -190,7 +197,7 @@ def empty_urlconf(request):
|
|||||||
c = Context({
|
c = Context({
|
||||||
'project_name': settings.SETTINGS_MODULE.split('.')[0]
|
'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):
|
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>
|
<th>Python Path:</th>
|
||||||
<td>{{ sys_path }}</td>
|
<td>{{ sys_path }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Server time:</th>
|
||||||
|
<td>{{server_time|date:"r"}}</td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
{% if unicode_hint %}
|
{% if unicode_hint %}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
from django.template import loader, RequestContext
|
from django.template import loader, RequestContext
|
||||||
from django.http import Http404, HttpResponse
|
from django.http import Http404, HttpResponse
|
||||||
from django.core.xheaders import populate_xheaders
|
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
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
|
|
||||||
def object_list(request, queryset, paginate_by=None, page=None,
|
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 = {}
|
if extra_context is None: extra_context = {}
|
||||||
queryset = queryset._clone()
|
queryset = queryset._clone()
|
||||||
if paginate_by:
|
if paginate_by:
|
||||||
paginator = ObjectPaginator(queryset, paginate_by)
|
paginator = QuerySetPaginator(queryset, paginate_by, allow_empty_first_page=allow_empty)
|
||||||
if not page:
|
if not page:
|
||||||
page = request.GET.get('page', 1)
|
page = request.GET.get('page', 1)
|
||||||
try:
|
try:
|
||||||
page_number = int(page)
|
page_number = int(page)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
if page == 'last':
|
if page == 'last':
|
||||||
page_number = paginator.pages
|
page_number = paginator.num_pages
|
||||||
else:
|
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
|
raise Http404
|
||||||
try:
|
try:
|
||||||
object_list = paginator.get_page(page_number - 1)
|
page_obj = paginator.page(page_number)
|
||||||
except InvalidPage:
|
except InvalidPage:
|
||||||
if page_number == 1 and allow_empty:
|
raise Http404
|
||||||
object_list = []
|
|
||||||
else:
|
|
||||||
raise Http404
|
|
||||||
c = RequestContext(request, {
|
c = RequestContext(request, {
|
||||||
'%s_list' % template_object_name: object_list,
|
'%s_list' % template_object_name: page_obj.object_list,
|
||||||
'is_paginated': paginator.pages > 1,
|
'paginator': paginator,
|
||||||
'results_per_page': paginate_by,
|
'page_obj': page_obj,
|
||||||
'has_next': paginator.has_next_page(page_number - 1),
|
|
||||||
'has_previous': paginator.has_previous_page(page_number - 1),
|
# Legacy template context stuff. New templates should use page_obj
|
||||||
'page': page_number,
|
# to access this instead.
|
||||||
'next': page_number + 1,
|
'is_paginated': page_obj.has_other_pages(),
|
||||||
'previous': page_number - 1,
|
'results_per_page': paginator.per_page,
|
||||||
'last_on_page': paginator.last_on_page(page_number - 1),
|
'has_next': page_obj.has_next(),
|
||||||
'first_on_page': paginator.first_on_page(page_number - 1),
|
'has_previous': page_obj.has_previous(),
|
||||||
'pages': paginator.pages,
|
'page': page_obj.number,
|
||||||
'hits' : paginator.hits,
|
'next': page_obj.next_page_number(),
|
||||||
'page_range' : paginator.page_range
|
'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)
|
}, context_processors)
|
||||||
else:
|
else:
|
||||||
c = RequestContext(request, {
|
c = RequestContext(request, {
|
||||||
'%s_list' % template_object_name: queryset,
|
'%s_list' % template_object_name: queryset,
|
||||||
'is_paginated': False
|
'paginator': None,
|
||||||
|
'page_obj': None,
|
||||||
|
'is_paginated': False,
|
||||||
}, context_processors)
|
}, context_processors)
|
||||||
if not allow_empty and len(queryset) == 0:
|
if not allow_empty and len(queryset) == 0:
|
||||||
raise Http404
|
raise Http404
|
||||||
|
@ -401,8 +401,8 @@ For example::
|
|||||||
# ...
|
# ...
|
||||||
|
|
||||||
def get_db_prep_save(self, value):
|
def get_db_prep_save(self, value):
|
||||||
return ''.join([''.join(l) for l in (self.north,
|
return ''.join([''.join(l) for l in (value.north,
|
||||||
self.east, self.south, self.west)])
|
value.east, value.south, value.west)])
|
||||||
|
|
||||||
``pre_save(self, model_instance, add)``
|
``pre_save(self, model_instance, add)``
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
@ -1372,7 +1372,7 @@ equivalent::
|
|||||||
Entry.objects.filter(blog__pk=3) # __pk implies __id__exact
|
Entry.objects.filter(blog__pk=3) # __pk implies __id__exact
|
||||||
|
|
||||||
Lookups that span relationships
|
Lookups that span relationships
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
-------------------------------
|
||||||
|
|
||||||
Django offers a powerful and intuitive way to "follow" relationships in
|
Django offers a powerful and intuitive way to "follow" relationships in
|
||||||
lookups, taking care of the SQL ``JOIN``\s for you automatically, behind the
|
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')
|
Blog.objects.filter(entry__headline__contains='Lennon')
|
||||||
|
|
||||||
Escaping percent signs and underscores in LIKE statements
|
Escaping percent signs and underscores in LIKE statements
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
---------------------------------------------------------
|
||||||
|
|
||||||
The field lookups that equate to ``LIKE`` SQL statements (``iexact``,
|
The field lookups that equate to ``LIKE`` SQL statements (``iexact``,
|
||||||
``contains``, ``icontains``, ``startswith``, ``istartswith``, ``endswith``
|
``contains``, ``icontains``, ``startswith``, ``istartswith``, ``endswith``
|
||||||
|
@ -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:
|
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
|
* ``results_per_page``: The number of objects per page. (Same as the
|
||||||
``paginate_by`` parameter.)
|
``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
|
* ``hits``: The total number of objects across *all* pages, not just this
|
||||||
page.
|
page.
|
||||||
|
|
||||||
* **New in Django development version:** ``page_range``: A list of the
|
* ``page_range``: A list of the page numbers that are available. This is
|
||||||
page numbers that are available. This is 1-based.
|
1-based.
|
||||||
|
|
||||||
Notes on pagination
|
Notes on pagination
|
||||||
~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~
|
||||||
|
@ -167,6 +167,20 @@ These commands will install Django in your Python installation's
|
|||||||
Installing the development version
|
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
|
If you'd like to be able to update your Django code occasionally with the
|
||||||
latest bug fixes and improvements, follow these instructions:
|
latest bug fixes and improvements, follow these instructions:
|
||||||
|
|
||||||
|
@ -626,7 +626,8 @@ option is ignored.
|
|||||||
``default``
|
``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``
|
``editable``
|
||||||
~~~~~~~~~~~~
|
~~~~~~~~~~~~
|
||||||
@ -1788,14 +1789,15 @@ For example::
|
|||||||
This example allows you to request ``Person.men.all()``, ``Person.women.all()``,
|
This example allows you to request ``Person.men.all()``, ``Person.women.all()``,
|
||||||
and ``Person.people.all()``, yielding predictable results.
|
and ``Person.people.all()``, yielding predictable results.
|
||||||
|
|
||||||
If you use custom ``Manager`` objects, take note that the first ``Manager``
|
If you use custom ``Manager`` objects, take note that the first
|
||||||
Django encounters (in order by which they're defined in the model) has a
|
``Manager`` Django encounters (in the order in which they're defined
|
||||||
special status. Django interprets the first ``Manager`` defined in a class as
|
in the model) has a special status. Django interprets this first
|
||||||
the "default" ``Manager``. Certain operations -- such as Django's admin site --
|
``Manager`` defined in a class as the "default" ``Manager``, and
|
||||||
use the default ``Manager`` to obtain lists of objects, so it's generally a
|
several parts of Django (though not the admin application) will use
|
||||||
good idea for the first ``Manager`` to be relatively unfiltered. In the last
|
that ``Manager`` exclusively for that model. As a result, it's often a
|
||||||
example, the ``people`` ``Manager`` is defined first -- so it's the default
|
good idea to be careful in your choice of default manager, in order to
|
||||||
``Manager``.
|
avoid a situation where overriding of ``get_query_set()`` results in
|
||||||
|
an inability to retrieve objects you'd like to work with.
|
||||||
|
|
||||||
Model methods
|
Model methods
|
||||||
=============
|
=============
|
||||||
|
@ -231,6 +231,16 @@ For example::
|
|||||||
# Create and save the new author instance. There's no need to do anything else.
|
# Create and save the new author instance. There's no need to do anything else.
|
||||||
>>> new_author = f.save()
|
>>> 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
|
Using a subset of fields on the form
|
||||||
------------------------------------
|
------------------------------------
|
||||||
|
|
||||||
|
@ -1333,13 +1333,14 @@ given length.
|
|||||||
|
|
||||||
An ``UploadedFile`` object has two attributes:
|
An ``UploadedFile`` object has two attributes:
|
||||||
|
|
||||||
====================== =====================================================
|
====================== ====================================================
|
||||||
Argument Description
|
Attribute Description
|
||||||
====================== =====================================================
|
====================== ====================================================
|
||||||
``filename`` The name of the file, provided by the uploading
|
``filename`` The name of the file, provided by the uploading
|
||||||
client.
|
client.
|
||||||
|
|
||||||
``content`` The array of bytes comprising the file content.
|
``content`` The array of bytes comprising the file content.
|
||||||
====================== =====================================================
|
====================== ====================================================
|
||||||
|
|
||||||
The string representation of an ``UploadedFile`` is the same as the filename
|
The string representation of an ``UploadedFile`` is the same as the filename
|
||||||
attribute.
|
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`_
|
.. _`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``
|
``ImageField``
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
@ -1499,6 +1532,41 @@ the bottom of this document, for examples of their use.
|
|||||||
``SplitDateTimeField``
|
``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
|
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,
|
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
|
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
|
``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
|
As mentioned above, any of these methods can raise a ``ValidationError``. For
|
||||||
any field, if the ``Field.clean()`` method raises a ``ValidationError``, any
|
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()
|
comment = forms.CharField()
|
||||||
|
|
||||||
This form will include three default TextInput widgets, with default rendering -
|
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::
|
each widget will be rendered exactly the same::
|
||||||
|
|
||||||
>>> f = CommentForm(auto_id=False)
|
>>> f = CommentForm(auto_id=False)
|
||||||
|
133
docs/pagination.txt
Normal file
133
docs/pagination.txt
Normal 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.
|
@ -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
|
The raw HTTP POST data. This is only useful for advanced processing. Use
|
||||||
``POST`` instead.
|
``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
|
Methods
|
||||||
-------
|
-------
|
||||||
|
|
||||||
@ -189,6 +199,23 @@ Methods
|
|||||||
Returns ``True`` if the request is secure; that is, if it was made with
|
Returns ``True`` if the request is secure; that is, if it was made with
|
||||||
HTTPS.
|
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
|
QueryDict objects
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
@ -48,10 +48,10 @@ Using file-based sessions
|
|||||||
To use file-based sessions, set the ``SESSION_ENGINE`` setting to
|
To use file-based sessions, set the ``SESSION_ENGINE`` setting to
|
||||||
``"django.contrib.sessions.backends.file"``.
|
``"django.contrib.sessions.backends.file"``.
|
||||||
|
|
||||||
You might also want to set the ``SESSION_FILE_PATH`` setting (which
|
You might also want to set the ``SESSION_FILE_PATH`` setting (which defaults
|
||||||
defaults to ``/tmp``) to control where Django stores session files. Be
|
to output from ``tempfile.gettempdir()``, most likely ``/tmp``) to control
|
||||||
sure to check that your Web server has permissions to read and write to
|
where Django stores session files. Be sure to check that your Web server has
|
||||||
this location.
|
permissions to read and write to this location.
|
||||||
|
|
||||||
Using cache-based sessions
|
Using cache-based sessions
|
||||||
--------------------------
|
--------------------------
|
||||||
|
@ -185,8 +185,11 @@ ADMIN_MEDIA_PREFIX
|
|||||||
|
|
||||||
Default: ``'/media/'``
|
Default: ``'/media/'``
|
||||||
|
|
||||||
The URL prefix for admin media -- CSS, JavaScript and images. Make sure to use
|
The URL prefix for admin media -- CSS, JavaScript and images used by
|
||||||
a trailing slash.
|
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
|
ADMINS
|
||||||
------
|
------
|
||||||
@ -754,7 +757,9 @@ ROOT_URLCONF
|
|||||||
Default: Not defined
|
Default: Not defined
|
||||||
|
|
||||||
A string representing the full Python import path to your root URLconf. For example:
|
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
|
.. _How Django processes a request: ../url_dispatch/#how-django-processes-a-request
|
||||||
|
|
||||||
|
@ -245,6 +245,13 @@ request to the URL ``/rss/beats/0613/``:
|
|||||||
subclass of ``ObjectDoesNotExist``. Raising ``ObjectDoesNotExist`` in
|
subclass of ``ObjectDoesNotExist``. Raising ``ObjectDoesNotExist`` in
|
||||||
``get_object()`` tells Django to produce a 404 error for that request.
|
``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>``,
|
* To generate the feed's ``<title>``, ``<link>`` and ``<description>``,
|
||||||
Django uses the ``title()``, ``link()`` and ``description()`` methods. In
|
Django uses the ``title()``, ``link()`` and ``description()`` methods. In
|
||||||
the previous example, they were simple string class attributes, but this
|
the previous example, they were simple string class attributes, but this
|
||||||
|
@ -429,8 +429,9 @@ all block tags. For example::
|
|||||||
# base.html
|
# base.html
|
||||||
|
|
||||||
{% autoescape off %}
|
{% autoescape off %}
|
||||||
<h1>{% block title %}</h1>
|
<h1>{% block title %}{% endblock %}</h1>
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
{% endblock %}
|
||||||
{% endautoescape %}
|
{% endautoescape %}
|
||||||
|
|
||||||
|
|
||||||
@ -438,10 +439,11 @@ all block tags. For example::
|
|||||||
|
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block title %}This & that{% endblock %}
|
{% 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
|
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>
|
<h1>This & that</h1>
|
||||||
<b>Hello!</b>
|
<b>Hello!</b>
|
||||||
@ -1222,20 +1224,20 @@ Built-in filter reference
|
|||||||
add
|
add
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
Adds the arg to the value.
|
Adds the argument to the value.
|
||||||
|
|
||||||
For example::
|
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
|
addslashes
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
|
|
||||||
Adds slashes before quotes. Useful for escaping strings in CSV, for example.
|
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.
|
use the `escapejs`_ filter instead.
|
||||||
|
|
||||||
capfirst
|
capfirst
|
||||||
@ -1257,7 +1259,7 @@ For example::
|
|||||||
|
|
||||||
{{ value|cut:" "}}
|
{{ 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
|
date
|
||||||
~~~~
|
~~~~
|
||||||
@ -1268,35 +1270,40 @@ For example::
|
|||||||
|
|
||||||
{{ value|date:"D d M Y" }}
|
{{ value|date:"D d M Y" }}
|
||||||
|
|
||||||
If ``value`` is a datetime object (ie. datetime.datetime.now()), the output
|
If ``value`` is a ``datetime`` object (e.g., the result of
|
||||||
would be formatted like ``Wed 09 Jan 2008``.
|
``datetime.datetime.now()``), the output will be the string
|
||||||
|
``'Wed 09 Jan 2008'``.
|
||||||
|
|
||||||
default
|
default
|
||||||
~~~~~~~
|
~~~~~~~
|
||||||
|
|
||||||
If value is unavailable, use given default.
|
If value evaluates to ``False``, use given default. Otherwise, use the value.
|
||||||
|
|
||||||
For example::
|
For example::
|
||||||
|
|
||||||
{{ value|default:"nothing" }}
|
{{ 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
|
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::
|
For example::
|
||||||
|
|
||||||
{{ value|default_if_none:"nothing" }}
|
{{ 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
|
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.
|
the argument.
|
||||||
|
|
||||||
For example::
|
For example::
|
||||||
@ -1304,46 +1311,42 @@ For example::
|
|||||||
{{ value|dictsort:"name" }}
|
{{ value|dictsort:"name" }}
|
||||||
|
|
||||||
If ``value`` is::
|
If ``value`` is::
|
||||||
|
|
||||||
[
|
[
|
||||||
{'name': 'zed', 'age': 19}
|
{'name': 'zed', 'age': 19},
|
||||||
{'name': 'amy', 'age': 22},
|
{'name': 'amy', 'age': 22},
|
||||||
{'name': 'joe', 'age': 31},
|
{'name': 'joe', 'age': 31},
|
||||||
]
|
]
|
||||||
|
|
||||||
then the output would be::
|
then the output would be::
|
||||||
|
|
||||||
[
|
[
|
||||||
{'name': 'amy', 'age': 22},
|
{'name': 'amy', 'age': 22},
|
||||||
{'name': 'joe', 'age': 31},
|
{'name': 'joe', 'age': 31},
|
||||||
{'name': 'zed', 'age': 19}
|
{'name': 'zed', 'age': 19},
|
||||||
]
|
]
|
||||||
|
|
||||||
dictsortreversed
|
dictsortreversed
|
||||||
~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Takes a list of dictionaries, returns that list sorted in reverse order by the
|
Takes a list of dictionaries and returns that list sorted in reverse order by
|
||||||
key given in the argument. This works exactly the same as the above filter, but
|
the key given in the argument. This works exactly the same as the above filter,
|
||||||
the returned value will be in reverse order.
|
but the returned value will be in reverse order.
|
||||||
|
|
||||||
divisibleby
|
divisibleby
|
||||||
~~~~~~~~~~~
|
~~~~~~~~~~~
|
||||||
|
|
||||||
Returns true if the value is divisible by the argument.
|
Returns ``True`` if the value is divisible by the argument.
|
||||||
|
|
||||||
For example::
|
For example::
|
||||||
|
|
||||||
{{ value|divisibleby:3 }}
|
{{ value|divisibleby:"3" }}
|
||||||
|
|
||||||
If ``value`` is ``21``, the output would be ``True``.
|
If ``value`` is ``21``, the output would be ``True``.
|
||||||
|
|
||||||
escape
|
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:
|
Escapes a string's HTML. Specifically, it makes these replacements:
|
||||||
|
|
||||||
* ``<`` is converted to ``<``
|
* ``<`` is converted to ``<``
|
||||||
@ -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
|
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.
|
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
|
escapejs
|
||||||
~~~~~~~~
|
~~~~~~~~
|
||||||
|
|
||||||
@ -1392,7 +1399,7 @@ For example::
|
|||||||
|
|
||||||
{{ value|first }}
|
{{ 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
|
fix_ampersands
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
@ -1403,11 +1410,11 @@ For example::
|
|||||||
|
|
||||||
{{ value|fix_ampersands }}
|
{{ value|fix_ampersands }}
|
||||||
|
|
||||||
If ``value`` is ``Tom & Jerry``, the output would be ``Tom & Jerry``.
|
If ``value`` is ``Tom & Jerry``, the output will be ``Tom & Jerry``.
|
||||||
|
|
||||||
**New in Django development version**: you probably don't need to use this
|
**New in Django development version**: This filter generally is no longer
|
||||||
filter since ampersands will be automatically escaped. See escape_ for more on
|
useful, because ampersands are automatically escaped in templates. See escape_
|
||||||
how auto-escaping works.
|
for more on how auto-escaping works.
|
||||||
|
|
||||||
floatformat
|
floatformat
|
||||||
~~~~~~~~~~~
|
~~~~~~~~~~~
|
||||||
@ -1463,16 +1470,16 @@ filter.
|
|||||||
get_digit
|
get_digit
|
||||||
~~~~~~~~~
|
~~~~~~~~~
|
||||||
|
|
||||||
Given a whole number, returns the requested digit of it, where 1 is the
|
Given a whole number, returns the requested digit, where 1 is the right-most
|
||||||
right-most digit, 2 is the second-right-most digit, etc. Returns the original
|
digit, 2 is the second-right-most digit, etc. Returns the original value for
|
||||||
value for invalid input (if input or argument is not an integer, or if argument
|
invalid input (if input or argument is not an integer, or if argument is less
|
||||||
is less than 1). Otherwise, output is always an integer.
|
than 1). Otherwise, output is always an integer.
|
||||||
|
|
||||||
For example::
|
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
|
iriencode
|
||||||
~~~~~~~~~
|
~~~~~~~~~
|
||||||
@ -1493,7 +1500,8 @@ For example::
|
|||||||
|
|
||||||
{{ value|join:" // " }}
|
{{ 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
|
last
|
||||||
~~~~
|
~~~~
|
||||||
@ -1506,29 +1514,30 @@ For example::
|
|||||||
|
|
||||||
{{ value|last }}
|
{{ 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
|
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::
|
For example::
|
||||||
|
|
||||||
{{ value|length }}
|
{{ 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
|
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::
|
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
|
linebreaks
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
@ -1541,7 +1550,7 @@ For example::
|
|||||||
|
|
||||||
{{ value|linebreaks }}
|
{{ 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>``.
|
slug</p>``.
|
||||||
|
|
||||||
linebreaksbr
|
linebreaksbr
|
||||||
@ -1571,7 +1580,7 @@ For example::
|
|||||||
|
|
||||||
{{ value|lower }}
|
{{ 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
|
make_list
|
||||||
~~~~~~~~~
|
~~~~~~~~~
|
||||||
@ -1583,8 +1592,9 @@ For example::
|
|||||||
|
|
||||||
{{ value|make_list }}
|
{{ value|make_list }}
|
||||||
|
|
||||||
If ``value`` is "Joe", the output would be ``[u'J', u'o', u'e']. If ``value`` is
|
If ``value`` is the string ``"Joe"``, the output would be the list
|
||||||
123, the output would be ``[1, 2, 3]``.
|
``[u'J', u'o', u'e']``. If ``value`` is ``123``, the output will be the list
|
||||||
|
``[1, 2, 3]``.
|
||||||
|
|
||||||
phone2numeric
|
phone2numeric
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
@ -1629,25 +1639,25 @@ __ http://www.python.org/doc/2.5/lib/module-pprint.html
|
|||||||
random
|
random
|
||||||
~~~~~~
|
~~~~~~
|
||||||
|
|
||||||
Returns a random item from the list.
|
Returns a random item from the given list.
|
||||||
|
|
||||||
For example::
|
For example::
|
||||||
|
|
||||||
{{ value|random }}
|
{{ 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
|
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::
|
For example::
|
||||||
|
|
||||||
{{ value|removetags:"b span"|safe }}
|
{{ value|removetags:"b span"|safe }}
|
||||||
|
|
||||||
If ``value`` is ``<b>Joel</b> <button>is</button> a <span>slug</span>`` the
|
If ``value`` is ``"<b>Joel</b> <button>is</button> a <span>slug</span>"`` the
|
||||||
output would be ``Joel <button>is</button> a slug``.
|
output will be ``"Joel <button>is</button> a slug"``.
|
||||||
|
|
||||||
rjust
|
rjust
|
||||||
~~~~~
|
~~~~~
|
||||||
@ -1684,7 +1694,7 @@ For example::
|
|||||||
|
|
||||||
{{ value|slugify }}
|
{{ 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
|
stringformat
|
||||||
~~~~~~~~~~~~
|
~~~~~~~~~~~~
|
||||||
@ -1700,7 +1710,7 @@ For example::
|
|||||||
|
|
||||||
{{ value|stringformat:"s" }}
|
{{ 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
|
striptags
|
||||||
~~~~~~~~~
|
~~~~~~~~~
|
||||||
@ -1711,8 +1721,8 @@ For example::
|
|||||||
|
|
||||||
{{ value|striptags }}
|
{{ value|striptags }}
|
||||||
|
|
||||||
If ``value`` is ``<b>Joel</b> <button>is</button> a <span>slug</span>`` the
|
If ``value`` is ``"<b>Joel</b> <button>is</button> a <span>slug</span>"``, the
|
||||||
output would be ``Joel is a slug``.
|
output will be ``"Joel is a slug"``.
|
||||||
|
|
||||||
time
|
time
|
||||||
~~~~
|
~~~~
|
||||||
@ -1726,13 +1736,13 @@ For example::
|
|||||||
|
|
||||||
{{ value|time:"H:i" }}
|
{{ value|time:"H:i" }}
|
||||||
|
|
||||||
If ``value`` is ``datetime.datetime.now()``, the output would be formatted
|
If ``value`` is equivalent to ``datetime.datetime.now()``, the output will be
|
||||||
like ``01:23``.
|
the string ``"01:23"``.
|
||||||
|
|
||||||
timesince
|
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
|
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*).
|
the comparison point (without the argument, the comparison point is *now*).
|
||||||
@ -1774,7 +1784,7 @@ For example::
|
|||||||
|
|
||||||
{{ value|truncatewords:2 }}
|
{{ 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
|
truncatewords_html
|
||||||
~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~
|
||||||
@ -1792,10 +1802,8 @@ unordered_list
|
|||||||
Recursively takes a self-nested list and returns an HTML unordered list --
|
Recursively takes a self-nested list and returns an HTML unordered list --
|
||||||
WITHOUT opening and closing <ul> tags.
|
WITHOUT opening and closing <ul> tags.
|
||||||
|
|
||||||
**Changed in Django development version**
|
**New in Django development version:** The format accepted by
|
||||||
|
``unordered_list`` has changed to be easier to understand.
|
||||||
The format accepted by ``unordered_list`` has changed to an easier to
|
|
||||||
understand format.
|
|
||||||
|
|
||||||
The list is assumed to be in the proper format. For example, if ``var`` contains
|
The list is assumed to be in the proper format. For example, if ``var`` contains
|
||||||
``['States', ['Kansas', ['Lawrence', 'Topeka'], 'Illinois']]``, then
|
``['States', ['Kansas', ['Lawrence', 'Topeka'], 'Illinois']]``, then
|
||||||
@ -1825,7 +1833,7 @@ For example::
|
|||||||
|
|
||||||
{{ value|upper }}
|
{{ 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
|
urlencode
|
||||||
~~~~~~~~~
|
~~~~~~~~~
|
||||||
@ -1844,9 +1852,9 @@ For example::
|
|||||||
|
|
||||||
{{ value|urlize }}
|
{{ value|urlize }}
|
||||||
|
|
||||||
If ``value`` is ``Check out www.djangoproject.com``, the output would be
|
If ``value`` is ``"Check out www.djangoproject.com"``, the output will be
|
||||||
``Check out <a
|
``"Check out <a
|
||||||
href="http://www.djangoproject.com">www.djangoproject.com</a>``.
|
href="http://www.djangoproject.com">www.djangoproject.com</a>"``.
|
||||||
|
|
||||||
urlizetrunc
|
urlizetrunc
|
||||||
~~~~~~~~~~~
|
~~~~~~~~~~~
|
||||||
@ -1862,9 +1870,9 @@ For example::
|
|||||||
|
|
||||||
{{ value|urlizetrunc:15 }}
|
{{ value|urlizetrunc:15 }}
|
||||||
|
|
||||||
If ``value`` is ``Check out www.djangoproject.com``, the output would be
|
If ``value`` is ``"Check out www.djangoproject.com"``, the output would be
|
||||||
``Check out <a
|
``'Check out <a
|
||||||
href="http://www.djangoproject.com">www.djangopr...</a>``.
|
href="http://www.djangoproject.com">www.djangopr...</a>'``.
|
||||||
|
|
||||||
wordcount
|
wordcount
|
||||||
~~~~~~~~~
|
~~~~~~~~~
|
||||||
|
@ -37,6 +37,12 @@ A quick rundown:
|
|||||||
form will alter data server-side. Whenever you create a form that alters
|
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
|
data server-side, use ``method="post"``. This tip isn't specific to
|
||||||
Django; it's just good Web development practice.
|
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
|
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
|
something with it. Remember, in `Tutorial 3`_, we created a URLconf for the
|
||||||
|
@ -32,9 +32,11 @@ How Django processes a request
|
|||||||
When a user requests a page from your Django-powered site, this is the
|
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:
|
algorithm the system follows to determine which Python code to execute:
|
||||||
|
|
||||||
1. Django looks at the ``ROOT_URLCONF`` setting in your `settings file`_.
|
1. Django determines the root URLConf module to use; ordinarily
|
||||||
This should be a string representing the full Python import path to your
|
this is the value of the ``ROOT_URLCONF`` setting in your
|
||||||
URLconf. For example: ``"mydjangoapps.urls"``.
|
`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
|
2. Django loads that Python module and looks for the variable
|
||||||
``urlpatterns``. This should be a Python list, in the format returned by
|
``urlpatterns``. This should be a Python list, in the format returned by
|
||||||
the function ``django.conf.urls.defaults.patterns()``.
|
the function ``django.conf.urls.defaults.patterns()``.
|
||||||
|
@ -71,8 +71,9 @@ u'ABC123'
|
|||||||
>>> fran.save()
|
>>> fran.save()
|
||||||
>>> Employee.objects.filter(last_name__exact='Jones')
|
>>> Employee.objects.filter(last_name__exact='Jones')
|
||||||
[<Employee: Dan Jones>, <Employee: Fran Jones>]
|
[<Employee: Dan Jones>, <Employee: Fran Jones>]
|
||||||
>>> Employee.objects.in_bulk(['ABC123', 'XYZ456'])
|
>>> emps = Employee.objects.in_bulk(['ABC123', 'XYZ456'])
|
||||||
{u'XYZ456': <Employee: Fran Jones>, u'ABC123': <Employee: Dan Jones>}
|
>>> emps['ABC123']
|
||||||
|
<Employee: Dan Jones>
|
||||||
|
|
||||||
>>> b = Business(name='Sears')
|
>>> b = Business(name='Sears')
|
||||||
>>> b.save()
|
>>> b.save()
|
||||||
|
@ -76,8 +76,11 @@ Article 4
|
|||||||
|
|
||||||
# in_bulk() takes a list of IDs and returns a dictionary mapping IDs
|
# in_bulk() takes a list of IDs and returns a dictionary mapping IDs
|
||||||
# to objects.
|
# to objects.
|
||||||
>>> Article.objects.in_bulk([1, 2])
|
>>> arts = Article.objects.in_bulk([1, 2])
|
||||||
{1: <Article: Article 1>, 2: <Article: Article 2>}
|
>>> arts[1]
|
||||||
|
<Article: Article 1>
|
||||||
|
>>> arts[2]
|
||||||
|
<Article: Article 2>
|
||||||
>>> Article.objects.in_bulk([3])
|
>>> Article.objects.in_bulk([3])
|
||||||
{3: <Article: Article 3>}
|
{3: <Article: Article 3>}
|
||||||
>>> Article.objects.in_bulk([1000])
|
>>> Article.objects.in_bulk([1000])
|
||||||
|
@ -41,25 +41,33 @@ __test__ = {'API_TESTS':u"""
|
|||||||
True
|
True
|
||||||
|
|
||||||
# Attempt to add a Musician without a first_name.
|
# Attempt to add a Musician without a first_name.
|
||||||
>>> man.get_validation_errors(MultiValueDict({'last_name': ['Blakey']}))
|
>>> man.get_validation_errors(MultiValueDict({'last_name': ['Blakey']}))['first_name']
|
||||||
{'first_name': [u'This field is required.']}
|
[u'This field is required.']
|
||||||
|
|
||||||
# Attempt to add a Musician without a first_name and last_name.
|
# Attempt to add a Musician without a first_name and last_name.
|
||||||
>>> man.get_validation_errors(MultiValueDict({}))
|
>>> errors = man.get_validation_errors(MultiValueDict({}))
|
||||||
{'first_name': [u'This field is required.'], 'last_name': [u'This field is required.']}
|
>>> 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.
|
# Attempt to create an Album without a name or musician.
|
||||||
>>> man = Album.AddManipulator()
|
>>> man = Album.AddManipulator()
|
||||||
>>> man.get_validation_errors(MultiValueDict({}))
|
>>> errors = man.get_validation_errors(MultiValueDict({}))
|
||||||
{'musician': [u'This field is required.'], 'name': [u'This field is required.']}
|
>>> errors['musician']
|
||||||
|
[u'This field is required.']
|
||||||
|
>>> errors['name']
|
||||||
|
[u'This field is required.']
|
||||||
|
|
||||||
# Attempt to create an Album with an invalid musician.
|
# Attempt to create an Album with an invalid musician.
|
||||||
>>> man.get_validation_errors(MultiValueDict({'name': ['Sallies Fforth'], 'musician': ['foo']}))
|
>>> errors = man.get_validation_errors(MultiValueDict({'name': ['Sallies Fforth'], 'musician': ['foo']}))
|
||||||
{'musician': [u"Select a valid choice; 'foo' is not in [u'', u'1']."]}
|
>>> errors['musician']
|
||||||
|
[u"Select a valid choice; 'foo' is not in [u'', u'1']."]
|
||||||
|
|
||||||
# Attempt to create an Album with an invalid release_date.
|
# Attempt to create an Album with an invalid release_date.
|
||||||
>>> man.get_validation_errors(MultiValueDict({'name': ['Sallies Fforth'], 'musician': ['1'], 'release_date': 'today'}))
|
>>> errors = 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['release_date']
|
||||||
|
[u'Enter a valid date in YYYY-MM-DD format.']
|
||||||
|
|
||||||
# Create an Album without a release_date (because it's optional).
|
# Create an Album without a release_date (because it's optional).
|
||||||
>>> data = MultiValueDict({'name': ['Ella and Basie'], 'musician': ['1']})
|
>>> data = MultiValueDict({'name': ['Ella and Basie'], 'musician': ['1']})
|
||||||
|
@ -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 = CategoryForm({'name': 'Entertainment', 'slug': 'entertainment', 'url': 'entertainment'})
|
||||||
>>> f.is_valid()
|
>>> f.is_valid()
|
||||||
True
|
True
|
||||||
>>> f.cleaned_data
|
>>> f.cleaned_data['url']
|
||||||
{'url': u'entertainment', 'name': u'Entertainment', 'slug': u'entertainment'}
|
u'entertainment'
|
||||||
|
>>> f.cleaned_data['name']
|
||||||
|
u'Entertainment'
|
||||||
|
>>> f.cleaned_data['slug']
|
||||||
|
u'entertainment'
|
||||||
>>> obj = f.save()
|
>>> obj = f.save()
|
||||||
>>> obj
|
>>> obj
|
||||||
<Category: Entertainment>
|
<Category: Entertainment>
|
||||||
@ -245,8 +249,12 @@ True
|
|||||||
>>> f = CategoryForm({'name': "It's a test", 'slug': 'its-test', 'url': 'test'})
|
>>> f = CategoryForm({'name': "It's a test", 'slug': 'its-test', 'url': 'test'})
|
||||||
>>> f.is_valid()
|
>>> f.is_valid()
|
||||||
True
|
True
|
||||||
>>> f.cleaned_data
|
>>> f.cleaned_data['url']
|
||||||
{'url': u'test', 'name': u"It's a test", 'slug': u'its-test'}
|
u'test'
|
||||||
|
>>> f.cleaned_data['name']
|
||||||
|
u"It's a test"
|
||||||
|
>>> f.cleaned_data['slug']
|
||||||
|
u'its-test'
|
||||||
>>> obj = f.save()
|
>>> obj = f.save()
|
||||||
>>> obj
|
>>> obj
|
||||||
<Category: It's a test>
|
<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 = CategoryForm({'name': 'Third test', 'slug': 'third-test', 'url': 'third'})
|
||||||
>>> f.is_valid()
|
>>> f.is_valid()
|
||||||
True
|
True
|
||||||
>>> f.cleaned_data
|
>>> f.cleaned_data['url']
|
||||||
{'url': u'third', 'name': u'Third test', 'slug': u'third-test'}
|
u'third'
|
||||||
|
>>> f.cleaned_data['name']
|
||||||
|
u'Third test'
|
||||||
|
>>> f.cleaned_data['slug']
|
||||||
|
u'third-test'
|
||||||
>>> obj = f.save(commit=False)
|
>>> obj = f.save(commit=False)
|
||||||
>>> obj
|
>>> obj
|
||||||
<Category: Third test>
|
<Category: Third test>
|
||||||
@ -272,8 +284,10 @@ True
|
|||||||
|
|
||||||
If you call save() with invalid data, you'll get a ValueError.
|
If you call save() with invalid data, you'll get a ValueError.
|
||||||
>>> f = CategoryForm({'name': '', 'slug': '', 'url': 'foo'})
|
>>> f = CategoryForm({'name': '', 'slug': '', 'url': 'foo'})
|
||||||
>>> f.errors
|
>>> f.errors['name']
|
||||||
{'name': [u'This field is required.'], 'slug': [u'This field is required.']}
|
[u'This field is required.']
|
||||||
|
>>> f.errors['slug']
|
||||||
|
[u'This field is required.']
|
||||||
>>> f.cleaned_data
|
>>> f.cleaned_data
|
||||||
Traceback (most recent call last):
|
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.']
|
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 ####################################################
|
# ModelMultipleChoiceField ####################################################
|
||||||
|
|
||||||
@ -730,6 +757,10 @@ Traceback (most recent call last):
|
|||||||
...
|
...
|
||||||
ValidationError: [u'Select a valid choice. 4 is not one of the available choices.']
|
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 ############################################################
|
# 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 = PhoneNumberForm({'phone': '(312) 555-1212', 'description': 'Assistance'})
|
||||||
>>> f.is_valid()
|
>>> f.is_valid()
|
||||||
True
|
True
|
||||||
>>> f.cleaned_data
|
>>> f.cleaned_data['phone']
|
||||||
{'phone': u'312-555-1212', 'description': u'Assistance'}
|
u'312-555-1212'
|
||||||
|
>>> f.cleaned_data['description']
|
||||||
|
u'Assistance'
|
||||||
|
|
||||||
# FileField ###################################################################
|
# FileField ###################################################################
|
||||||
|
|
||||||
@ -766,7 +799,7 @@ True
|
|||||||
<class 'django.newforms.fields.UploadedFile'>
|
<class 'django.newforms.fields.UploadedFile'>
|
||||||
>>> instance = f.save()
|
>>> instance = f.save()
|
||||||
>>> instance.file
|
>>> instance.file
|
||||||
u'.../test1.txt'
|
u'...test1.txt'
|
||||||
|
|
||||||
# Edit an instance that already has the file defined in the model. This will not
|
# 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.
|
# save the file again, but leave it exactly as it is.
|
||||||
@ -775,10 +808,10 @@ u'.../test1.txt'
|
|||||||
>>> f.is_valid()
|
>>> f.is_valid()
|
||||||
True
|
True
|
||||||
>>> f.cleaned_data['file']
|
>>> f.cleaned_data['file']
|
||||||
u'.../test1.txt'
|
u'...test1.txt'
|
||||||
>>> instance = f.save()
|
>>> instance = f.save()
|
||||||
>>> instance.file
|
>>> instance.file
|
||||||
u'.../test1.txt'
|
u'...test1.txt'
|
||||||
|
|
||||||
# Delete the current file since this is not done by Django.
|
# Delete the current file since this is not done by Django.
|
||||||
|
|
||||||
@ -791,7 +824,7 @@ u'.../test1.txt'
|
|||||||
True
|
True
|
||||||
>>> instance = f.save()
|
>>> instance = f.save()
|
||||||
>>> instance.file
|
>>> instance.file
|
||||||
u'.../test2.txt'
|
u'...test2.txt'
|
||||||
|
|
||||||
>>> instance.delete()
|
>>> instance.delete()
|
||||||
|
|
||||||
@ -810,7 +843,7 @@ True
|
|||||||
True
|
True
|
||||||
>>> instance = f.save()
|
>>> instance = f.save()
|
||||||
>>> instance.file
|
>>> instance.file
|
||||||
u'.../test3.txt'
|
u'...test3.txt'
|
||||||
>>> instance.delete()
|
>>> instance.delete()
|
||||||
|
|
||||||
# ImageField ###################################################################
|
# ImageField ###################################################################
|
||||||
@ -832,7 +865,7 @@ True
|
|||||||
<class 'django.newforms.fields.UploadedFile'>
|
<class 'django.newforms.fields.UploadedFile'>
|
||||||
>>> instance = f.save()
|
>>> instance = f.save()
|
||||||
>>> instance.image
|
>>> instance.image
|
||||||
u'.../test.png'
|
u'...test.png'
|
||||||
|
|
||||||
# Edit an instance that already has the image defined in the model. This will not
|
# 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.
|
# save the image again, but leave it exactly as it is.
|
||||||
@ -841,10 +874,10 @@ u'.../test.png'
|
|||||||
>>> f.is_valid()
|
>>> f.is_valid()
|
||||||
True
|
True
|
||||||
>>> f.cleaned_data['image']
|
>>> f.cleaned_data['image']
|
||||||
u'.../test.png'
|
u'...test.png'
|
||||||
>>> instance = f.save()
|
>>> instance = f.save()
|
||||||
>>> instance.image
|
>>> instance.image
|
||||||
u'.../test.png'
|
u'...test.png'
|
||||||
|
|
||||||
# Delete the current image since this is not done by Django.
|
# Delete the current image since this is not done by Django.
|
||||||
|
|
||||||
@ -857,7 +890,7 @@ u'.../test.png'
|
|||||||
True
|
True
|
||||||
>>> instance = f.save()
|
>>> instance = f.save()
|
||||||
>>> instance.image
|
>>> instance.image
|
||||||
u'.../test2.png'
|
u'...test2.png'
|
||||||
|
|
||||||
>>> instance.delete()
|
>>> instance.delete()
|
||||||
|
|
||||||
@ -876,7 +909,7 @@ True
|
|||||||
True
|
True
|
||||||
>>> instance = f.save()
|
>>> instance = f.save()
|
||||||
>>> instance.image
|
>>> instance.image
|
||||||
u'.../test3.png'
|
u'...test3.png'
|
||||||
>>> instance.delete()
|
>>> instance.delete()
|
||||||
|
|
||||||
"""}
|
"""}
|
||||||
|
@ -4,6 +4,11 @@
|
|||||||
Django provides a framework for paginating a list of objects in a few lines
|
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
|
of code. This is often useful for dividing search results or long lists of
|
||||||
objects into easily readable pages.
|
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
|
from django.db import models
|
||||||
@ -16,70 +21,210 @@ class Article(models.Model):
|
|||||||
return self.headline
|
return self.headline
|
||||||
|
|
||||||
__test__ = {'API_TESTS':"""
|
__test__ = {'API_TESTS':"""
|
||||||
# prepare a list of objects for pagination
|
# Prepare a list of objects for pagination.
|
||||||
>>> from datetime import datetime
|
>>> from datetime import datetime
|
||||||
>>> for x in range(1, 10):
|
>>> for x in range(1, 10):
|
||||||
... a = Article(headline='Article %s' % x, pub_date=datetime(2005, 7, 29))
|
... a = Article(headline='Article %s' % x, pub_date=datetime(2005, 7, 29))
|
||||||
... a.save()
|
... 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
|
>>> from django.core.paginator import ObjectPaginator, InvalidPage
|
||||||
>>> paginator = ObjectPaginator(Article.objects.all(), 5)
|
>>> paginator = ObjectPaginator(Article.objects.all(), 5)
|
||||||
|
|
||||||
# the paginator knows how many hits and pages it contains
|
|
||||||
>>> paginator.hits
|
>>> paginator.hits
|
||||||
9
|
9
|
||||||
|
|
||||||
>>> paginator.pages
|
>>> paginator.pages
|
||||||
2
|
2
|
||||||
|
>>> paginator.page_range
|
||||||
|
[1, 2]
|
||||||
|
|
||||||
# get the first page (zero-based)
|
# Get the first page.
|
||||||
>>> paginator.get_page(0)
|
>>> paginator.get_page(0)
|
||||||
[<Article: Article 1>, <Article: Article 2>, <Article: Article 3>, <Article: Article 4>, <Article: Article 5>]
|
[<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)
|
>>> paginator.has_next_page(0)
|
||||||
True
|
True
|
||||||
|
|
||||||
>>> paginator.has_previous_page(0)
|
>>> paginator.has_previous_page(0)
|
||||||
False
|
False
|
||||||
|
|
||||||
# check the second page
|
|
||||||
>>> paginator.has_next_page(1)
|
|
||||||
False
|
|
||||||
|
|
||||||
>>> paginator.has_previous_page(1)
|
|
||||||
True
|
|
||||||
|
|
||||||
>>> paginator.first_on_page(0)
|
>>> paginator.first_on_page(0)
|
||||||
1
|
1
|
||||||
>>> paginator.first_on_page(1)
|
|
||||||
6
|
|
||||||
>>> paginator.last_on_page(0)
|
>>> paginator.last_on_page(0)
|
||||||
5
|
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)
|
>>> paginator.last_on_page(1)
|
||||||
9
|
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.
|
# Add a few more records to test out the orphans feature.
|
||||||
>>> for x in range(10, 13):
|
>>> for x in range(10, 13):
|
||||||
... Article(headline="Article %s" % x, pub_date=datetime(2006, 10, 6)).save()
|
... 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 = ObjectPaginator(Article.objects.all(), 10, orphans=3)
|
||||||
>>> paginator.pages
|
>>> paginator.pages
|
||||||
1
|
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 = ObjectPaginator(Article.objects.all(), 10, orphans=1)
|
||||||
>>> paginator.pages
|
>>> paginator.pages
|
||||||
2
|
2
|
||||||
|
|
||||||
# The paginator can provide a list of all available pages.
|
|
||||||
>>> paginator = ObjectPaginator(Article.objects.all(), 10)
|
|
||||||
>>> paginator.page_range
|
|
||||||
[1, 2]
|
|
||||||
"""}
|
"""}
|
||||||
|
@ -41,8 +41,8 @@ __test__ = {'API_TESTS':"""
|
|||||||
23
|
23
|
||||||
|
|
||||||
>>> p = Person(**dict(valid_params, id='foo'))
|
>>> p = Person(**dict(valid_params, id='foo'))
|
||||||
>>> p.validate()
|
>>> p.validate()['id']
|
||||||
{'id': [u'This value must be an integer.']}
|
[u'This value must be an integer.']
|
||||||
|
|
||||||
>>> p = Person(**dict(valid_params, id=None))
|
>>> p = Person(**dict(valid_params, id=None))
|
||||||
>>> p.validate()
|
>>> p.validate()
|
||||||
@ -75,8 +75,8 @@ True
|
|||||||
False
|
False
|
||||||
|
|
||||||
>>> p = Person(**dict(valid_params, is_child='foo'))
|
>>> p = Person(**dict(valid_params, is_child='foo'))
|
||||||
>>> p.validate()
|
>>> p.validate()['is_child']
|
||||||
{'is_child': [u'This value must be either True or False.']}
|
[u'This value must be either True or False.']
|
||||||
|
|
||||||
>>> p = Person(**dict(valid_params, name=u'Jose'))
|
>>> p = Person(**dict(valid_params, name=u'Jose'))
|
||||||
>>> p.validate()
|
>>> p.validate()
|
||||||
@ -115,8 +115,8 @@ datetime.date(2000, 5, 3)
|
|||||||
datetime.date(2000, 5, 3)
|
datetime.date(2000, 5, 3)
|
||||||
|
|
||||||
>>> p = Person(**dict(valid_params, birthdate='foo'))
|
>>> p = Person(**dict(valid_params, birthdate='foo'))
|
||||||
>>> p.validate()
|
>>> p.validate()['birthdate']
|
||||||
{'birthdate': [u'Enter a valid date in YYYY-MM-DD format.']}
|
[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 = Person(**dict(valid_params, favorite_moment=datetime.datetime(2002, 4, 3, 13, 23)))
|
||||||
>>> p.validate()
|
>>> p.validate()
|
||||||
@ -143,11 +143,15 @@ datetime.datetime(2002, 4, 3, 0, 0)
|
|||||||
u'john@example.com'
|
u'john@example.com'
|
||||||
|
|
||||||
>>> p = Person(**dict(valid_params, email=22))
|
>>> p = Person(**dict(valid_params, email=22))
|
||||||
>>> p.validate()
|
>>> p.validate()['email']
|
||||||
{'email': [u'Enter a valid e-mail address.']}
|
[u'Enter a valid e-mail address.']
|
||||||
|
|
||||||
# Make sure that Date and DateTime return validation errors and don't raise Python errors.
|
# 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()
|
>>> p = Person(name='John Doe', is_child=True, email='abc@def.com')
|
||||||
{'favorite_moment': [u'This field is required.'], 'birthdate': [u'This field is required.']}
|
>>> errors = p.validate()
|
||||||
|
>>> errors['favorite_moment']
|
||||||
|
[u'This field is required.']
|
||||||
|
>>> errors['birthdate']
|
||||||
|
[u'This field is required.']
|
||||||
|
|
||||||
"""}
|
"""}
|
||||||
|
@ -22,7 +22,7 @@ classes that demonstrate some of the library's abilities.
|
|||||||
>>> from django.newforms.extras import SelectDateWidget
|
>>> from django.newforms.extras import SelectDateWidget
|
||||||
>>> w = SelectDateWidget(years=('2007','2008','2009','2010','2011','2012','2013','2014','2015','2016'))
|
>>> w = SelectDateWidget(years=('2007','2008','2009','2010','2011','2012','2013','2014','2015','2016'))
|
||||||
>>> print w.render('mydate', '')
|
>>> print w.render('mydate', '')
|
||||||
<select name="mydate_month">
|
<select name="mydate_month" id="id_mydate_month">
|
||||||
<option value="1">January</option>
|
<option value="1">January</option>
|
||||||
<option value="2">February</option>
|
<option value="2">February</option>
|
||||||
<option value="3">March</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="11">November</option>
|
||||||
<option value="12">December</option>
|
<option value="12">December</option>
|
||||||
</select>
|
</select>
|
||||||
<select name="mydate_day">
|
<select name="mydate_day" id="id_mydate_day">
|
||||||
<option value="1">1</option>
|
<option value="1">1</option>
|
||||||
<option value="2">2</option>
|
<option value="2">2</option>
|
||||||
<option value="3">3</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="30">30</option>
|
||||||
<option value="31">31</option>
|
<option value="31">31</option>
|
||||||
</select>
|
</select>
|
||||||
<select name="mydate_year">
|
<select name="mydate_year" id="id_mydate_year">
|
||||||
<option value="2007">2007</option>
|
<option value="2007">2007</option>
|
||||||
<option value="2008">2008</option>
|
<option value="2008">2008</option>
|
||||||
<option value="2009">2009</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', '')
|
>>> w.render('mydate', None) == w.render('mydate', '')
|
||||||
True
|
True
|
||||||
>>> print w.render('mydate', '2010-04-15')
|
>>> 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="1">January</option>
|
||||||
<option value="2">February</option>
|
<option value="2">February</option>
|
||||||
<option value="3">March</option>
|
<option value="3">March</option>
|
||||||
@ -98,7 +98,7 @@ True
|
|||||||
<option value="11">November</option>
|
<option value="11">November</option>
|
||||||
<option value="12">December</option>
|
<option value="12">December</option>
|
||||||
</select>
|
</select>
|
||||||
<select name="mydate_day">
|
<select name="mydate_day" id="id_mydate_day">
|
||||||
<option value="1">1</option>
|
<option value="1">1</option>
|
||||||
<option value="2">2</option>
|
<option value="2">2</option>
|
||||||
<option value="3">3</option>
|
<option value="3">3</option>
|
||||||
@ -131,7 +131,74 @@ True
|
|||||||
<option value="30">30</option>
|
<option value="30">30</option>
|
||||||
<option value="31">31</option>
|
<option value="31">31</option>
|
||||||
</select>
|
</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="2007">2007</option>
|
||||||
<option value="2008">2008</option>
|
<option value="2008">2008</option>
|
||||||
<option value="2009">2009</option>
|
<option value="2009">2009</option>
|
||||||
@ -252,8 +319,8 @@ ValidationError: [u'This field is required.']
|
|||||||
</select>
|
</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>
|
<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
|
>>> f.cleaned_data['field1']
|
||||||
{'field1': u'some text,JP,2007-04-25 06:24:00'}
|
u'some text,JP,2007-04-25 06:24:00'
|
||||||
|
|
||||||
|
|
||||||
# IPAddressField ##################################################################
|
# IPAddressField ##################################################################
|
||||||
|
@ -1133,6 +1133,33 @@ u''
|
|||||||
>>> f.clean(None)
|
>>> f.clean(None)
|
||||||
u''
|
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 ##########################################################
|
# SplitDateTimeField ##########################################################
|
||||||
|
|
||||||
>>> f = SplitDateTimeField()
|
>>> f = SplitDateTimeField()
|
||||||
|
@ -36,8 +36,8 @@ True
|
|||||||
u''
|
u''
|
||||||
>>> p.errors.as_text()
|
>>> p.errors.as_text()
|
||||||
u''
|
u''
|
||||||
>>> p.cleaned_data
|
>>> p.cleaned_data["first_name"], p.cleaned_data["last_name"], p.cleaned_data["birthday"]
|
||||||
{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)}
|
(u'John', u'Lennon', datetime.date(1940, 10, 9))
|
||||||
>>> print p['first_name']
|
>>> print p['first_name']
|
||||||
<input type="text" name="first_name" value="John" id="id_first_name" />
|
<input type="text" name="first_name" value="John" id="id_first_name" />
|
||||||
>>> print p['last_name']
|
>>> print p['last_name']
|
||||||
@ -68,8 +68,12 @@ Empty dictionaries are valid, too.
|
|||||||
>>> p = Person({})
|
>>> p = Person({})
|
||||||
>>> p.is_bound
|
>>> p.is_bound
|
||||||
True
|
True
|
||||||
>>> p.errors
|
>>> p.errors['first_name']
|
||||||
{'first_name': [u'This field is required.'], 'last_name': [u'This field is required.'], 'birthday': [u'This field is required.']}
|
[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()
|
>>> p.is_valid()
|
||||||
False
|
False
|
||||||
>>> p.cleaned_data
|
>>> 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>'
|
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 = Person({'last_name': u'Lennon'})
|
||||||
>>> p.errors
|
>>> p.errors['first_name']
|
||||||
{'first_name': [u'This field is required.'], 'birthday': [u'This field is required.']}
|
[u'This field is required.']
|
||||||
|
>>> p.errors['birthday']
|
||||||
|
[u'This field is required.']
|
||||||
>>> p.is_valid()
|
>>> p.is_valid()
|
||||||
False
|
False
|
||||||
>>> p.errors.as_ul()
|
>>> p.errors.as_ul()
|
||||||
@ -175,8 +181,13 @@ but cleaned_data contains only the form's fields.
|
|||||||
>>> p = Person(data)
|
>>> p = Person(data)
|
||||||
>>> p.is_valid()
|
>>> p.is_valid()
|
||||||
True
|
True
|
||||||
>>> p.cleaned_data
|
>>> p.cleaned_data['first_name']
|
||||||
{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)}
|
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,
|
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
|
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 = OptionalPersonForm(data)
|
||||||
>>> f.is_valid()
|
>>> f.is_valid()
|
||||||
True
|
True
|
||||||
>>> f.cleaned_data
|
>>> f.cleaned_data['nick_name']
|
||||||
{'nick_name': u'', 'first_name': u'John', 'last_name': u'Lennon'}
|
u''
|
||||||
|
>>> f.cleaned_data['first_name']
|
||||||
|
u'John'
|
||||||
|
>>> f.cleaned_data['last_name']
|
||||||
|
u'Lennon'
|
||||||
|
|
||||||
For DateFields, it's set to None.
|
For DateFields, it's set to None.
|
||||||
>>> class OptionalPersonForm(Form):
|
>>> class OptionalPersonForm(Form):
|
||||||
@ -203,8 +218,12 @@ For DateFields, it's set to None.
|
|||||||
>>> f = OptionalPersonForm(data)
|
>>> f = OptionalPersonForm(data)
|
||||||
>>> f.is_valid()
|
>>> f.is_valid()
|
||||||
True
|
True
|
||||||
>>> f.cleaned_data
|
>>> print f.cleaned_data['birth_date']
|
||||||
{'birth_date': None, 'first_name': u'John', 'last_name': u'Lennon'}
|
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.
|
"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
|
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
|
When using CheckboxSelectMultiple, the framework expects a list of input and
|
||||||
returns a list of input.
|
returns a list of input.
|
||||||
>>> f = SongForm({'name': 'Yesterday'}, auto_id=False)
|
>>> f = SongForm({'name': 'Yesterday'}, auto_id=False)
|
||||||
>>> f.errors
|
>>> f.errors['composers']
|
||||||
{'composers': [u'This field is required.']}
|
[u'This field is required.']
|
||||||
>>> f = SongForm({'name': 'Yesterday', 'composers': ['J']}, auto_id=False)
|
>>> f = SongForm({'name': 'Yesterday', 'composers': ['J']}, auto_id=False)
|
||||||
>>> f.errors
|
>>> f.errors
|
||||||
{}
|
{}
|
||||||
>>> f.cleaned_data
|
>>> f.cleaned_data['composers']
|
||||||
{'composers': [u'J'], 'name': u'Yesterday'}
|
[u'J']
|
||||||
|
>>> f.cleaned_data['name']
|
||||||
|
u'Yesterday'
|
||||||
>>> f = SongForm({'name': 'Yesterday', 'composers': ['J', 'P']}, auto_id=False)
|
>>> f = SongForm({'name': 'Yesterday', 'composers': ['J', 'P']}, auto_id=False)
|
||||||
>>> f.errors
|
>>> f.errors
|
||||||
{}
|
{}
|
||||||
>>> f.cleaned_data
|
>>> f.cleaned_data['composers']
|
||||||
{'composers': [u'J', u'P'], 'name': u'Yesterday'}
|
[u'J', u'P']
|
||||||
|
>>> f.cleaned_data['name']
|
||||||
|
u'Yesterday'
|
||||||
|
|
||||||
Validation errors are HTML-escaped when output as HTML.
|
Validation errors are HTML-escaped when output as HTML.
|
||||||
>>> class EscapingForm(Form):
|
>>> 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.errors
|
||||||
{}
|
{}
|
||||||
>>> f = UserRegistration({}, auto_id=False)
|
>>> f = UserRegistration({}, auto_id=False)
|
||||||
>>> f.errors
|
>>> f.errors['username']
|
||||||
{'username': [u'This field is required.'], 'password1': [u'This field is required.'], 'password2': [u'This field is required.']}
|
[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 = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'bar'}, auto_id=False)
|
||||||
>>> f.errors
|
>>> f.errors['password2']
|
||||||
{'password2': [u'Please make sure your passwords match.']}
|
[u'Please make sure your passwords match.']
|
||||||
>>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'}, auto_id=False)
|
>>> f = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'}, auto_id=False)
|
||||||
>>> f.errors
|
>>> f.errors
|
||||||
{}
|
{}
|
||||||
>>> f.cleaned_data
|
>>> f.cleaned_data['username']
|
||||||
{'username': u'adrian', 'password1': u'foo', 'password2': u'foo'}
|
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
|
Another way of doing multiple-field validation is by implementing the
|
||||||
Form's clean() method. If you do this, any ValidationError raised by that
|
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>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>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>
|
<tr><th>Password2:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="password" name="password2" /></td></tr>
|
||||||
>>> f.errors
|
>>> f.errors['username']
|
||||||
{'username': [u'This field is required.'], 'password1': [u'This field is required.'], 'password2': [u'This field is required.']}
|
[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 = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'bar'}, auto_id=False)
|
||||||
>>> f.errors
|
>>> f.errors['__all__']
|
||||||
{'__all__': [u'Please make sure your passwords match.']}
|
[u'Please make sure your passwords match.']
|
||||||
>>> print f.as_table()
|
>>> print f.as_table()
|
||||||
<tr><td colspan="2"><ul class="errorlist"><li>Please make sure your passwords match.</li></ul></td></tr>
|
<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>
|
<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 = UserRegistration({'username': 'adrian', 'password1': 'foo', 'password2': 'foo'}, auto_id=False)
|
||||||
>>> f.errors
|
>>> f.errors
|
||||||
{}
|
{}
|
||||||
>>> f.cleaned_data
|
>>> f.cleaned_data['username']
|
||||||
{'username': u'adrian', 'password1': u'foo', 'password2': u'foo'}
|
u'adrian'
|
||||||
|
>>> f.cleaned_data['password1']
|
||||||
|
u'foo'
|
||||||
|
>>> f.cleaned_data['password2']
|
||||||
|
u'foo'
|
||||||
|
|
||||||
# Dynamic construction ########################################################
|
# 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
|
example, we don't provide a value for 'username', and the form raises a
|
||||||
validation error rather than using the initial value for 'username'.
|
validation error rather than using the initial value for 'username'.
|
||||||
>>> p = UserRegistration({'password': 'secret'})
|
>>> p = UserRegistration({'password': 'secret'})
|
||||||
>>> p.errors
|
>>> p.errors['username']
|
||||||
{'username': [u'This field is required.']}
|
[u'This field is required.']
|
||||||
>>> p.is_valid()
|
>>> p.is_valid()
|
||||||
False
|
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
|
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'.
|
validation error rather than using the initial value for 'username'.
|
||||||
>>> p = UserRegistration({'password': 'secret'}, initial={'username': 'django'})
|
>>> p = UserRegistration({'password': 'secret'}, initial={'username': 'django'})
|
||||||
>>> p.errors
|
>>> p.errors['username']
|
||||||
{'username': [u'This field is required.']}
|
[u'This field is required.']
|
||||||
>>> p.is_valid()
|
>>> p.is_valid()
|
||||||
False
|
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
|
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'.
|
validation error rather than using the initial value for 'username'.
|
||||||
>>> p = UserRegistration({'password': 'secret'}, initial={'username': initial_django})
|
>>> p = UserRegistration({'password': 'secret'}, initial={'username': initial_django})
|
||||||
>>> p.errors
|
>>> p.errors['username']
|
||||||
{'username': [u'This field is required.']}
|
[u'This field is required.']
|
||||||
>>> p.is_valid()
|
>>> p.is_valid()
|
||||||
False
|
False
|
||||||
|
|
||||||
@ -1258,8 +1297,12 @@ actual field name.
|
|||||||
{}
|
{}
|
||||||
>>> p.is_valid()
|
>>> p.is_valid()
|
||||||
True
|
True
|
||||||
>>> p.cleaned_data
|
>>> p.cleaned_data['first_name']
|
||||||
{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)}
|
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
|
Let's try submitting some bad data to make sure form.errors and field.errors
|
||||||
work as expected.
|
work as expected.
|
||||||
@ -1269,8 +1312,12 @@ work as expected.
|
|||||||
... 'person1-birthday': u''
|
... 'person1-birthday': u''
|
||||||
... }
|
... }
|
||||||
>>> p = Person(data, prefix='person1')
|
>>> p = Person(data, prefix='person1')
|
||||||
>>> p.errors
|
>>> p.errors['first_name']
|
||||||
{'first_name': [u'This field is required.'], 'last_name': [u'This field is required.'], 'birthday': [u'This field is required.']}
|
[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
|
>>> p['first_name'].errors
|
||||||
[u'This field is required.']
|
[u'This field is required.']
|
||||||
>>> p['person1-first_name'].errors
|
>>> p['person1-first_name'].errors
|
||||||
@ -1286,8 +1333,12 @@ the form doesn't "see" the fields.
|
|||||||
... 'birthday': u'1940-10-9'
|
... 'birthday': u'1940-10-9'
|
||||||
... }
|
... }
|
||||||
>>> p = Person(data, prefix='person1')
|
>>> p = Person(data, prefix='person1')
|
||||||
>>> p.errors
|
>>> p.errors['first_name']
|
||||||
{'first_name': [u'This field is required.'], 'last_name': [u'This field is required.'], 'birthday': [u'This field is required.']}
|
[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
|
With prefixes, a single data dictionary can hold data for multiple instances
|
||||||
of the same form.
|
of the same form.
|
||||||
@ -1302,13 +1353,21 @@ of the same form.
|
|||||||
>>> p1 = Person(data, prefix='person1')
|
>>> p1 = Person(data, prefix='person1')
|
||||||
>>> p1.is_valid()
|
>>> p1.is_valid()
|
||||||
True
|
True
|
||||||
>>> p1.cleaned_data
|
>>> p1.cleaned_data['first_name']
|
||||||
{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)}
|
u'John'
|
||||||
|
>>> p1.cleaned_data['last_name']
|
||||||
|
u'Lennon'
|
||||||
|
>>> p1.cleaned_data['birthday']
|
||||||
|
datetime.date(1940, 10, 9)
|
||||||
>>> p2 = Person(data, prefix='person2')
|
>>> p2 = Person(data, prefix='person2')
|
||||||
>>> p2.is_valid()
|
>>> p2.is_valid()
|
||||||
True
|
True
|
||||||
>>> p2.cleaned_data
|
>>> p2.cleaned_data['first_name']
|
||||||
{'first_name': u'Jim', 'last_name': u'Morrison', 'birthday': datetime.date(1943, 12, 8)}
|
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
|
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
|
form can alter that behavior by implementing the add_prefix() method. This
|
||||||
@ -1333,8 +1392,12 @@ self.prefix.
|
|||||||
>>> p = Person(data, prefix='foo')
|
>>> p = Person(data, prefix='foo')
|
||||||
>>> p.is_valid()
|
>>> p.is_valid()
|
||||||
True
|
True
|
||||||
>>> p.cleaned_data
|
>>> p.cleaned_data['first_name']
|
||||||
{'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)}
|
u'John'
|
||||||
|
>>> p.cleaned_data['last_name']
|
||||||
|
u'Lennon'
|
||||||
|
>>> p.cleaned_data['birthday']
|
||||||
|
datetime.date(1940, 10, 9)
|
||||||
|
|
||||||
# Forms with NullBooleanFields ################################################
|
# Forms with NullBooleanFields ################################################
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
|
||||||
|
"""}
|
0
tests/regressiontests/syndication/__init__.py
Normal file
0
tests/regressiontests/syndication/__init__.py
Normal file
0
tests/regressiontests/syndication/models.py
Normal file
0
tests/regressiontests/syndication/models.py
Normal file
14
tests/regressiontests/syndication/tests.py
Normal file
14
tests/regressiontests/syndication/tests.py
Normal 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)
|
18
tests/regressiontests/syndication/urls.py
Normal file
18
tests/regressiontests/syndication/urls.py
Normal 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,
|
||||||
|
)}),
|
||||||
|
)
|
@ -3,7 +3,7 @@ Regression tests for the Test Client, especially the customized assertions.
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
from django.test import Client, TestCase
|
from django.test import Client, TestCase
|
||||||
from django.core import mail
|
from django.core.urlresolvers import reverse
|
||||||
import os
|
import os
|
||||||
|
|
||||||
class AssertContainsTests(TestCase):
|
class AssertContainsTests(TestCase):
|
||||||
@ -261,3 +261,31 @@ class LoginTests(TestCase):
|
|||||||
# Check that assertRedirects uses the original client, not the
|
# Check that assertRedirects uses the original client, not the
|
||||||
# default client.
|
# default client.
|
||||||
self.assertRedirects(response, "http://testserver/test_client_regress/get_view/")
|
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')
|
||||||
|
|
||||||
|
|
||||||
|
@ -5,5 +5,6 @@ urlpatterns = patterns('',
|
|||||||
(r'^no_template_view/$', views.no_template_view),
|
(r'^no_template_view/$', views.no_template_view),
|
||||||
(r'^file_upload/$', views.file_upload_view),
|
(r'^file_upload/$', views.file_upload_view),
|
||||||
(r'^get_view/$', views.get_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)
|
(r'^login_protected_redirect_view/$', views.login_protected_redirect_view)
|
||||||
)
|
)
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.core.mail import EmailMessage, SMTPConnection
|
|
||||||
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseServerError
|
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseServerError
|
||||||
from django.shortcuts import render_to_response
|
|
||||||
|
|
||||||
def no_template_view(request):
|
def no_template_view(request):
|
||||||
"A simple view that expects a GET request, and returns a rendered template"
|
"A simple view that expects a GET request, and returns a rendered template"
|
||||||
@ -20,10 +18,22 @@ def file_upload_view(request):
|
|||||||
return HttpResponseServerError()
|
return HttpResponseServerError()
|
||||||
|
|
||||||
def get_view(request):
|
def get_view(request):
|
||||||
"A simple login protected view"
|
"A simple login protected view"
|
||||||
return HttpResponse("Hello world")
|
return HttpResponse("Hello world")
|
||||||
get_view = login_required(get_view)
|
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):
|
def login_protected_redirect_view(request):
|
||||||
"A view that redirects all requests to the GET view"
|
"A view that redirects all requests to the GET view"
|
||||||
return HttpResponseRedirect('/test_client_regress/get_view/')
|
return HttpResponseRedirect('/test_client_regress/get_view/')
|
||||||
|
@ -19,4 +19,7 @@ urlpatterns = patterns('',
|
|||||||
(r'^middleware/', include('regressiontests.middleware.urls')),
|
(r'^middleware/', include('regressiontests.middleware.urls')),
|
||||||
|
|
||||||
(r'^utils/', include('regressiontests.utils.urls')),
|
(r'^utils/', include('regressiontests.utils.urls')),
|
||||||
|
|
||||||
|
# test urlconf for syndication tests
|
||||||
|
(r'^syndication/', include('regressiontests.syndication.urls')),
|
||||||
)
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user