mirror of
https://github.com/django/django.git
synced 2025-07-06 18:59:13 +00:00
queryset-refactor: Merged to [6190]
git-svn-id: http://code.djangoproject.com/svn/django/branches/queryset-refactor@6334 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
fb6a0c8ffa
commit
bf6a46d8ad
1
AUTHORS
1
AUTHORS
@ -246,6 +246,7 @@ answer newbie questions, and generally made Django that much better:
|
||||
Brian Ray <http://brianray.chipy.org/>
|
||||
remco@diji.biz
|
||||
rhettg@gmail.com
|
||||
Matt Riggott
|
||||
Henrique Romano <onaiort@gmail.com>
|
||||
Armin Ronacher
|
||||
Brian Rosner <brosner@gmail.com>
|
||||
|
Binary file not shown.
@ -14,7 +14,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: KBabel 1.11.4\n"
|
||||
"Plural-Forms: nplurals=2; nplurals=n>1;"
|
||||
"Plural-Forms: nplurals=2; plural=n>1;"
|
||||
|
||||
#: contrib/comments/models.py:67 contrib/comments/models.py:166
|
||||
msgid "object ID"
|
||||
|
@ -1,5 +1,9 @@
|
||||
// Core javascript helper functions
|
||||
|
||||
// basic browser identification & version
|
||||
var isOpera = (navigator.userAgent.indexOf("Opera")>=0) && parseFloat(navigator.appVersion);
|
||||
var isIE = ((document.all) && (!isOpera)) && parseFloat(navigator.appVersion.split("MSIE ")[1].split(";")[0]);
|
||||
|
||||
// Cross-browser event handlers.
|
||||
function addEvent(obj, evType, fn) {
|
||||
if (obj.addEventListener) {
|
||||
@ -71,9 +75,13 @@ function findPosX(obj) {
|
||||
var curleft = 0;
|
||||
if (obj.offsetParent) {
|
||||
while (obj.offsetParent) {
|
||||
curleft += obj.offsetLeft;
|
||||
curleft += obj.offsetLeft - ((isOpera) ? 0 : obj.scrollLeft);
|
||||
obj = obj.offsetParent;
|
||||
}
|
||||
// IE offsetParent does not include the top-level
|
||||
if (isIE && obj.parentElement){
|
||||
curleft += obj.offsetLeft - obj.scrollLeft;
|
||||
}
|
||||
} else if (obj.x) {
|
||||
curleft += obj.x;
|
||||
}
|
||||
@ -84,9 +92,13 @@ function findPosY(obj) {
|
||||
var curtop = 0;
|
||||
if (obj.offsetParent) {
|
||||
while (obj.offsetParent) {
|
||||
curtop += obj.offsetTop;
|
||||
curtop += obj.offsetTop - ((isOpera) ? 0 : obj.scrollTop);
|
||||
obj = obj.offsetParent;
|
||||
}
|
||||
// IE offsetParent does not include the top-level
|
||||
if (isIE && obj.parentElement){
|
||||
curtop += obj.offsetTop - obj.scrollTop;
|
||||
}
|
||||
} else if (obj.y) {
|
||||
curtop += obj.y;
|
||||
}
|
||||
|
@ -6,16 +6,21 @@ BR-specific Form helpers
|
||||
from django.newforms import ValidationError
|
||||
from django.newforms.fields import Field, RegexField, CharField, Select, EMPTY_VALUES
|
||||
from django.utils.encoding import smart_unicode
|
||||
from django.utils.translation import ugettext
|
||||
from django.utils.translation import ugettext as _
|
||||
import re
|
||||
|
||||
try:
|
||||
set
|
||||
except NameError:
|
||||
from sets import Set as set # For Python 2.3
|
||||
|
||||
phone_digits_re = re.compile(r'^(\d{2})[-\.]?(\d{4})[-\.]?(\d{4})$')
|
||||
|
||||
class BRZipCodeField(RegexField):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(BRZipCodeField, self).__init__(r'^\d{5}-\d{3}$',
|
||||
max_length=None, min_length=None,
|
||||
error_message=ugettext('Enter a zip code in the format XXXXX-XXX.'),
|
||||
error_message=_('Enter a zip code in the format XXXXX-XXX.'),
|
||||
*args, **kwargs)
|
||||
|
||||
class BRPhoneNumberField(Field):
|
||||
@ -27,7 +32,7 @@ class BRPhoneNumberField(Field):
|
||||
m = phone_digits_re.search(value)
|
||||
if m:
|
||||
return u'%s-%s-%s' % (m.group(1), m.group(2), m.group(3))
|
||||
raise ValidationError(ugettext('Phone numbers must be in XX-XXXX-XXXX format.'))
|
||||
raise ValidationError(_('Phone numbers must be in XX-XXXX-XXXX format.'))
|
||||
|
||||
class BRStateSelect(Select):
|
||||
"""
|
||||
@ -38,6 +43,32 @@ class BRStateSelect(Select):
|
||||
from br_states import STATE_CHOICES
|
||||
super(BRStateSelect, self).__init__(attrs, choices=STATE_CHOICES)
|
||||
|
||||
class BRStateChoiceField(Field):
|
||||
"""
|
||||
A choice field that uses a list of Brazilian states as its choices.
|
||||
"""
|
||||
widget = Select
|
||||
|
||||
def __init__(self, required=True, widget=None, label=None,
|
||||
initial=None, help_text=None):
|
||||
super(BRStateChoiceField, self).__init__(required, widget, label,
|
||||
initial, help_text)
|
||||
from br_states import STATE_CHOICES
|
||||
self.widget.choices = STATE_CHOICES
|
||||
|
||||
def clean(self, value):
|
||||
value = super(BRStateChoiceField, self).clean(value)
|
||||
if value in EMPTY_VALUES:
|
||||
value = u''
|
||||
value = smart_unicode(value)
|
||||
if value == u'':
|
||||
return value
|
||||
valid_values = set([smart_unicode(k) for k, v in self.widget.choices])
|
||||
if value not in valid_values:
|
||||
raise ValidationError(_(u'Select a valid brazilian state.'
|
||||
u' That state is not one'
|
||||
u' of the available states.'))
|
||||
return value
|
||||
|
||||
def DV_maker(v):
|
||||
if v >= 2:
|
||||
@ -69,9 +100,9 @@ class BRCPFField(CharField):
|
||||
try:
|
||||
int(value)
|
||||
except ValueError:
|
||||
raise ValidationError(ugettext("This field requires only numbers."))
|
||||
raise ValidationError(_("This field requires only numbers."))
|
||||
if len(value) != 11:
|
||||
raise ValidationError(ugettext("This field requires at most 11 digits or 14 characters."))
|
||||
raise ValidationError(_("This field requires at most 11 digits or 14 characters."))
|
||||
orig_dv = value[-2:]
|
||||
|
||||
new_1dv = sum([i * int(value[idx]) for idx, i in enumerate(range(10, 1, -1))])
|
||||
@ -81,7 +112,7 @@ class BRCPFField(CharField):
|
||||
new_2dv = DV_maker(new_2dv % 11)
|
||||
value = value[:-1] + str(new_2dv)
|
||||
if value[-2:] != orig_dv:
|
||||
raise ValidationError(ugettext("Invalid CPF number."))
|
||||
raise ValidationError(_("Invalid CPF number."))
|
||||
|
||||
return orig_value
|
||||
|
||||
@ -103,7 +134,7 @@ class BRCNPJField(Field):
|
||||
raise ValidationError("This field requires only numbers.")
|
||||
if len(value) != 14:
|
||||
raise ValidationError(
|
||||
ugettext("This field requires at least 14 digits"))
|
||||
_("This field requires at least 14 digits"))
|
||||
orig_dv = value[-2:]
|
||||
|
||||
new_1dv = sum([i * int(value[idx]) for idx, i in enumerate(range(5, 1, -1) + range(9, 1, -1))])
|
||||
@ -113,7 +144,7 @@ class BRCNPJField(Field):
|
||||
new_2dv = DV_maker(new_2dv % 11)
|
||||
value = value[:-1] + str(new_2dv)
|
||||
if value[-2:] != orig_dv:
|
||||
raise ValidationError(ugettext("Invalid CNPJ number."))
|
||||
raise ValidationError(_("Invalid CNPJ number."))
|
||||
|
||||
return orig_value
|
||||
|
||||
|
@ -1,15 +1,33 @@
|
||||
from django.db import models
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.http import get_host
|
||||
|
||||
SITE_CACHE = {}
|
||||
|
||||
class SiteManager(models.Manager):
|
||||
def get_current(self):
|
||||
"""
|
||||
Returns the current ``Site`` based on the SITE_ID in the
|
||||
project's settings. The ``Site`` object is cached the first
|
||||
time it's retrieved from the database.
|
||||
"""
|
||||
from django.conf import settings
|
||||
try:
|
||||
sid = settings.SITE_ID
|
||||
except AttributeError:
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
raise ImproperlyConfigured("You're using the Django \"sites framework\" without having set the SITE_ID setting. Create a site in your database and set the SITE_ID setting to fix this error.")
|
||||
return self.get(pk=sid)
|
||||
try:
|
||||
current_site = SITE_CACHE[sid]
|
||||
except KeyError:
|
||||
current_site = self.get(pk=sid)
|
||||
SITE_CACHE[sid] = current_site
|
||||
return current_site
|
||||
|
||||
def clear_cache(self):
|
||||
"""Clears the ``Site`` object cache."""
|
||||
global SITE_CACHE
|
||||
SITE_CACHE = {}
|
||||
|
||||
class Site(models.Model):
|
||||
domain = models.CharField(_('domain name'), max_length=100)
|
||||
@ -36,7 +54,7 @@ class RequestSite(object):
|
||||
The save() and delete() methods raise NotImplementedError.
|
||||
"""
|
||||
def __init__(self, request):
|
||||
self.domain = self.name = request.META['SERVER_NAME']
|
||||
self.domain = self.name = get_host(request)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.domain
|
||||
|
@ -1,11 +0,0 @@
|
||||
# This module is DEPRECATED!
|
||||
#
|
||||
# You should no longer be pointing your mod_python configuration
|
||||
# at "django.core.handler".
|
||||
#
|
||||
# Use "django.core.handlers.modpython" instead.
|
||||
|
||||
from django.core.handlers.modpython import ModPythonHandler
|
||||
|
||||
def handler(req):
|
||||
return ModPythonHandler()(req)
|
@ -50,6 +50,10 @@ class BaseHandler(object):
|
||||
|
||||
def get_response(self, request):
|
||||
"Returns an HttpResponse object for the given HttpRequest"
|
||||
response = self._real_get_response(request)
|
||||
return fix_location_header(request, response)
|
||||
|
||||
def _real_get_response(self, request):
|
||||
from django.core import exceptions, urlresolvers
|
||||
from django.core.mail import mail_admins
|
||||
from django.conf import settings
|
||||
@ -129,3 +133,16 @@ class BaseHandler(object):
|
||||
"Helper function to return the traceback as a string"
|
||||
import traceback
|
||||
return '\n'.join(traceback.format_exception(*(exc_info or sys.exc_info())))
|
||||
|
||||
def fix_location_header(request, response):
|
||||
"""
|
||||
Ensure that we always use an absolute URI in any location header in the
|
||||
response. This is required by RFC 2616, section 14.30.
|
||||
|
||||
Code constructing response objects is free to insert relative paths and
|
||||
this function converts them to absolute paths.
|
||||
"""
|
||||
if 'Location' in response.headers and http.get_host(request):
|
||||
response['Location'] = request.build_absolute_uri(response['Location'])
|
||||
return response
|
||||
|
||||
|
@ -181,10 +181,15 @@ def isValidImage(field_data, all_data):
|
||||
except TypeError:
|
||||
raise ValidationError, _("No file was submitted. Check the encoding type on the form.")
|
||||
try:
|
||||
Image.open(StringIO(content))
|
||||
except (IOError, OverflowError): # Python Imaging Library doesn't recognize it as an image
|
||||
# OverflowError is due to a bug in PIL with Python 2.4+ which can cause
|
||||
# it to gag on OLE files.
|
||||
# load() is the only method that can spot a truncated JPEG,
|
||||
# but it cannot be called sanely after verify()
|
||||
trial_image = Image.open(StringIO(content))
|
||||
trial_image.load()
|
||||
# verify() is the only method that can spot a corrupt PNG,
|
||||
# but it must be called immediately after the constructor
|
||||
trial_image = Image.open(StringIO(content))
|
||||
trial_image.verify()
|
||||
except Exception: # Python Imaging Library doesn't recognize it as an image
|
||||
raise ValidationError, _("Upload a valid image. The file you uploaded was either not an image or a corrupted image.")
|
||||
|
||||
def isValidImageURL(field_data, all_data):
|
||||
|
@ -241,10 +241,12 @@ class Model(object):
|
||||
placeholders = ['%s'] * len(field_names)
|
||||
if self._meta.order_with_respect_to:
|
||||
field_names.append(qn('_order'))
|
||||
# TODO: This assumes the database supports subqueries.
|
||||
placeholders.append('(SELECT COUNT(*) FROM %s WHERE %s = %%s)' % \
|
||||
(qn(self._meta.db_table), qn(self._meta.order_with_respect_to.column)))
|
||||
db_values.append(getattr(self, self._meta.order_with_respect_to.attname))
|
||||
placeholders.append('%s')
|
||||
subsel = 'SELECT COUNT(*) FROM %s WHERE %s = %%s' % (
|
||||
qn(self._meta.db_table),
|
||||
qn(self._meta.order_with_respect_to.column))
|
||||
cursor.execute(subsel, (getattr(self, self._meta.order_with_respect_to.attname),))
|
||||
db_values.append(cursor.fetchone()[0])
|
||||
if db_values:
|
||||
cursor.execute("INSERT INTO %s (%s) VALUES (%s)" % \
|
||||
(qn(self._meta.db_table), ','.join(field_names),
|
||||
|
@ -2,6 +2,7 @@ import os
|
||||
from Cookie import SimpleCookie
|
||||
from pprint import pformat
|
||||
from urllib import urlencode
|
||||
from urlparse import urljoin
|
||||
from django.utils.datastructures import MultiValueDict, FileDict
|
||||
from django.utils.encoding import smart_str, iri_to_uri, force_unicode
|
||||
|
||||
@ -46,6 +47,20 @@ class HttpRequest(object):
|
||||
def get_full_path(self):
|
||||
return ''
|
||||
|
||||
def build_absolute_uri(self, location=None):
|
||||
"""
|
||||
Builds an absolute URI from the location and the variables available in
|
||||
this request. If no location is specified, the absolute URI is built on
|
||||
``request.get_full_path()``.
|
||||
"""
|
||||
if not location:
|
||||
location = self.get_full_path()
|
||||
if not ':' in location:
|
||||
current_uri = '%s://%s%s' % (self.is_secure() and 'https' or 'http',
|
||||
get_host(self), self.path)
|
||||
location = urljoin(current_uri, location)
|
||||
return location
|
||||
|
||||
def is_secure(self):
|
||||
return os.environ.get("HTTPS") == "on"
|
||||
|
||||
@ -364,9 +379,16 @@ class HttpResponseServerError(HttpResponse):
|
||||
|
||||
def get_host(request):
|
||||
"Gets the HTTP host from the environment or request headers."
|
||||
# We try three options, in order of decreasing preference.
|
||||
host = request.META.get('HTTP_X_FORWARDED_HOST', '')
|
||||
if not host:
|
||||
host = request.META.get('HTTP_HOST', '')
|
||||
if 'HTTP_HOST' in request.META:
|
||||
host = request.META['HTTP_HOST']
|
||||
else:
|
||||
# Reconstruct the host using the algorithm from PEP 333.
|
||||
host = request.META['SERVER_NAME']
|
||||
server_port = request.META['SERVER_PORT']
|
||||
if server_port != (request.is_secure() and 443 or 80):
|
||||
host = '%s:%s' % (host, server_port)
|
||||
return host
|
||||
|
||||
# It's neither necessary nor appropriate to use
|
||||
|
@ -2,6 +2,7 @@
|
||||
Field classes
|
||||
"""
|
||||
|
||||
import copy
|
||||
import datetime
|
||||
import re
|
||||
import time
|
||||
@ -100,6 +101,12 @@ class Field(object):
|
||||
"""
|
||||
return {}
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
result = copy.copy(self)
|
||||
memo[id(self)] = result
|
||||
result.widget = copy.deepcopy(self.widget, memo)
|
||||
return result
|
||||
|
||||
class CharField(Field):
|
||||
def __init__(self, max_length=None, min_length=None, *args, **kwargs):
|
||||
self.max_length, self.min_length = max_length, min_length
|
||||
@ -386,10 +393,15 @@ class ImageField(FileField):
|
||||
from PIL import Image
|
||||
from cStringIO import StringIO
|
||||
try:
|
||||
Image.open(StringIO(f.content))
|
||||
except (IOError, OverflowError): # Python Imaging Library doesn't recognize it as an image
|
||||
# OverflowError is due to a bug in PIL with Python 2.4+ which can cause
|
||||
# it to gag on OLE files.
|
||||
# load() is the only method that can spot a truncated JPEG,
|
||||
# but it cannot be called sanely after verify()
|
||||
trial_image = Image.open(StringIO(f.content))
|
||||
trial_image.load()
|
||||
# verify() is the only method that can spot a corrupt PNG,
|
||||
# but it must be called immediately after the constructor
|
||||
trial_image = Image.open(StringIO(f.content))
|
||||
trial_image.verify()
|
||||
except Exception: # Python Imaging Library doesn't recognize it as an image
|
||||
raise ValidationError(ugettext(u"Upload a valid image. The file you uploaded was either not an image or a corrupted image."))
|
||||
return f
|
||||
|
||||
@ -409,6 +421,9 @@ class URLField(RegexField):
|
||||
self.user_agent = validator_user_agent
|
||||
|
||||
def clean(self, value):
|
||||
# If no URL scheme given, assume http://
|
||||
if value and '://' not in value:
|
||||
value = u'http://%s' % value
|
||||
value = super(URLField, self).clean(value)
|
||||
if value == u'':
|
||||
return value
|
||||
|
@ -31,7 +31,7 @@ class SortedDictFromList(SortedDict):
|
||||
dict.__init__(self, dict(data))
|
||||
|
||||
def copy(self):
|
||||
return SortedDictFromList([(k, copy.copy(v)) for k, v in self.items()])
|
||||
return SortedDictFromList([(k, copy.deepcopy(v)) for k, v in self.items()])
|
||||
|
||||
class DeclarativeFieldsMetaclass(type):
|
||||
"""
|
||||
|
@ -84,12 +84,8 @@ class TestCase(unittest.TestCase):
|
||||
self.assertEqual(response.status_code, status_code,
|
||||
("Response didn't redirect as expected: Response code was %d"
|
||||
" (expected %d)" % (response.status_code, status_code)))
|
||||
scheme, netloc, path, query, fragment = urlsplit(response['Location'])
|
||||
url = path
|
||||
if query:
|
||||
url += '?' + query
|
||||
if fragment:
|
||||
url += '#' + fragment
|
||||
url = response['Location']
|
||||
scheme, netloc, path, query, fragment = urlsplit(url)
|
||||
self.assertEqual(url, expected_url,
|
||||
"Response redirected to '%s', expected '%s'" % (url, expected_url))
|
||||
|
||||
|
@ -10,7 +10,7 @@ from django.http import Http404, HttpResponse
|
||||
def archive_index(request, queryset, date_field, num_latest=15,
|
||||
template_name=None, template_loader=loader,
|
||||
extra_context=None, allow_empty=False, context_processors=None,
|
||||
mimetype=None, allow_future=False):
|
||||
mimetype=None, allow_future=False, template_object_name='latest'):
|
||||
"""
|
||||
Generic top-level archive of date-based objects.
|
||||
|
||||
@ -39,7 +39,7 @@ def archive_index(request, queryset, date_field, num_latest=15,
|
||||
t = template_loader.get_template(template_name)
|
||||
c = RequestContext(request, {
|
||||
'date_list' : date_list,
|
||||
'latest' : latest,
|
||||
template_object_name : latest,
|
||||
}, context_processors)
|
||||
for key, value in extra_context.items():
|
||||
if callable(value):
|
||||
|
@ -9,15 +9,21 @@ def set_language(request):
|
||||
"""
|
||||
Redirect to a given url while setting the chosen language in the
|
||||
session or cookie. The url and the language code need to be
|
||||
specified in the GET parameters.
|
||||
specified in the request parameters.
|
||||
|
||||
Since this view changes how the user will see the rest of the site, it must
|
||||
only be accessed as a POST request. If called as a GET request, it will
|
||||
redirect to the page in the request (the 'next' parameter) without changing
|
||||
any state.
|
||||
"""
|
||||
lang_code = request.GET.get('language', None)
|
||||
next = request.GET.get('next', None)
|
||||
if not next:
|
||||
next = request.META.get('HTTP_REFERER', None)
|
||||
if not next:
|
||||
next = '/'
|
||||
response = http.HttpResponseRedirect(next)
|
||||
if request.method == 'POST':
|
||||
lang_code = request.POST.get('language', None)
|
||||
if lang_code and check_for_language(lang_code):
|
||||
if hasattr(request, 'session'):
|
||||
request.session['django_language'] = lang_code
|
||||
|
@ -799,6 +799,9 @@ of the arguments is required, but you should use at least one of them.
|
||||
|
||||
Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
|
||||
|
||||
The combined number of placeholders in the list of strings for ``select``
|
||||
or ``where`` should equal the number of values in the ``params`` list.
|
||||
|
||||
QuerySet methods that do not return QuerySets
|
||||
---------------------------------------------
|
||||
|
||||
|
@ -201,6 +201,10 @@ a date in the *future* are not included unless you set ``allow_future`` to
|
||||
specified in ``date_field`` is greater than the current date/time. By
|
||||
default, this is ``False``.
|
||||
|
||||
* **New in Django development version:** ``template_object_name``:
|
||||
Designates the name of the template variable to use in the template
|
||||
context. By default, this is ``'latest'``.
|
||||
|
||||
**Template name:**
|
||||
|
||||
If ``template_name`` isn't specified, this view will use the template
|
||||
@ -221,10 +225,16 @@ In addition to ``extra_context``, the template's context will be:
|
||||
years that have objects available according to ``queryset``. These are
|
||||
ordered in reverse. This is equivalent to
|
||||
``queryset.dates(date_field, 'year')[::-1]``.
|
||||
|
||||
* ``latest``: The ``num_latest`` objects in the system, ordered descending
|
||||
by ``date_field``. For example, if ``num_latest`` is ``10``, then
|
||||
``latest`` will be a list of the latest 10 objects in ``queryset``.
|
||||
|
||||
**New in Django development version:** This variable's name depends on
|
||||
the ``template_object_name`` parameter, which is ``'latest'`` by default.
|
||||
If ``template_object_name`` is ``'foo'``, this variable's name will be
|
||||
``foo``.
|
||||
|
||||
.. _RequestContext docs: ../templates_python/#subclassing-context-requestcontext
|
||||
|
||||
``django.views.generic.date_based.archive_year``
|
||||
@ -764,8 +774,8 @@ If the results are paginated, the context will contain these extra variables:
|
||||
* ``hits``: The total number of objects across *all* pages, not just this
|
||||
page.
|
||||
|
||||
* ``page_range``: A list of the page numbers that are available. This
|
||||
is 1-based.
|
||||
* **New in Django development version:** ``page_range``: A list of the
|
||||
page numbers that are available. This is 1-based.
|
||||
|
||||
Notes on pagination
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
@ -788,7 +798,11 @@ specify the page number in the URL in one of two ways:
|
||||
to create a link to every page of results.
|
||||
|
||||
These values and lists are is 1-based, not 0-based, so the first page would be
|
||||
represented as page ``1``. As a special case, you are also permitted to use
|
||||
represented as page ``1``.
|
||||
|
||||
**New in Django development version:**
|
||||
|
||||
As a special case, you are also permitted to use
|
||||
``last`` as a value for ``page``::
|
||||
|
||||
/objects/?page=last
|
||||
|
@ -127,16 +127,24 @@ Installing an official release
|
||||
|
||||
1. Download the latest release from our `download page`_.
|
||||
|
||||
2. Untar the downloaded file (e.g. ``tar xzvf Django-NNN.tar.gz``).
|
||||
2. Untar the downloaded file (e.g. ``tar xzvf Django-NNN.tar.gz``,
|
||||
where ``NNN`` is the version number of the latest release).
|
||||
If you're using Windows, you can download the command-line tool
|
||||
bsdtar_ to do this, or you can use a GUI-based tool such as 7-zip_.
|
||||
|
||||
3. Change into the downloaded directory (e.g. ``cd Django-NNN``).
|
||||
3. Change into the directory created in step 2 (e.g. ``cd Django-NNN``).
|
||||
|
||||
4. Run ``sudo python setup.py install``.
|
||||
4. If you're using Linux, Mac OS X or some other flavor of Unix, enter
|
||||
the command ``sudo python setup.py install`` at the shell prompt.
|
||||
If you're using Windows, start up a command shell with administrator
|
||||
privileges and run the command ``setup.py install``.
|
||||
|
||||
The command will install Django in your Python installation's ``site-packages``
|
||||
directory.
|
||||
These commands will install Django in your Python installation's
|
||||
``site-packages`` directory.
|
||||
|
||||
.. _distribution specific notes: ../distributions/
|
||||
.. _bsdtar: http://gnuwin32.sourceforge.net/packages/bsdtar.htm
|
||||
.. _7-zip: http://www.7-zip.org/
|
||||
|
||||
Installing the development version
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@ -144,34 +152,55 @@ Installing the development version
|
||||
If you'd like to be able to update your Django code occasionally with the
|
||||
latest bug fixes and improvements, follow these instructions:
|
||||
|
||||
1. Make sure you have Subversion_ installed.
|
||||
2. Check out the Django code into your Python ``site-packages`` directory.
|
||||
1. Make sure that you have Subversion_ installed, and that you can run its
|
||||
commands from a shell. (Enter ``svn help`` at a shell prompt to test
|
||||
this.)
|
||||
|
||||
On Linux / Mac OSX / Unix, do this::
|
||||
2. Check out Django's main development branch (the 'trunk') like so::
|
||||
|
||||
svn co http://code.djangoproject.com/svn/django/trunk/ django_src
|
||||
ln -s `pwd`/django_src/django SITE-PACKAGES-DIR/django
|
||||
svn co http://code.djangoproject.com/svn/django/trunk/ django-trunk
|
||||
|
||||
3. Next, make sure that the Python interpreter can load Django's code. There
|
||||
are various ways of accomplishing this. One of the most convenient, on
|
||||
Linux, Mac OSX or other Unix-like systems, is to use a symbolic link::
|
||||
|
||||
ln -s `pwd`/django-trunk/django SITE-PACKAGES-DIR/django
|
||||
|
||||
(In the above line, change ``SITE-PACKAGES-DIR`` to match the location of
|
||||
your system's ``site-packages`` directory, as explained in the
|
||||
"Where are my ``site-packages`` stored?" section above.)
|
||||
|
||||
On Windows, do this::
|
||||
Alternatively, you can define your ``PYTHONPATH`` environment variable
|
||||
so that it includes the ``django`` subdirectory of ``django-trunk``.
|
||||
This is perhaps the most convenient solution on Windows systems, which
|
||||
don't support symbolic links. (Environment variables can be defined on
|
||||
Windows systems `from the Control Panel`_.)
|
||||
|
||||
svn co http://code.djangoproject.com/svn/django/trunk/django c:\Python24\lib\site-packages\django
|
||||
.. admonition:: What about Apache and mod_python?
|
||||
|
||||
3. Copy the file ``django_src/django/bin/django-admin.py`` to somewhere on your
|
||||
system path, such as ``/usr/local/bin`` (Unix) or ``C:\Python24\Scripts``
|
||||
If you take the approach of setting ``PYTHONPATH``, you'll need to
|
||||
remember to do the same thing in your Apache configuration once you
|
||||
deploy your production site. Do this by setting ``PythonPath`` in your
|
||||
Apache configuration file.
|
||||
|
||||
More information about deployment is available, of course, in our
|
||||
`How to use Django with mod_python`_ documentation.
|
||||
|
||||
.. _How to use Django with mod_python: ../modpython/
|
||||
|
||||
4. Copy the file ``django-trunk/django/bin/django-admin.py`` to somewhere on
|
||||
your system path, such as ``/usr/local/bin`` (Unix) or ``C:\Python24\Scripts``
|
||||
(Windows). This step simply lets you type ``django-admin.py`` from within
|
||||
any directory, rather than having to qualify the command with the full path
|
||||
to the file.
|
||||
|
||||
You *don't* have to run ``python setup.py install``, because that command
|
||||
takes care of steps 2 and 3 for you.
|
||||
You *don't* have to run ``python setup.py install``, because you've already
|
||||
carried out the equivalent actions in steps 3 and 4.
|
||||
|
||||
When you want to update your copy of the Django source code, just run the
|
||||
command ``svn update`` from within the ``django`` directory. When you do this,
|
||||
Subversion will automatically download any changes.
|
||||
command ``svn update`` from within the ``django-trunk`` directory. When you do
|
||||
this, Subversion will automatically download any changes.
|
||||
|
||||
.. _`download page`: http://www.djangoproject.com/download/
|
||||
.. _Subversion: http://subversion.tigris.org/
|
||||
.. _from the Control Panel: http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/sysdm_advancd_environmnt_addchange_variable.mspx
|
||||
|
@ -1550,8 +1550,8 @@ Finally, note that in order to use ``list_display_links``, you must define
|
||||
|
||||
Set ``list_filter`` to activate filters in the right sidebar of the change list
|
||||
page of the admin. This should be a list of field names, and each specified
|
||||
field should be either a ``BooleanField``, ``DateField``, ``DateTimeField``
|
||||
or ``ForeignKey``.
|
||||
field should be either a ``BooleanField``, ``CharField``, ``DateField``,
|
||||
``DateTimeField``, ``IntegerField`` or ``ForeignKey``.
|
||||
|
||||
This example, taken from the ``django.contrib.auth.models.User`` model, shows
|
||||
how both ``list_display`` and ``list_filter`` work::
|
||||
|
@ -83,7 +83,7 @@ need to write your ``PythonPath`` directive as::
|
||||
With this path, ``import weblog`` and ``import mysite.settings`` will both
|
||||
work. If you had ``import blogroll`` in your code somewhere and ``blogroll``
|
||||
lived under the ``weblog/`` directory, you would *also* need to add
|
||||
``/var/production/django-apps/weblog/`` to your ``PythonPath``. Remember: the
|
||||
``/usr/local/django-apps/weblog/`` to your ``PythonPath``. Remember: the
|
||||
**parent directories** of anything you import directly must be on the Python
|
||||
path.
|
||||
|
||||
|
@ -161,6 +161,18 @@ Methods
|
||||
|
||||
Example: ``"/music/bands/the_beatles/?print=true"``
|
||||
|
||||
``build_absolute_uri(location)``
|
||||
**New in Django development version**
|
||||
|
||||
Returns the absolute URI form of ``location``. If no location is provided,
|
||||
the location will be set to ``request.get_full_path()``.
|
||||
|
||||
If the location is already an absolute URI, it will not be altered.
|
||||
Otherwise the absolute URI is built using the server variables available in
|
||||
this request.
|
||||
|
||||
Example: ``"http://example.com/music/bands/the_beatles/?print=true"``
|
||||
|
||||
``is_secure()``
|
||||
Returns ``True`` if the request is secure; that is, if it was made with
|
||||
HTTPS.
|
||||
@ -184,8 +196,8 @@ subclass of dictionary. Exceptions are outlined here:
|
||||
* ``__getitem__(key)`` -- Returns the value for the given key. If the key
|
||||
has more than one value, ``__getitem__()`` returns the last value.
|
||||
Raises ``django.utils.datastructure.MultiValueDictKeyError`` if the key
|
||||
does not exist (fortunately, this is a subclass of Python's standard
|
||||
``KeyError``, so you can stick to catching ``KeyError``).
|
||||
does not exist. (This is a subclass of Python's standard ``KeyError``,
|
||||
so you can stick to catching ``KeyError``.)
|
||||
|
||||
* ``__setitem__(key, value)`` -- Sets the given key to ``[value]``
|
||||
(a Python list whose single element is ``value``). Note that this, as
|
||||
|
@ -213,6 +213,31 @@ To do this, you can use the sites framework. A simple example::
|
||||
>>> 'http://%s%s' % (Site.objects.get_current().domain, obj.get_absolute_url())
|
||||
'http://example.com/mymodel/objects/3/'
|
||||
|
||||
Caching the current ``Site`` object
|
||||
===================================
|
||||
|
||||
**New in Django development version**
|
||||
|
||||
As the current site is stored in the database, each call to
|
||||
``Site.objects.get_current()`` could result in a database query. But Django is a
|
||||
little cleverer than that: on the first request, the current site is cached, and
|
||||
any subsequent call returns the cached data instead of hitting the database.
|
||||
|
||||
If for any reason you want to force a database query, you can tell Django to
|
||||
clear the cache using ``Site.objects.clear_cache()``::
|
||||
|
||||
# First call; current site fetched from database.
|
||||
current_site = Site.objects.get_current()
|
||||
# ...
|
||||
|
||||
# Second call; current site fetched from cache.
|
||||
current_site = Site.objects.get_current()
|
||||
# ...
|
||||
|
||||
# Force a database query for the third call.
|
||||
Site.objects.clear_cache()
|
||||
current_site = Site.objects.get_current()
|
||||
|
||||
The ``CurrentSiteManager``
|
||||
==========================
|
||||
|
||||
|
@ -1431,4 +1431,4 @@ Read the document `The Django template language: For Python programmers`_ if
|
||||
you're interested in learning the template system from a technical
|
||||
perspective -- how it works and how to extend it.
|
||||
|
||||
.. _The Django template language: For Python programmers: ../templates_python/
|
||||
.. _The Django template language\: For Python programmers: ../templates_python/
|
||||
|
@ -642,12 +642,12 @@ your function. Example::
|
||||
"Converts a string into all lowercase"
|
||||
return value.lower()
|
||||
|
||||
Template filters which expect strings
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Template filters that expect strings
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If you're writing a template filter which only expects a string as the first
|
||||
argument, you should use the included decorator ``stringfilter``. This will
|
||||
convert an object to it's string value before being passed to your function::
|
||||
If you're writing a template filter that only expects a string as the first
|
||||
argument, you should use the decorator ``stringfilter``. This will
|
||||
convert an object to its string value before being passed to your function::
|
||||
|
||||
from django.template.defaultfilters import stringfilter
|
||||
|
||||
@ -655,6 +655,10 @@ convert an object to it's string value before being passed to your function::
|
||||
def lower(value):
|
||||
return value.lower()
|
||||
|
||||
This way, you'll be able to pass, say, an integer to this filter, and it
|
||||
won't cause an ``AttributeError`` (because integers don't have ``lower()``
|
||||
methods).
|
||||
|
||||
Registering a custom filters
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -83,23 +83,31 @@ class ClientTest(TestCase):
|
||||
def test_redirect(self):
|
||||
"GET a URL that redirects elsewhere"
|
||||
response = self.client.get('/test_client/redirect_view/')
|
||||
|
||||
# Check that the response was a 302 (redirect)
|
||||
self.assertRedirects(response, '/test_client/get_view/')
|
||||
self.assertRedirects(response, 'http://testserver/test_client/get_view/')
|
||||
|
||||
client_providing_host = Client(HTTP_HOST='django.testserver')
|
||||
response = client_providing_host.get('/test_client/redirect_view/')
|
||||
# Check that the response was a 302 (redirect) with absolute URI
|
||||
self.assertRedirects(response, 'http://django.testserver/test_client/get_view/')
|
||||
|
||||
def test_redirect_with_query(self):
|
||||
"GET a URL that redirects with given GET parameters"
|
||||
response = self.client.get('/test_client/redirect_view/', {'var': 'value'})
|
||||
|
||||
# Check if parameters are intact
|
||||
self.assertRedirects(response, '/test_client/get_view/?var=value')
|
||||
self.assertRedirects(response, 'http://testserver/test_client/get_view/?var=value')
|
||||
|
||||
def test_permanent_redirect(self):
|
||||
"GET a URL that redirects permanently elsewhere"
|
||||
response = self.client.get('/test_client/permanent_redirect_view/')
|
||||
|
||||
# Check that the response was a 301 (permanent redirect)
|
||||
self.assertRedirects(response, '/test_client/get_view/', status_code=301)
|
||||
self.assertRedirects(response, 'http://testserver/test_client/get_view/', status_code=301)
|
||||
|
||||
client_providing_host = Client(HTTP_HOST='django.testserver')
|
||||
response = client_providing_host.get('/test_client/permanent_redirect_view/')
|
||||
# Check that the response was a 301 (permanent redirect) with absolute URI
|
||||
self.assertRedirects(response, 'http://django.testserver/test_client/get_view/', status_code=301)
|
||||
|
||||
def test_redirect_to_strange_location(self):
|
||||
"GET a URL that redirects to a non-200 page"
|
||||
@ -107,7 +115,7 @@ class ClientTest(TestCase):
|
||||
|
||||
# Check that the response was a 302, and that
|
||||
# the attempt to get the redirection location returned 301 when retrieved
|
||||
self.assertRedirects(response, '/test_client/permanent_redirect_view/', target_status_code=301)
|
||||
self.assertRedirects(response, 'http://testserver/test_client/permanent_redirect_view/', target_status_code=301)
|
||||
|
||||
def test_notfound_response(self):
|
||||
"GET a URL that responds as '404:Not Found'"
|
||||
@ -231,11 +239,11 @@ class ClientTest(TestCase):
|
||||
|
||||
# Get the page without logging in. Should result in 302.
|
||||
response = self.client.get('/test_client/login_protected_view/')
|
||||
self.assertRedirects(response, '/accounts/login/?next=/test_client/login_protected_view/')
|
||||
self.assertRedirects(response, 'http://testserver/accounts/login/?next=/test_client/login_protected_view/')
|
||||
|
||||
# Log in
|
||||
login = self.client.login(username='testclient', password='password')
|
||||
self.assertTrue(login, 'Could not log in')
|
||||
self.failUnless(login, 'Could not log in')
|
||||
|
||||
# Request a page that requires a login
|
||||
response = self.client.get('/test_client/login_protected_view/')
|
||||
@ -269,7 +277,7 @@ class ClientTest(TestCase):
|
||||
|
||||
# Request a page that requires a login
|
||||
response = self.client.get('/test_client/login_protected_view/')
|
||||
self.assertRedirects(response, '/accounts/login/?next=/test_client/login_protected_view/')
|
||||
self.assertRedirects(response, 'http://testserver/accounts/login/?next=/test_client/login_protected_view/')
|
||||
|
||||
def test_session_modifying_view(self):
|
||||
"Request a page that modifies the session"
|
||||
|
@ -980,6 +980,20 @@ u'41-3562-3464'
|
||||
>>> w.render('states', 'PR')
|
||||
u'<select name="states">\n<option value="AC">Acre</option>\n<option value="AL">Alagoas</option>\n<option value="AP">Amap\xe1</option>\n<option value="AM">Amazonas</option>\n<option value="BA">Bahia</option>\n<option value="CE">Cear\xe1</option>\n<option value="DF">Distrito Federal</option>\n<option value="ES">Esp\xedrito Santo</option>\n<option value="GO">Goi\xe1s</option>\n<option value="MA">Maranh\xe3o</option>\n<option value="MT">Mato Grosso</option>\n<option value="MS">Mato Grosso do Sul</option>\n<option value="MG">Minas Gerais</option>\n<option value="PA">Par\xe1</option>\n<option value="PB">Para\xedba</option>\n<option value="PR" selected="selected">Paran\xe1</option>\n<option value="PE">Pernambuco</option>\n<option value="PI">Piau\xed</option>\n<option value="RJ">Rio de Janeiro</option>\n<option value="RN">Rio Grande do Norte</option>\n<option value="RS">Rio Grande do Sul</option>\n<option value="RO">Rond\xf4nia</option>\n<option value="RR">Roraima</option>\n<option value="SC">Santa Catarina</option>\n<option value="SP">S\xe3o Paulo</option>\n<option value="SE">Sergipe</option>\n<option value="TO">Tocantins</option>\n</select>'
|
||||
|
||||
# BRStateChoiceField #########################################################
|
||||
>>> from django.contrib.localflavor.br.forms import BRStateChoiceField
|
||||
>>> f = BRStateChoiceField()
|
||||
>>> ', '.join([f.clean(s) for s, _ in f.widget.choices])
|
||||
u'AC, AL, AP, AM, BA, CE, DF, ES, GO, MA, MT, MS, MG, PA, PB, PR, PE, PI, RJ, RN, RS, RO, RR, SC, SP, SE, TO'
|
||||
>>> f.clean('')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'This field is required.']
|
||||
>>> f.clean('pr')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Select a valid brazilian state. That state is not one of the available states.']
|
||||
|
||||
# DEZipCodeField ##############################################################
|
||||
|
||||
>>> from django.contrib.localflavor.de.forms import DEZipCodeField
|
||||
|
@ -1623,10 +1623,6 @@ u'http://200.8.9.10:8000/test'
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Enter a valid URL.']
|
||||
>>> f.clean('example.com')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Enter a valid URL.']
|
||||
>>> f.clean('http://')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
@ -1657,10 +1653,6 @@ u'http://www.example.com'
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Enter a valid URL.']
|
||||
>>> f.clean('example.com')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Enter a valid URL.']
|
||||
>>> f.clean('http://')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
@ -1714,6 +1706,15 @@ Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Ensure this value has at most 20 characters (it has 37).']
|
||||
|
||||
URLField should prepend 'http://' if no scheme was given
|
||||
>>> f = URLField(required=False)
|
||||
>>> f.clean('example.com')
|
||||
u'http://example.com'
|
||||
>>> f.clean('')
|
||||
u''
|
||||
>>> f.clean('https://example.com')
|
||||
u'https://example.com'
|
||||
|
||||
# BooleanField ################################################################
|
||||
|
||||
>>> f = BooleanField()
|
||||
@ -2690,16 +2691,24 @@ to the next.
|
||||
... super(Person, self).__init__(*args, **kwargs)
|
||||
... if names_required:
|
||||
... self.fields['first_name'].required = True
|
||||
... self.fields['first_name'].widget.attrs['class'] = 'required'
|
||||
... self.fields['last_name'].required = True
|
||||
... self.fields['last_name'].widget.attrs['class'] = 'required'
|
||||
>>> f = Person(names_required=False)
|
||||
>>> f['first_name'].field.required, f['last_name'].field.required
|
||||
(False, False)
|
||||
>>> f['first_name'].field.widget.attrs, f['last_name'].field.widget.attrs
|
||||
({}, {})
|
||||
>>> f = Person(names_required=True)
|
||||
>>> f['first_name'].field.required, f['last_name'].field.required
|
||||
(True, True)
|
||||
>>> f['first_name'].field.widget.attrs, f['last_name'].field.widget.attrs
|
||||
({'class': 'required'}, {'class': 'required'})
|
||||
>>> f = Person(names_required=False)
|
||||
>>> f['first_name'].field.required, f['last_name'].field.required
|
||||
(False, False)
|
||||
>>> f['first_name'].field.widget.attrs, f['last_name'].field.widget.attrs
|
||||
({}, {})
|
||||
>>> class Person(Form):
|
||||
... first_name = CharField(max_length=30)
|
||||
... last_name = CharField(max_length=30)
|
||||
|
@ -119,7 +119,7 @@ class AssertRedirectsTests(TestCase):
|
||||
try:
|
||||
self.assertRedirects(response, '/test_client/get_view/')
|
||||
except AssertionError, e:
|
||||
self.assertEquals(str(e), "Response redirected to '/test_client/get_view/?var=value', expected '/test_client/get_view/'")
|
||||
self.assertEquals(str(e), "Response redirected to 'http://testserver/test_client/get_view/?var=value', expected '/test_client/get_view/'")
|
||||
|
||||
def test_incorrect_target(self):
|
||||
"An assertion is raised if the response redirects to another target"
|
||||
@ -135,7 +135,7 @@ class AssertRedirectsTests(TestCase):
|
||||
response = self.client.get('/test_client/double_redirect_view/')
|
||||
try:
|
||||
# The redirect target responds with a 301 code, not 200
|
||||
self.assertRedirects(response, '/test_client/permanent_redirect_view/')
|
||||
self.assertRedirects(response, 'http://testserver/test_client/permanent_redirect_view/')
|
||||
except AssertionError, e:
|
||||
self.assertEquals(str(e), "Couldn't retrieve redirection page '/test_client/permanent_redirect_view/': response code was 301 (expected 200)")
|
||||
|
||||
@ -252,7 +252,7 @@ class LoginTests(TestCase):
|
||||
# Create a second client, and log in.
|
||||
c = Client()
|
||||
login = c.login(username='testclient', password='password')
|
||||
self.assertTrue(login, 'Could not log in')
|
||||
self.failUnless(login, 'Could not log in')
|
||||
|
||||
# Get a redirection page with the second client.
|
||||
response = c.get("/test_client_regress/login_protected_redirect_view/")
|
||||
@ -260,4 +260,4 @@ class LoginTests(TestCase):
|
||||
# At this points, the self.client isn't logged in.
|
||||
# Check that assertRedirects uses the original client, not the
|
||||
# default client.
|
||||
self.assertRedirects(response, "/test_client_regress/get_view/")
|
||||
self.assertRedirects(response, "http://testserver/test_client_regress/get_view/")
|
||||
|
@ -4,11 +4,17 @@ import os, sys, traceback
|
||||
import unittest
|
||||
|
||||
import django.contrib as contrib
|
||||
|
||||
try:
|
||||
set
|
||||
except NameError:
|
||||
from sets import Set as set # For Python 2.3
|
||||
|
||||
|
||||
CONTRIB_DIR_NAME = 'django.contrib'
|
||||
MODEL_TESTS_DIR_NAME = 'modeltests'
|
||||
REGRESSION_TESTS_DIR_NAME = 'regressiontests'
|
||||
|
||||
TEST_DATABASE_NAME = 'django_test_db'
|
||||
TEST_TEMPLATE_DIR = 'templates'
|
||||
|
||||
CONTRIB_DIR = os.path.dirname(contrib.__file__)
|
||||
@ -90,7 +96,6 @@ def django_tests(verbosity, interactive, test_labels):
|
||||
old_middleware_classes = settings.MIDDLEWARE_CLASSES
|
||||
|
||||
# Redirect some settings for the duration of these tests.
|
||||
settings.TEST_DATABASE_NAME = TEST_DATABASE_NAME
|
||||
settings.INSTALLED_APPS = ALWAYS_INSTALLED_APPS
|
||||
settings.ROOT_URLCONF = 'urls'
|
||||
settings.TEMPLATE_DIRS = (os.path.join(os.path.dirname(__file__), TEST_TEMPLATE_DIR),)
|
||||
@ -143,7 +148,6 @@ def django_tests(verbosity, interactive, test_labels):
|
||||
|
||||
# Restore the old settings.
|
||||
settings.INSTALLED_APPS = old_installed_apps
|
||||
settings.TESTS_DATABASE_NAME = old_test_database_name
|
||||
settings.ROOT_URLCONF = old_root_urlconf
|
||||
settings.TEMPLATE_DIRS = old_template_dirs
|
||||
settings.USE_I18N = old_use_i18n
|
||||
|
Loading…
x
Reference in New Issue
Block a user