mirror of
https://github.com/django/django.git
synced 2025-07-04 17:59:13 +00:00
[multi-db] Merge trunk to [3812]. Some tests still failing.
git-svn-id: http://code.djangoproject.com/svn/django/branches/multiple-db-support@4139 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
f6d48b5d02
commit
71012a4be3
7
AUTHORS
7
AUTHORS
@ -42,6 +42,7 @@ And here is an inevitably incomplete list of MUCH-APPRECIATED CONTRIBUTORS --
|
|||||||
people who have submitted patches, reported bugs, added translations, helped
|
people who have submitted patches, reported bugs, added translations, helped
|
||||||
answer newbie questions, and generally made Django that much better:
|
answer newbie questions, and generally made Django that much better:
|
||||||
|
|
||||||
|
adurdin@gmail.com
|
||||||
akaihola
|
akaihola
|
||||||
Andreas
|
Andreas
|
||||||
ant9000@netwise.it
|
ant9000@netwise.it
|
||||||
@ -68,15 +69,19 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
Alex Dedul
|
Alex Dedul
|
||||||
deric@monowerks.com
|
deric@monowerks.com
|
||||||
dne@mayonnaise.net
|
dne@mayonnaise.net
|
||||||
|
Maximillian Dornseif <md@hudora.de>
|
||||||
|
dummy@habmalnefrage.de
|
||||||
Jeremy Dunck <http://dunck.us/>
|
Jeremy Dunck <http://dunck.us/>
|
||||||
Andy Dustman <farcepest@gmail.com>
|
Andy Dustman <farcepest@gmail.com>
|
||||||
Clint Ecker
|
Clint Ecker
|
||||||
|
favo@exoweb.net
|
||||||
gandalf@owca.info
|
gandalf@owca.info
|
||||||
Baishampayan Ghose
|
Baishampayan Ghose
|
||||||
martin.glueck@gmail.com
|
martin.glueck@gmail.com
|
||||||
Simon Greenhill <dev@simon.net.nz>
|
Simon Greenhill <dev@simon.net.nz>
|
||||||
Espen Grindhaug <http://grindhaug.org/>
|
Espen Grindhaug <http://grindhaug.org/>
|
||||||
Brant Harris
|
Brant Harris
|
||||||
|
heckj@mac.com
|
||||||
hipertracker@gmail.com
|
hipertracker@gmail.com
|
||||||
Ian Holsman <http://feh.holsman.net/>
|
Ian Holsman <http://feh.holsman.net/>
|
||||||
Kieran Holland <http://www.kieranholland.com>
|
Kieran Holland <http://www.kieranholland.com>
|
||||||
@ -96,6 +101,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
lakin.wecker@gmail.com
|
lakin.wecker@gmail.com
|
||||||
Stuart Langridge <http://www.kryogenix.org/>
|
Stuart Langridge <http://www.kryogenix.org/>
|
||||||
Eugene Lazutkin <http://lazutkin.com/blog/>
|
Eugene Lazutkin <http://lazutkin.com/blog/>
|
||||||
|
Jeong-Min Lee
|
||||||
Christopher Lenz <http://www.cmlenz.net/>
|
Christopher Lenz <http://www.cmlenz.net/>
|
||||||
limodou
|
limodou
|
||||||
Martin Maney <http://www.chipy.org/Martin_Maney>
|
Martin Maney <http://www.chipy.org/Martin_Maney>
|
||||||
@ -122,6 +128,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
Daniel Poelzleithner <http://poelzi.org/>
|
Daniel Poelzleithner <http://poelzi.org/>
|
||||||
J. Rademaker
|
J. Rademaker
|
||||||
Michael Radziej <mir@noris.de>
|
Michael Radziej <mir@noris.de>
|
||||||
|
ramiro
|
||||||
Brian Ray <http://brianray.chipy.org/>
|
Brian Ray <http://brianray.chipy.org/>
|
||||||
rhettg@gmail.com
|
rhettg@gmail.com
|
||||||
Oliver Rutherfurd <http://rutherfurd.net/>
|
Oliver Rutherfurd <http://rutherfurd.net/>
|
||||||
|
4
README
4
README
@ -25,10 +25,10 @@ http://code.djangoproject.com/newticket
|
|||||||
To get more help:
|
To get more help:
|
||||||
|
|
||||||
* Join the #django channel on irc.freenode.net. Lots of helpful people
|
* Join the #django channel on irc.freenode.net. Lots of helpful people
|
||||||
hang out there. Read the archives at http://loglibrary.com/179 .
|
hang out there. Read the archives at http://simon.bofh.ms/logger/django/ .
|
||||||
|
|
||||||
* Join the django-users mailing list, or read the archives, at
|
* Join the django-users mailing list, or read the archives, at
|
||||||
http://groups-beta.google.com/group/django-users.
|
http://groups.google.com/group/django-users.
|
||||||
|
|
||||||
To contribute to Django:
|
To contribute to Django:
|
||||||
|
|
||||||
|
@ -275,6 +275,10 @@ CACHE_MIDDLEWARE_KEY_PREFIX = ''
|
|||||||
|
|
||||||
COMMENTS_ALLOW_PROFANITIES = False
|
COMMENTS_ALLOW_PROFANITIES = False
|
||||||
|
|
||||||
|
# The profanities that will trigger a validation error in the
|
||||||
|
# 'hasNoProfanities' validator. All of these should be in lower-case.
|
||||||
|
PROFANITIES_LIST = ['asshat', 'asshead', 'asshole', 'cunt', 'fuck', 'gook', 'nigger', 'shit']
|
||||||
|
|
||||||
# The group ID that designates which users are banned.
|
# The group ID that designates which users are banned.
|
||||||
# Set to None if you're not using it.
|
# Set to None if you're not using it.
|
||||||
COMMENTS_BANNED_USERS_GROUP = None
|
COMMENTS_BANNED_USERS_GROUP = None
|
||||||
|
@ -160,8 +160,10 @@ class EditInlineNode(template.Node):
|
|||||||
context.push()
|
context.push()
|
||||||
if relation.field.rel.edit_inline == models.TABULAR:
|
if relation.field.rel.edit_inline == models.TABULAR:
|
||||||
bound_related_object_class = TabularBoundRelatedObject
|
bound_related_object_class = TabularBoundRelatedObject
|
||||||
else:
|
elif relation.field.rel.edit_inline == models.STACKED:
|
||||||
bound_related_object_class = StackedBoundRelatedObject
|
bound_related_object_class = StackedBoundRelatedObject
|
||||||
|
else:
|
||||||
|
bound_related_object_class = relation.field.rel.edit_inline
|
||||||
original = context.get('original', None)
|
original = context.get('original', None)
|
||||||
bound_related_object = relation.bind(context['form'], original, bound_related_object_class)
|
bound_related_object = relation.bind(context['form'], original, bound_related_object_class)
|
||||||
context['bound_related_object'] = bound_related_object
|
context['bound_related_object'] = bound_related_object
|
||||||
|
@ -727,6 +727,8 @@ class ChangeList(object):
|
|||||||
for bit in self.query.split():
|
for bit in self.query.split():
|
||||||
or_queries = [models.Q(**{construct_search(field_name): bit}) for field_name in self.lookup_opts.admin.search_fields]
|
or_queries = [models.Q(**{construct_search(field_name): bit}) for field_name in self.lookup_opts.admin.search_fields]
|
||||||
other_qs = QuerySet(self.model)
|
other_qs = QuerySet(self.model)
|
||||||
|
if qs._select_related:
|
||||||
|
other_qs = other_qs.select_related()
|
||||||
other_qs = other_qs.filter(reduce(operator.or_, or_queries))
|
other_qs = other_qs.filter(reduce(operator.or_, or_queries))
|
||||||
qs = qs & other_qs
|
qs = qs & other_qs
|
||||||
|
|
||||||
|
@ -26,3 +26,11 @@ login_required.__doc__ = (
|
|||||||
to the log-in page if necessary.
|
to the log-in page if necessary.
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def permission_required(perm, login_url=LOGIN_URL):
|
||||||
|
"""
|
||||||
|
Decorator for views that checks if a user has a particular permission
|
||||||
|
enabled, redirectiing to the log-in page if necessary.
|
||||||
|
"""
|
||||||
|
return user_passes_test(lambda u: u.has_perm(perm), login_url=login_url)
|
||||||
|
|
||||||
|
@ -22,6 +22,8 @@ def authenhandler(req, **kwargs):
|
|||||||
os.environ['DJANGO_SETTINGS_MODULE'] = settings_module
|
os.environ['DJANGO_SETTINGS_MODULE'] = settings_module
|
||||||
|
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
from django import db
|
||||||
|
db.reset_queries()
|
||||||
|
|
||||||
# check that the username is valid
|
# check that the username is valid
|
||||||
kwargs = {'username': req.user, 'is_active': True}
|
kwargs = {'username': req.user, 'is_active': True}
|
||||||
@ -30,18 +32,21 @@ def authenhandler(req, **kwargs):
|
|||||||
if superuser_only:
|
if superuser_only:
|
||||||
kwargs['is_superuser'] = True
|
kwargs['is_superuser'] = True
|
||||||
try:
|
try:
|
||||||
user = User.objects.get(**kwargs)
|
try:
|
||||||
except User.DoesNotExist:
|
user = User.objects.get(**kwargs)
|
||||||
return apache.HTTP_UNAUTHORIZED
|
except User.DoesNotExist:
|
||||||
|
return apache.HTTP_UNAUTHORIZED
|
||||||
|
|
||||||
# check the password and any permission given
|
# check the password and any permission given
|
||||||
if user.check_password(req.get_basic_auth_pw()):
|
if user.check_password(req.get_basic_auth_pw()):
|
||||||
if permission_name:
|
if permission_name:
|
||||||
if user.has_perm(permission_name):
|
if user.has_perm(permission_name):
|
||||||
return apache.OK
|
return apache.OK
|
||||||
|
else:
|
||||||
|
return apache.HTTP_UNAUTHORIZED
|
||||||
else:
|
else:
|
||||||
return apache.HTTP_UNAUTHORIZED
|
return apache.OK
|
||||||
else:
|
else:
|
||||||
return apache.OK
|
return apache.HTTP_UNAUTHORIZED
|
||||||
else:
|
finally:
|
||||||
return apache.HTTP_UNAUTHORIZED
|
db.connection.close()
|
||||||
|
@ -51,15 +51,19 @@ def request(request):
|
|||||||
class PermLookupDict(object):
|
class PermLookupDict(object):
|
||||||
def __init__(self, user, module_name):
|
def __init__(self, user, module_name):
|
||||||
self.user, self.module_name = user, module_name
|
self.user, self.module_name = user, module_name
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return str(self.user.get_permission_list())
|
return str(self.user.get_all_permissions())
|
||||||
|
|
||||||
def __getitem__(self, perm_name):
|
def __getitem__(self, perm_name):
|
||||||
return self.user.has_perm("%s.%s" % (self.module_name, perm_name))
|
return self.user.has_perm("%s.%s" % (self.module_name, perm_name))
|
||||||
|
|
||||||
def __nonzero__(self):
|
def __nonzero__(self):
|
||||||
return self.user.has_module_perms(self.module_name)
|
return self.user.has_module_perms(self.module_name)
|
||||||
|
|
||||||
class PermWrapper(object):
|
class PermWrapper(object):
|
||||||
def __init__(self, user):
|
def __init__(self, user):
|
||||||
self.user = user
|
self.user = user
|
||||||
|
|
||||||
def __getitem__(self, module_name):
|
def __getitem__(self, module_name):
|
||||||
return PermLookupDict(self.user, module_name)
|
return PermLookupDict(self.user, module_name)
|
||||||
|
@ -155,8 +155,11 @@ def populate_apache_request(http_response, mod_python_req):
|
|||||||
for c in http_response.cookies.values():
|
for c in http_response.cookies.values():
|
||||||
mod_python_req.headers_out.add('Set-Cookie', c.output(header=''))
|
mod_python_req.headers_out.add('Set-Cookie', c.output(header=''))
|
||||||
mod_python_req.status = http_response.status_code
|
mod_python_req.status = http_response.status_code
|
||||||
for chunk in http_response.iterator:
|
try:
|
||||||
mod_python_req.write(chunk)
|
for chunk in http_response:
|
||||||
|
mod_python_req.write(chunk)
|
||||||
|
finally:
|
||||||
|
http_response.close()
|
||||||
|
|
||||||
def handler(req):
|
def handler(req):
|
||||||
# mod_python hooks into this function.
|
# mod_python hooks into this function.
|
||||||
|
@ -4,6 +4,11 @@ from django.dispatch import dispatcher
|
|||||||
from django.utils import datastructures
|
from django.utils import datastructures
|
||||||
from django import http
|
from django import http
|
||||||
from pprint import pformat
|
from pprint import pformat
|
||||||
|
from shutil import copyfileobj
|
||||||
|
try:
|
||||||
|
from cStringIO import StringIO
|
||||||
|
except ImportError:
|
||||||
|
from StringIO import StringIO
|
||||||
|
|
||||||
# See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
|
# See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
|
||||||
STATUS_CODE_TEXT = {
|
STATUS_CODE_TEXT = {
|
||||||
@ -50,6 +55,21 @@ STATUS_CODE_TEXT = {
|
|||||||
505: 'HTTP VERSION NOT SUPPORTED',
|
505: 'HTTP VERSION NOT SUPPORTED',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def safe_copyfileobj(fsrc, fdst, length=16*1024, size=0):
|
||||||
|
"""
|
||||||
|
A version of shutil.copyfileobj that will not read more than 'size' bytes.
|
||||||
|
This makes it safe from clients sending more than CONTENT_LENGTH bytes of
|
||||||
|
data in the body.
|
||||||
|
"""
|
||||||
|
if not size:
|
||||||
|
return copyfileobj(fsrc, fdst, length)
|
||||||
|
while size > 0:
|
||||||
|
buf = fsrc.read(min(length, size))
|
||||||
|
if not buf:
|
||||||
|
break
|
||||||
|
fdst.write(buf)
|
||||||
|
size -= len(buf)
|
||||||
|
|
||||||
class WSGIRequest(http.HttpRequest):
|
class WSGIRequest(http.HttpRequest):
|
||||||
def __init__(self, environ):
|
def __init__(self, environ):
|
||||||
self.environ = environ
|
self.environ = environ
|
||||||
@ -119,7 +139,11 @@ class WSGIRequest(http.HttpRequest):
|
|||||||
try:
|
try:
|
||||||
return self._raw_post_data
|
return self._raw_post_data
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
self._raw_post_data = self.environ['wsgi.input'].read(int(self.environ["CONTENT_LENGTH"]))
|
buf = StringIO()
|
||||||
|
content_length = int(self.environ['CONTENT_LENGTH'])
|
||||||
|
safe_copyfileobj(self.environ['wsgi.input'], buf, size=content_length)
|
||||||
|
self._raw_post_data = buf.getvalue()
|
||||||
|
buf.close()
|
||||||
return self._raw_post_data
|
return self._raw_post_data
|
||||||
|
|
||||||
GET = property(_get_get, _set_get)
|
GET = property(_get_get, _set_get)
|
||||||
@ -163,4 +187,4 @@ class WSGIHandler(BaseHandler):
|
|||||||
for c in response.cookies.values():
|
for c in response.cookies.values():
|
||||||
response_headers.append(('Set-Cookie', c.output(header='')))
|
response_headers.append(('Set-Cookie', c.output(header='')))
|
||||||
start_response(status, response_headers)
|
start_response(status, response_headers)
|
||||||
return response.iterator
|
return response
|
||||||
|
@ -812,7 +812,8 @@ def get_validation_errors(outfile, app=None):
|
|||||||
try:
|
try:
|
||||||
f = opts.get_field(fn)
|
f = opts.get_field(fn)
|
||||||
except models.FieldDoesNotExist:
|
except models.FieldDoesNotExist:
|
||||||
e.add(opts, '"admin.list_filter" refers to %r, which isn\'t a field.' % fn)
|
if not hasattr(cls, fn):
|
||||||
|
e.add(opts, '"admin.list_display_links" refers to %r, which isn\'t an attribute, method or property.' % fn)
|
||||||
if fn not in opts.admin.list_display:
|
if fn not in opts.admin.list_display:
|
||||||
e.add(opts, '"admin.list_display_links" refers to %r, which is not defined in "admin.list_display".' % fn)
|
e.add(opts, '"admin.list_display_links" refers to %r, which is not defined in "admin.list_display".' % fn)
|
||||||
# list_filter
|
# list_filter
|
||||||
@ -870,10 +871,12 @@ def get_validation_errors(outfile, app=None):
|
|||||||
|
|
||||||
return len(e.errors)
|
return len(e.errors)
|
||||||
|
|
||||||
def validate(outfile=sys.stdout):
|
def validate(outfile=sys.stdout, silent_success=False):
|
||||||
"Validates all installed models."
|
"Validates all installed models."
|
||||||
try:
|
try:
|
||||||
num_errors = get_validation_errors(outfile)
|
num_errors = get_validation_errors(outfile)
|
||||||
|
if silent_success and num_errors == 0:
|
||||||
|
return
|
||||||
outfile.write('%s error%s found.\n' % (num_errors, num_errors != 1 and 's' or ''))
|
outfile.write('%s error%s found.\n' % (num_errors, num_errors != 1 and 's' or ''))
|
||||||
except ImproperlyConfigured:
|
except ImproperlyConfigured:
|
||||||
outfile.write("Skipping validation because things aren't configured properly.")
|
outfile.write("Skipping validation because things aren't configured properly.")
|
||||||
@ -896,7 +899,7 @@ def _check_for_validation_errors(app=None):
|
|||||||
sys.stderr.write(s.read())
|
sys.stderr.write(s.read())
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
def runserver(addr, port, use_reloader=True):
|
def runserver(addr, port, use_reloader=True, admin_media_dir=''):
|
||||||
"Starts a lightweight Web server for development."
|
"Starts a lightweight Web server for development."
|
||||||
from django.core.servers.basehttp import run, AdminMediaHandler, WSGIServerException
|
from django.core.servers.basehttp import run, AdminMediaHandler, WSGIServerException
|
||||||
from django.core.handlers.wsgi import WSGIHandler
|
from django.core.handlers.wsgi import WSGIHandler
|
||||||
@ -914,7 +917,10 @@ def runserver(addr, port, use_reloader=True):
|
|||||||
print "Development server is running at http://%s:%s/" % (addr, port)
|
print "Development server is running at http://%s:%s/" % (addr, port)
|
||||||
print "Quit the server with %s." % quit_command
|
print "Quit the server with %s." % quit_command
|
||||||
try:
|
try:
|
||||||
run(addr, int(port), AdminMediaHandler(WSGIHandler()))
|
import django
|
||||||
|
path = admin_media_dir or django.__path__[0] + '/contrib/admin/media'
|
||||||
|
handler = AdminMediaHandler(WSGIHandler(), path)
|
||||||
|
run(addr, int(port), handler)
|
||||||
except WSGIServerException, e:
|
except WSGIServerException, e:
|
||||||
# Use helpful error messages instead of ugly tracebacks.
|
# Use helpful error messages instead of ugly tracebacks.
|
||||||
ERRORS = {
|
ERRORS = {
|
||||||
@ -935,7 +941,7 @@ def runserver(addr, port, use_reloader=True):
|
|||||||
autoreload.main(inner_run)
|
autoreload.main(inner_run)
|
||||||
else:
|
else:
|
||||||
inner_run()
|
inner_run()
|
||||||
runserver.args = '[--noreload] [optional port number, or ipaddr:port]'
|
runserver.args = '[--noreload] [--adminmedia=ADMIN_MEDIA_PATH] [optional port number, or ipaddr:port]'
|
||||||
|
|
||||||
def createcachetable(tablename):
|
def createcachetable(tablename):
|
||||||
"Creates the table needed to use the SQL cache backend"
|
"Creates the table needed to use the SQL cache backend"
|
||||||
@ -1121,7 +1127,8 @@ def execute_from_command_line(action_mapping=DEFAULT_ACTION_MAPPING, argv=None):
|
|||||||
help='Tells Django to NOT use the auto-reloader when running the development server.')
|
help='Tells Django to NOT use the auto-reloader when running the development server.')
|
||||||
parser.add_option('--verbosity', action='store', dest='verbosity', default='2',
|
parser.add_option('--verbosity', action='store', dest='verbosity', default='2',
|
||||||
type='choice', choices=['0', '1', '2'],
|
type='choice', choices=['0', '1', '2'],
|
||||||
help='Verbosity level; 0=minimal output, 1=normal output, 2=all output')
|
help='Verbosity level; 0=minimal output, 1=normal output, 2=all output'),
|
||||||
|
parser.add_option('--adminmedia', dest='admin_media_path', default='', help='Lets you manually specify the directory to serve admin media from when running the development server.'),
|
||||||
|
|
||||||
options, args = parser.parse_args(argv[1:])
|
options, args = parser.parse_args(argv[1:])
|
||||||
|
|
||||||
@ -1185,11 +1192,12 @@ def execute_from_command_line(action_mapping=DEFAULT_ACTION_MAPPING, argv=None):
|
|||||||
addr, port = args[1].split(':')
|
addr, port = args[1].split(':')
|
||||||
except ValueError:
|
except ValueError:
|
||||||
addr, port = '', args[1]
|
addr, port = '', args[1]
|
||||||
action_mapping[action](addr, port, options.use_reloader)
|
action_mapping[action](addr, port, options.use_reloader, options.admin_media_path)
|
||||||
elif action == 'runfcgi':
|
elif action == 'runfcgi':
|
||||||
action_mapping[action](args[1:])
|
action_mapping[action](args[1:])
|
||||||
else:
|
else:
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
validate(silent_success=True)
|
||||||
try:
|
try:
|
||||||
mod_list = [models.get_app(app_label) for app_label in args[1:]]
|
mod_list = [models.get_app(app_label) for app_label in args[1:]]
|
||||||
except ImportError, e:
|
except ImportError, e:
|
||||||
|
@ -16,7 +16,7 @@ class Serializer(PythonSerializer):
|
|||||||
Convert a queryset to JSON.
|
Convert a queryset to JSON.
|
||||||
"""
|
"""
|
||||||
def end_serialization(self):
|
def end_serialization(self):
|
||||||
simplejson.dump(self.objects, self.stream, cls=DateTimeAwareJSONEncoder)
|
simplejson.dump(self.objects, self.stream, cls=DateTimeAwareJSONEncoder, **self.options)
|
||||||
|
|
||||||
def getvalue(self):
|
def getvalue(self):
|
||||||
return self.stream.getvalue()
|
return self.stream.getvalue()
|
||||||
|
@ -594,11 +594,14 @@ class AdminMediaHandler(object):
|
|||||||
Use this ONLY LOCALLY, for development! This hasn't been tested for
|
Use this ONLY LOCALLY, for development! This hasn't been tested for
|
||||||
security and is not super efficient.
|
security and is not super efficient.
|
||||||
"""
|
"""
|
||||||
def __init__(self, application):
|
def __init__(self, application, media_dir = None):
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
import django
|
|
||||||
self.application = application
|
self.application = application
|
||||||
self.media_dir = django.__path__[0] + '/contrib/admin/media'
|
if not media_dir:
|
||||||
|
import django
|
||||||
|
self.media_dir = django.__path__[0] + '/contrib/admin/media'
|
||||||
|
else:
|
||||||
|
self.media_dir = media_dir
|
||||||
self.media_url = settings.ADMIN_MEDIA_PREFIX
|
self.media_url = settings.ADMIN_MEDIA_PREFIX
|
||||||
|
|
||||||
def __call__(self, environ, start_response):
|
def __call__(self, environ, start_response):
|
||||||
|
@ -74,7 +74,7 @@ def fastcgi_help(message=None):
|
|||||||
print message
|
print message
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def runfastcgi(argset, **kwargs):
|
def runfastcgi(argset=[], **kwargs):
|
||||||
options = FASTCGI_OPTIONS.copy()
|
options = FASTCGI_OPTIONS.copy()
|
||||||
options.update(kwargs)
|
options.update(kwargs)
|
||||||
for x in argset:
|
for x in argset:
|
||||||
|
@ -227,9 +227,8 @@ def hasNoProfanities(field_data, all_data):
|
|||||||
catch 'motherfucker' as well. Raises a ValidationError such as:
|
catch 'motherfucker' as well. Raises a ValidationError such as:
|
||||||
Watch your mouth! The words "f--k" and "s--t" are not allowed here.
|
Watch your mouth! The words "f--k" and "s--t" are not allowed here.
|
||||||
"""
|
"""
|
||||||
bad_words = ['asshat', 'asshead', 'asshole', 'cunt', 'fuck', 'gook', 'nigger', 'shit'] # all in lower case
|
|
||||||
field_data = field_data.lower() # normalize
|
field_data = field_data.lower() # normalize
|
||||||
words_seen = [w for w in bad_words if field_data.find(w) > -1]
|
words_seen = [w for w in settings.PROFANITIES_LIST if field_data.find(w) > -1]
|
||||||
if words_seen:
|
if words_seen:
|
||||||
from django.utils.text import get_text_list
|
from django.utils.text import get_text_list
|
||||||
plural = len(words_seen) > 1
|
plural = len(words_seen) > 1
|
||||||
@ -352,10 +351,12 @@ class IsValidFloat(object):
|
|||||||
float(data)
|
float(data)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise ValidationError, gettext("Please enter a valid decimal number.")
|
raise ValidationError, gettext("Please enter a valid decimal number.")
|
||||||
if len(data) > (self.max_digits + 1):
|
# Negative floats require more space to input.
|
||||||
|
max_allowed_length = data.startswith('-') and (self.max_digits + 2) or (self.max_digits + 1)
|
||||||
|
if len(data) > max_allowed_length:
|
||||||
raise ValidationError, ngettext("Please enter a valid decimal number with at most %s total digit.",
|
raise ValidationError, ngettext("Please enter a valid decimal number with at most %s total digit.",
|
||||||
"Please enter a valid decimal number with at most %s total digits.", self.max_digits) % self.max_digits
|
"Please enter a valid decimal number with at most %s total digits.", self.max_digits) % self.max_digits
|
||||||
if (not '.' in data and len(data) > (self.max_digits - self.decimal_places)) or ('.' in data and len(data) > (self.max_digits - (self.decimal_places - len(data.split('.')[1])) + 1)):
|
if (not '.' in data and len(data) > (max_allowed_length - self.decimal_places)) or ('.' in data and len(data) > (self.max_digits - (self.decimal_places - len(data.split('.')[1])) + 1)):
|
||||||
raise ValidationError, ngettext( "Please enter a valid decimal number with a whole part of at most %s digit.",
|
raise ValidationError, ngettext( "Please enter a valid decimal number with a whole part of at most %s digit.",
|
||||||
"Please enter a valid decimal number with a whole part of at most %s digits.", str(self.max_digits-self.decimal_places)) % str(self.max_digits-self.decimal_places)
|
"Please enter a valid decimal number with a whole part of at most %s digits.", str(self.max_digits-self.decimal_places)) % str(self.max_digits-self.decimal_places)
|
||||||
if '.' in data and len(data.split('.')[1]) > self.decimal_places:
|
if '.' in data and len(data.split('.')[1]) > self.decimal_places:
|
||||||
|
@ -13,9 +13,10 @@ def populate_xheaders(request, response, model, object_id):
|
|||||||
"""
|
"""
|
||||||
Adds the "X-Object-Type" and "X-Object-Id" headers to the given
|
Adds the "X-Object-Type" and "X-Object-Id" headers to the given
|
||||||
HttpResponse according to the given model and object_id -- but only if the
|
HttpResponse according to the given model and object_id -- but only if the
|
||||||
given HttpRequest object has an IP address within the INTERNAL_IPS setting.
|
given HttpRequest object has an IP address within the INTERNAL_IPS setting
|
||||||
|
or if the request is from a logged in staff member.
|
||||||
"""
|
"""
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
if request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS:
|
if request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS or (request.user.is_authenticated() and request.user.is_staff):
|
||||||
response['X-Object-Type'] = "%s.%s" % (model._meta.app_label, model._meta.object_name.lower())
|
response['X-Object-Type'] = "%s.%s" % (model._meta.app_label, model._meta.object_name.lower())
|
||||||
response['X-Object-Id'] = str(object_id)
|
response['X-Object-Id'] = str(object_id)
|
||||||
|
@ -110,9 +110,11 @@ def dictfetchone(cursor):
|
|||||||
def dictfetchmany(cursor, number):
|
def dictfetchmany(cursor, number):
|
||||||
"Returns a certain number of rows from a cursor as a dict"
|
"Returns a certain number of rows from a cursor as a dict"
|
||||||
desc = cursor.description
|
desc = cursor.description
|
||||||
return [_dict_helper(desc, row) for row in cursor.fetchmany(number)]
|
for row in cursor.fetchmany(number):
|
||||||
|
yield _dict_helper(desc, row)
|
||||||
|
|
||||||
def dictfetchall(cursor):
|
def dictfetchall(cursor):
|
||||||
"Returns all rows from a cursor as a dict"
|
"Returns all rows from a cursor as a dict"
|
||||||
desc = cursor.description
|
desc = cursor.description
|
||||||
return [_dict_helper(desc, row) for row in cursor.fetchall()]
|
for row in cursor.fetchall():
|
||||||
|
yield _dict_helper(desc, row)
|
||||||
|
@ -117,7 +117,7 @@ class GenericRelation(RelatedField, Field):
|
|||||||
return self.object_id_field_name
|
return self.object_id_field_name
|
||||||
|
|
||||||
def m2m_reverse_name(self):
|
def m2m_reverse_name(self):
|
||||||
return self.model._meta.pk.attname
|
return self.object_id_field_name
|
||||||
|
|
||||||
def contribute_to_class(self, cls, name):
|
def contribute_to_class(self, cls, name):
|
||||||
super(GenericRelation, self).contribute_to_class(cls, name)
|
super(GenericRelation, self).contribute_to_class(cls, name)
|
||||||
|
@ -434,11 +434,11 @@ class HiddenField(FormField):
|
|||||||
(self.get_id(), self.field_name, escape(data))
|
(self.get_id(), self.field_name, escape(data))
|
||||||
|
|
||||||
class CheckboxField(FormField):
|
class CheckboxField(FormField):
|
||||||
def __init__(self, field_name, checked_by_default=False, validator_list=None):
|
def __init__(self, field_name, checked_by_default=False, validator_list=None, is_required=False):
|
||||||
if validator_list is None: validator_list = []
|
if validator_list is None: validator_list = []
|
||||||
self.field_name = field_name
|
self.field_name = field_name
|
||||||
self.checked_by_default = checked_by_default
|
self.checked_by_default = checked_by_default
|
||||||
self.is_required = False # because the validator looks for these
|
self.is_required = is_required
|
||||||
self.validator_list = validator_list[:]
|
self.validator_list = validator_list[:]
|
||||||
|
|
||||||
def render(self, data):
|
def render(self, data):
|
||||||
@ -639,8 +639,8 @@ class CheckboxSelectMultipleField(SelectMultipleField):
|
|||||||
checked_html = ' checked="checked"'
|
checked_html = ' checked="checked"'
|
||||||
field_name = '%s%s' % (self.field_name, value)
|
field_name = '%s%s' % (self.field_name, value)
|
||||||
output.append('<li><input type="checkbox" id="%s" class="v%s" name="%s"%s /> <label for="%s">%s</label></li>' % \
|
output.append('<li><input type="checkbox" id="%s" class="v%s" name="%s"%s /> <label for="%s">%s</label></li>' % \
|
||||||
(self.get_id() + value , self.__class__.__name__, field_name, checked_html,
|
(self.get_id() + escape(value), self.__class__.__name__, field_name, checked_html,
|
||||||
self.get_id() + value, choice))
|
self.get_id() + escape(value), choice))
|
||||||
output.append('</ul>')
|
output.append('</ul>')
|
||||||
return '\n'.join(output)
|
return '\n'.join(output)
|
||||||
|
|
||||||
@ -743,7 +743,7 @@ class FloatField(TextField):
|
|||||||
if validator_list is None: validator_list = []
|
if validator_list is None: validator_list = []
|
||||||
self.max_digits, self.decimal_places = max_digits, decimal_places
|
self.max_digits, self.decimal_places = max_digits, decimal_places
|
||||||
validator_list = [self.isValidFloat] + validator_list
|
validator_list = [self.isValidFloat] + validator_list
|
||||||
TextField.__init__(self, field_name, max_digits+1, max_digits+1, is_required, validator_list)
|
TextField.__init__(self, field_name, max_digits+2, max_digits+2, is_required, validator_list)
|
||||||
|
|
||||||
def isValidFloat(self, field_data, all_data):
|
def isValidFloat(self, field_data, all_data):
|
||||||
v = validators.IsValidFloat(self.max_digits, self.decimal_places)
|
v = validators.IsValidFloat(self.max_digits, self.decimal_places)
|
||||||
@ -952,10 +952,7 @@ class USStateField(TextField):
|
|||||||
raise validators.CriticalValidationError, e.messages
|
raise validators.CriticalValidationError, e.messages
|
||||||
|
|
||||||
def html2python(data):
|
def html2python(data):
|
||||||
if data:
|
return data.upper() # Should always be stored in upper case
|
||||||
return data.upper() # Should always be stored in upper case
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
html2python = staticmethod(html2python)
|
html2python = staticmethod(html2python)
|
||||||
|
|
||||||
class CommaSeparatedIntegerField(TextField):
|
class CommaSeparatedIntegerField(TextField):
|
||||||
@ -972,9 +969,19 @@ class CommaSeparatedIntegerField(TextField):
|
|||||||
except validators.ValidationError, e:
|
except validators.ValidationError, e:
|
||||||
raise validators.CriticalValidationError, e.messages
|
raise validators.CriticalValidationError, e.messages
|
||||||
|
|
||||||
|
def render(self, data):
|
||||||
|
if data is None:
|
||||||
|
data = ''
|
||||||
|
elif isinstance(data, (list, tuple)):
|
||||||
|
data = ','.join(data)
|
||||||
|
return super(CommaSeparatedIntegerField, self).render(data)
|
||||||
|
|
||||||
class RawIdAdminField(CommaSeparatedIntegerField):
|
class RawIdAdminField(CommaSeparatedIntegerField):
|
||||||
def html2python(data):
|
def html2python(data):
|
||||||
return data.split(',')
|
if data:
|
||||||
|
return data.split(',')
|
||||||
|
else:
|
||||||
|
return []
|
||||||
html2python = staticmethod(html2python)
|
html2python = staticmethod(html2python)
|
||||||
|
|
||||||
class XMLLargeTextField(LargeTextField):
|
class XMLLargeTextField(LargeTextField):
|
||||||
|
@ -161,10 +161,10 @@ class HttpResponse(object):
|
|||||||
if not mimetype:
|
if not mimetype:
|
||||||
mimetype = "%s; charset=%s" % (settings.DEFAULT_CONTENT_TYPE, settings.DEFAULT_CHARSET)
|
mimetype = "%s; charset=%s" % (settings.DEFAULT_CONTENT_TYPE, settings.DEFAULT_CHARSET)
|
||||||
if hasattr(content, '__iter__'):
|
if hasattr(content, '__iter__'):
|
||||||
self._iterator = content
|
self._container = content
|
||||||
self._is_string = False
|
self._is_string = False
|
||||||
else:
|
else:
|
||||||
self._iterator = [content]
|
self._container = [content]
|
||||||
self._is_string = True
|
self._is_string = True
|
||||||
self.headers = {'Content-Type': mimetype}
|
self.headers = {'Content-Type': mimetype}
|
||||||
self.cookies = SimpleCookie()
|
self.cookies = SimpleCookie()
|
||||||
@ -213,32 +213,37 @@ class HttpResponse(object):
|
|||||||
self.cookies[key]['max-age'] = 0
|
self.cookies[key]['max-age'] = 0
|
||||||
|
|
||||||
def _get_content(self):
|
def _get_content(self):
|
||||||
content = ''.join(self._iterator)
|
content = ''.join(self._container)
|
||||||
if isinstance(content, unicode):
|
if isinstance(content, unicode):
|
||||||
content = content.encode(self._charset)
|
content = content.encode(self._charset)
|
||||||
return content
|
return content
|
||||||
|
|
||||||
def _set_content(self, value):
|
def _set_content(self, value):
|
||||||
self._iterator = [value]
|
self._container = [value]
|
||||||
self._is_string = True
|
self._is_string = True
|
||||||
|
|
||||||
content = property(_get_content, _set_content)
|
content = property(_get_content, _set_content)
|
||||||
|
|
||||||
def _get_iterator(self):
|
def __iter__(self):
|
||||||
"Output iterator. Converts data into client charset if necessary."
|
self._iterator = self._container.__iter__()
|
||||||
for chunk in self._iterator:
|
return self
|
||||||
if isinstance(chunk, unicode):
|
|
||||||
chunk = chunk.encode(self._charset)
|
|
||||||
yield chunk
|
|
||||||
|
|
||||||
iterator = property(_get_iterator)
|
def next(self):
|
||||||
|
chunk = self._iterator.next()
|
||||||
|
if isinstance(chunk, unicode):
|
||||||
|
chunk = chunk.encode(self._charset)
|
||||||
|
return chunk
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
if hasattr(self._container, 'close'):
|
||||||
|
self._container.close()
|
||||||
|
|
||||||
# The remaining methods partially implement the file-like object interface.
|
# The remaining methods partially implement the file-like object interface.
|
||||||
# See http://docs.python.org/lib/bltin-file-objects.html
|
# See http://docs.python.org/lib/bltin-file-objects.html
|
||||||
def write(self, content):
|
def write(self, content):
|
||||||
if not self._is_string:
|
if not self._is_string:
|
||||||
raise Exception, "This %s instance is not writable" % self.__class__
|
raise Exception, "This %s instance is not writable" % self.__class__
|
||||||
self._iterator.append(content)
|
self._container.append(content)
|
||||||
|
|
||||||
def flush(self):
|
def flush(self):
|
||||||
pass
|
pass
|
||||||
@ -246,7 +251,7 @@ class HttpResponse(object):
|
|||||||
def tell(self):
|
def tell(self):
|
||||||
if not self._is_string:
|
if not self._is_string:
|
||||||
raise Exception, "This %s instance cannot tell its position" % self.__class__
|
raise Exception, "This %s instance cannot tell its position" % self.__class__
|
||||||
return sum([len(chunk) for chunk in self._iterator])
|
return sum([len(chunk) for chunk in self._container])
|
||||||
|
|
||||||
class HttpResponseRedirect(HttpResponse):
|
class HttpResponseRedirect(HttpResponse):
|
||||||
def __init__(self, redirect_to):
|
def __init__(self, redirect_to):
|
||||||
|
@ -64,8 +64,9 @@ class CommonMiddleware(object):
|
|||||||
is_internal = referer and (domain in referer)
|
is_internal = referer and (domain in referer)
|
||||||
path = request.get_full_path()
|
path = request.get_full_path()
|
||||||
if referer and not _is_ignorable_404(path) and (is_internal or '?' not in referer):
|
if referer and not _is_ignorable_404(path) and (is_internal or '?' not in referer):
|
||||||
|
ua = request.META.get('HTTP_USER_AGENT','<none>')
|
||||||
mail_managers("Broken %slink on %s" % ((is_internal and 'INTERNAL ' or ''), domain),
|
mail_managers("Broken %slink on %s" % ((is_internal and 'INTERNAL ' or ''), domain),
|
||||||
"Referrer: %s\nRequested URL: %s\n" % (referer, request.get_full_path()))
|
"Referrer: %s\nRequested URL: %s\nUser Agent: %s\n" % (referer, request.get_full_path(), ua))
|
||||||
return response
|
return response
|
||||||
|
|
||||||
# Use ETags, if requested.
|
# Use ETags, if requested.
|
||||||
|
@ -7,11 +7,12 @@ class XViewMiddleware(object):
|
|||||||
"""
|
"""
|
||||||
def process_view(self, request, view_func, view_args, view_kwargs):
|
def process_view(self, request, view_func, view_args, view_kwargs):
|
||||||
"""
|
"""
|
||||||
If the request method is HEAD and the IP is internal, quickly return
|
If the request method is HEAD and either the IP is internal or the
|
||||||
with an x-header indicating the view function. This is used by the
|
user is a logged-in staff member, quickly return with an x-header
|
||||||
documentation module to lookup the view function for an arbitrary page.
|
indicating the view function. This is used by the documentation module
|
||||||
|
to lookup the view function for an arbitrary page.
|
||||||
"""
|
"""
|
||||||
if request.method == 'HEAD' and request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS:
|
if request.method == 'HEAD' and (request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS or (request.user.is_authenticated() and request.user.is_staff)):
|
||||||
response = http.HttpResponse()
|
response = http.HttpResponse()
|
||||||
response['X-View'] = "%s.%s" % (view_func.__module__, view_func.__name__)
|
response['X-View'] = "%s.%s" % (view_func.__module__, view_func.__name__)
|
||||||
return response
|
return response
|
||||||
|
@ -15,7 +15,7 @@ register = Library()
|
|||||||
|
|
||||||
def addslashes(value):
|
def addslashes(value):
|
||||||
"Adds slashes - useful for passing strings to JavaScript, for example."
|
"Adds slashes - useful for passing strings to JavaScript, for example."
|
||||||
return value.replace('"', '\\"').replace("'", "\\'")
|
return value.replace('\\', '\\\\').replace('"', '\\"').replace("'", "\\'")
|
||||||
|
|
||||||
def capfirst(value):
|
def capfirst(value):
|
||||||
"Capitalizes the first character of the value"
|
"Capitalizes the first character of the value"
|
||||||
|
@ -13,14 +13,18 @@ class CommentNode(Node):
|
|||||||
return ''
|
return ''
|
||||||
|
|
||||||
class CycleNode(Node):
|
class CycleNode(Node):
|
||||||
def __init__(self, cyclevars):
|
def __init__(self, cyclevars, variable_name=None):
|
||||||
self.cyclevars = cyclevars
|
self.cyclevars = cyclevars
|
||||||
self.cyclevars_len = len(cyclevars)
|
self.cyclevars_len = len(cyclevars)
|
||||||
self.counter = -1
|
self.counter = -1
|
||||||
|
self.variable_name = variable_name
|
||||||
|
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
self.counter += 1
|
self.counter += 1
|
||||||
return self.cyclevars[self.counter % self.cyclevars_len]
|
value = self.cyclevars[self.counter % self.cyclevars_len]
|
||||||
|
if self.variable_name:
|
||||||
|
context[self.variable_name] = value
|
||||||
|
return value
|
||||||
|
|
||||||
class DebugNode(Node):
|
class DebugNode(Node):
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
@ -125,6 +129,8 @@ class IfChangedNode(Node):
|
|||||||
self._last_seen = None
|
self._last_seen = None
|
||||||
|
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
|
if context.has_key('forloop') and context['forloop']['first']:
|
||||||
|
self._last_seen = None
|
||||||
content = self.nodelist.render(context)
|
content = self.nodelist.render(context)
|
||||||
if content != self._last_seen:
|
if content != self._last_seen:
|
||||||
firstloop = (self._last_seen == None)
|
firstloop = (self._last_seen == None)
|
||||||
@ -385,7 +391,7 @@ def cycle(parser, token):
|
|||||||
raise TemplateSyntaxError("Second 'cycle' argument must be 'as'")
|
raise TemplateSyntaxError("Second 'cycle' argument must be 'as'")
|
||||||
cyclevars = [v for v in args[1].split(",") if v] # split and kill blanks
|
cyclevars = [v for v in args[1].split(",") if v] # split and kill blanks
|
||||||
name = args[3]
|
name = args[3]
|
||||||
node = CycleNode(cyclevars)
|
node = CycleNode(cyclevars, name)
|
||||||
|
|
||||||
if not hasattr(parser, '_namedCycleNodes'):
|
if not hasattr(parser, '_namedCycleNodes'):
|
||||||
parser._namedCycleNodes = {}
|
parser._namedCycleNodes = {}
|
||||||
|
@ -14,6 +14,9 @@ class MergeDict(object):
|
|||||||
pass
|
pass
|
||||||
raise KeyError
|
raise KeyError
|
||||||
|
|
||||||
|
def __contains__(self, key):
|
||||||
|
return self.has_key(key)
|
||||||
|
|
||||||
def get(self, key, default):
|
def get(self, key, default):
|
||||||
try:
|
try:
|
||||||
return self[key]
|
return self[key]
|
||||||
|
@ -94,7 +94,8 @@ def compress_string(s):
|
|||||||
return zbuf.getvalue()
|
return zbuf.getvalue()
|
||||||
|
|
||||||
ustring_re = re.compile(u"([\u0080-\uffff])")
|
ustring_re = re.compile(u"([\u0080-\uffff])")
|
||||||
def javascript_quote(s):
|
|
||||||
|
def javascript_quote(s, quote_double_quotes=False):
|
||||||
|
|
||||||
def fix(match):
|
def fix(match):
|
||||||
return r"\u%04x" % ord(match.group(1))
|
return r"\u%04x" % ord(match.group(1))
|
||||||
@ -104,9 +105,12 @@ def javascript_quote(s):
|
|||||||
elif type(s) != unicode:
|
elif type(s) != unicode:
|
||||||
raise TypeError, s
|
raise TypeError, s
|
||||||
s = s.replace('\\', '\\\\')
|
s = s.replace('\\', '\\\\')
|
||||||
|
s = s.replace('\r', '\\r')
|
||||||
s = s.replace('\n', '\\n')
|
s = s.replace('\n', '\\n')
|
||||||
s = s.replace('\t', '\\t')
|
s = s.replace('\t', '\\t')
|
||||||
s = s.replace("'", "\\'")
|
s = s.replace("'", "\\'")
|
||||||
|
if quote_double_quotes:
|
||||||
|
s = s.replace('"', '"')
|
||||||
return str(ustring_re.sub(fix, s))
|
return str(ustring_re.sub(fix, s))
|
||||||
|
|
||||||
smart_split_re = re.compile('("(?:[^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'(?:[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'|[^\\s]+)')
|
smart_split_re = re.compile('("(?:[^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'(?:[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'|[^\\s]+)')
|
||||||
|
@ -456,6 +456,10 @@ As a shortcut, you can use the convenient ``user_passes_test`` decorator::
|
|||||||
# ...
|
# ...
|
||||||
my_view = user_passes_test(lambda u: u.has_perm('polls.can_vote'))(my_view)
|
my_view = user_passes_test(lambda u: u.has_perm('polls.can_vote'))(my_view)
|
||||||
|
|
||||||
|
We are using this particular test as a relatively simple example, however be
|
||||||
|
aware that if you just want to test if a permission is available to a user,
|
||||||
|
you can use the ``permission_required()`` decorator described below.
|
||||||
|
|
||||||
Here's the same thing, using Python 2.4's decorator syntax::
|
Here's the same thing, using Python 2.4's decorator syntax::
|
||||||
|
|
||||||
from django.contrib.auth.decorators import user_passes_test
|
from django.contrib.auth.decorators import user_passes_test
|
||||||
@ -488,6 +492,24 @@ Example in Python 2.4 syntax::
|
|||||||
def my_view(request):
|
def my_view(request):
|
||||||
# ...
|
# ...
|
||||||
|
|
||||||
|
The permission_required decorator
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Since checking whether a user has a particular permission available to them is a
|
||||||
|
relatively common operation, Django provides a shortcut for that particular
|
||||||
|
case: the ``permission_required()`` decorator. Using this decorator, the
|
||||||
|
earlier example can be written as::
|
||||||
|
|
||||||
|
from django.contrib.auth.decorators import permission_required
|
||||||
|
|
||||||
|
def my_view(request):
|
||||||
|
# ...
|
||||||
|
|
||||||
|
my_view = permission_required('polls.can_vote')(my_view)
|
||||||
|
|
||||||
|
Note that ``permission_required()`` also takes an optional ``login_url``
|
||||||
|
parameter.
|
||||||
|
|
||||||
Limiting access to generic views
|
Limiting access to generic views
|
||||||
--------------------------------
|
--------------------------------
|
||||||
|
|
||||||
@ -677,7 +699,7 @@ timestamps.
|
|||||||
Messages are used by the Django admin after successful actions. For example,
|
Messages are used by the Django admin after successful actions. For example,
|
||||||
``"The poll Foo was created successfully."`` is a message.
|
``"The poll Foo was created successfully."`` is a message.
|
||||||
|
|
||||||
The API is simple::
|
The API is simple:
|
||||||
|
|
||||||
* To create a new message, use
|
* To create a new message, use
|
||||||
``user_obj.message_set.create(message='message_text')``.
|
``user_obj.message_set.create(message='message_text')``.
|
||||||
|
@ -247,18 +247,23 @@ Django tarball. It's our policy to make sure all tests pass at all times.
|
|||||||
|
|
||||||
The tests cover:
|
The tests cover:
|
||||||
|
|
||||||
* Models and the database API (``tests/testapp/models``).
|
* Models and the database API (``tests/modeltests/``).
|
||||||
* The cache system (``tests/otherthests/cache.py``).
|
* The cache system (``tests/regressiontests/cache.py``).
|
||||||
* The ``django.utils.dateformat`` module (``tests/othertests/dateformat.py``).
|
* The ``django.utils.dateformat`` module (``tests/regressiontests/dateformat/``).
|
||||||
* Database typecasts (``tests/othertests/db_typecasts.py``).
|
* Database typecasts (``tests/regressiontests/db_typecasts/``).
|
||||||
* The template system (``tests/othertests/templates.py`` and
|
* The template system (``tests/regressiontests/templates/`` and
|
||||||
``tests/othertests/defaultfilters.py``).
|
``tests/regressiontests/defaultfilters/``).
|
||||||
* ``QueryDict`` objects (``tests/othertests/httpwrappers.py``).
|
* ``QueryDict`` objects (``tests/regressiontests/httpwrappers/``).
|
||||||
* Markup template tags (``tests/othertests/markup.py``).
|
* Markup template tags (``tests/regressiontests/markup/``).
|
||||||
* The ``django.utils.timesince`` module (``tests/othertests/timesince.py``).
|
|
||||||
|
|
||||||
We appreciate any and all contributions to the test suite!
|
We appreciate any and all contributions to the test suite!
|
||||||
|
|
||||||
|
The Django tests all use the testing infrastructure that ships with Django for
|
||||||
|
testing applications. See `Testing Django Applications`_ for an explanation of
|
||||||
|
how to write new tests.
|
||||||
|
|
||||||
|
.. _Testing Django Applications: http://www.djangoproject.com/documentation/testing/
|
||||||
|
|
||||||
Running the unit tests
|
Running the unit tests
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
@ -268,10 +273,14 @@ To run the tests, ``cd`` to the ``tests/`` directory and type::
|
|||||||
|
|
||||||
Yes, the unit tests need a settings module, but only for database connection
|
Yes, the unit tests need a settings module, but only for database connection
|
||||||
info -- the ``DATABASE_ENGINE``, ``DATABASE_USER`` and ``DATABASE_PASSWORD``.
|
info -- the ``DATABASE_ENGINE``, ``DATABASE_USER`` and ``DATABASE_PASSWORD``.
|
||||||
|
You will also need a ``ROOT_URLCONF`` setting (it's value is ignored; it just
|
||||||
|
needs to be present) and a ``SITE_ID`` setting (any integer value will do) in
|
||||||
|
order for all the tests to pass.
|
||||||
|
|
||||||
The unit tests will not touch your database; they create a new database, called
|
The unit tests will not touch your existing databases; they create a new
|
||||||
``django_test_db``, which is deleted when the tests are finished. This means
|
database, called ``django_test_db``, which is deleted when the tests are
|
||||||
your user account needs permission to execute ``CREATE DATABASE``.
|
finished. This means your user account needs permission to execute ``CREATE
|
||||||
|
DATABASE``.
|
||||||
|
|
||||||
Requesting features
|
Requesting features
|
||||||
===================
|
===================
|
||||||
|
@ -1511,7 +1511,7 @@ Many-to-many relationships
|
|||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
Both ends of a many-to-many relationship get automatic API access to the other
|
Both ends of a many-to-many relationship get automatic API access to the other
|
||||||
end. The API works just as a "backward" one-to-many relationship. See _Backward
|
end. The API works just as a "backward" one-to-many relationship. See Backward_
|
||||||
above.
|
above.
|
||||||
|
|
||||||
The only difference is in the attribute naming: The model that defines the
|
The only difference is in the attribute naming: The model that defines the
|
||||||
|
@ -352,8 +352,9 @@ options.
|
|||||||
|
|
||||||
**New in Django development version**
|
**New in Django development version**
|
||||||
|
|
||||||
Inform django-admin that the user should NOT be prompted for any input. Useful if
|
Inform django-admin that the user should NOT be prompted for any input. Useful
|
||||||
the django-admin script will be executed as an unattended, automated script.
|
if the django-admin script will be executed as an unattended, automated
|
||||||
|
script.
|
||||||
|
|
||||||
--noreload
|
--noreload
|
||||||
----------
|
----------
|
||||||
@ -383,6 +384,19 @@ Verbosity determines the amount of notification and debug information that
|
|||||||
will be printed to the console. '0' is no output, '1' is normal output,
|
will be printed to the console. '0' is no output, '1' is normal output,
|
||||||
and `2` is verbose output.
|
and `2` is verbose output.
|
||||||
|
|
||||||
|
--adminmedia
|
||||||
|
------------
|
||||||
|
|
||||||
|
**New in Django development version**
|
||||||
|
|
||||||
|
Example usage::
|
||||||
|
django-admin.py manage.py --adminmedia=/tmp/new-admin-style/
|
||||||
|
|
||||||
|
Tell Django where to find the various stylesheets and Javascript files for the
|
||||||
|
admin interface when running the development server. Normally these files are
|
||||||
|
served out of the Django source tree, but since some designers change these
|
||||||
|
files for their site, this option allows you to test against custom versions.
|
||||||
|
|
||||||
Extra niceties
|
Extra niceties
|
||||||
==============
|
==============
|
||||||
|
|
||||||
|
@ -136,7 +136,7 @@ template::
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
Before we get back to the problems with these naive set of views, let's go over
|
Before we get back to the problems with these naive set of views, let's go over
|
||||||
some salient points of the above template::
|
some salient points of the above template:
|
||||||
|
|
||||||
* Field "widgets" are handled for you: ``{{ form.field }}`` automatically
|
* Field "widgets" are handled for you: ``{{ form.field }}`` automatically
|
||||||
creates the "right" type of widget for the form, as you can see with the
|
creates the "right" type of widget for the form, as you can see with the
|
||||||
@ -148,8 +148,8 @@ some salient points of the above template::
|
|||||||
If you must use tables, use tables. If you're a semantic purist, you can
|
If you must use tables, use tables. If you're a semantic purist, you can
|
||||||
probably find better HTML than in the above template.
|
probably find better HTML than in the above template.
|
||||||
|
|
||||||
* To avoid name conflicts, the ``id``s of form elements take the form
|
* To avoid name conflicts, the ``id`` values of form elements take the
|
||||||
"id_*fieldname*".
|
form "id_*fieldname*".
|
||||||
|
|
||||||
By creating a creation form we've solved problem number 3 above, but we still
|
By creating a creation form we've solved problem number 3 above, but we still
|
||||||
don't have any validation. Let's revise the validation issue by writing a new
|
don't have any validation. Let's revise the validation issue by writing a new
|
||||||
@ -481,6 +481,33 @@ the data being validated.
|
|||||||
Also, because consistency in user interfaces is important, we strongly urge you
|
Also, because consistency in user interfaces is important, we strongly urge you
|
||||||
to put punctuation at the end of your validation messages.
|
to put punctuation at the end of your validation messages.
|
||||||
|
|
||||||
|
When Are Validators Called?
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
After a form has been submitted, Django first checks to see that all the
|
||||||
|
required fields are present and non-empty. For each field that passes that
|
||||||
|
test *and if the form submission contained data* for that field, all the
|
||||||
|
validators for that field are called in turn. The emphasised portion in the
|
||||||
|
last sentence is important: if a form field is not submitted (because it
|
||||||
|
contains no data -- which is normal HTML behaviour), the validators are not
|
||||||
|
run against the field.
|
||||||
|
|
||||||
|
This feature is particularly important for models using
|
||||||
|
``models.BooleanField`` or custom manipulators using things like
|
||||||
|
``forms.CheckBoxField``. If the checkbox is not selected, it will not
|
||||||
|
contribute to the form submission.
|
||||||
|
|
||||||
|
If you would like your validator to *always* run, regardless of whether the
|
||||||
|
field it is attached to contains any data, set the ``always_test`` attribute
|
||||||
|
on the validator function. For example::
|
||||||
|
|
||||||
|
def my_custom_validator(field_data, all_data):
|
||||||
|
# ...
|
||||||
|
|
||||||
|
my_custom_validator.always_test = True
|
||||||
|
|
||||||
|
This validator will always be executed for any field it is attached to.
|
||||||
|
|
||||||
Ready-made Validators
|
Ready-made Validators
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
|
@ -543,7 +543,9 @@ The default value for the field.
|
|||||||
``editable``
|
``editable``
|
||||||
~~~~~~~~~~~~
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
If ``False``, the field will not be editable in the admin. Default is ``True``.
|
If ``False``, the field will not be editable in the admin or via form
|
||||||
|
processing using the object's ``AddManipulator`` or ``ChangeManipulator``
|
||||||
|
classes. Default is ``True``.
|
||||||
|
|
||||||
``help_text``
|
``help_text``
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
|
@ -96,6 +96,21 @@ Django "ships" with a few included serializers:
|
|||||||
.. _json: http://json.org/
|
.. _json: http://json.org/
|
||||||
.. _simplejson: http://undefined.org/python/#simplejson
|
.. _simplejson: http://undefined.org/python/#simplejson
|
||||||
|
|
||||||
|
Notes For Specific Serialization Formats
|
||||||
|
----------------------------------------
|
||||||
|
|
||||||
|
json
|
||||||
|
~~~~
|
||||||
|
|
||||||
|
If you are using UTF-8 (or any other non-ASCII encoding) data with the JSON
|
||||||
|
serializer, you must pass ``ensure_ascii=False`` as a parameter to the
|
||||||
|
``serialize()`` call. Otherwise the output will not be encoded correctly.
|
||||||
|
|
||||||
|
For example::
|
||||||
|
|
||||||
|
json_serializer = serializers.get_serializer("json")
|
||||||
|
json_serializer.serialize(queryset, ensure_ascii=False, stream=response)
|
||||||
|
|
||||||
Writing custom serializers
|
Writing custom serializers
|
||||||
``````````````````````````
|
``````````````````````````
|
||||||
|
|
||||||
|
@ -603,6 +603,12 @@ Whether to prepend the "www." subdomain to URLs that don't have it. This is
|
|||||||
only used if ``CommonMiddleware`` is installed (see the `middleware docs`_).
|
only used if ``CommonMiddleware`` is installed (see the `middleware docs`_).
|
||||||
See also ``APPEND_SLASH``.
|
See also ``APPEND_SLASH``.
|
||||||
|
|
||||||
|
PROFANITIES_LIST
|
||||||
|
----------------
|
||||||
|
|
||||||
|
A list of profanities that will trigger a validation error when the
|
||||||
|
``hasNoProfanities`` validator is called.
|
||||||
|
|
||||||
ROOT_URLCONF
|
ROOT_URLCONF
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
@ -763,17 +763,17 @@ will use the function's name as the tag name.
|
|||||||
Shortcut for simple tags
|
Shortcut for simple tags
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Many template tags take a single argument -- a string or a template variable
|
Many template tags take a number of arguments -- strings or a template variables
|
||||||
reference -- and return a string after doing some processing based solely on
|
-- and return a string after doing some processing based solely on
|
||||||
the input argument and some external information. For example, the
|
the input argument and some external information. For example, the
|
||||||
``current_time`` tag we wrote above is of this variety: we give it a format
|
``current_time`` tag we wrote above is of this variety: we give it a format
|
||||||
string, it returns the time as a string.
|
string, it returns the time as a string.
|
||||||
|
|
||||||
To ease the creation of the types of tags, Django provides a helper function,
|
To ease the creation of the types of tags, Django provides a helper function,
|
||||||
``simple_tag``. This function, which is a method of
|
``simple_tag``. This function, which is a method of
|
||||||
``django.template.Library``, takes a function that accepts one argument, wraps
|
``django.template.Library``, takes a function that accepts any number of
|
||||||
it in a ``render`` function and the other necessary bits mentioned above and
|
arguments, wraps it in a ``render`` function and the other necessary bits
|
||||||
registers it with the template system.
|
mentioned above and registers it with the template system.
|
||||||
|
|
||||||
Our earlier ``current_time`` function could thus be written like this::
|
Our earlier ``current_time`` function could thus be written like this::
|
||||||
|
|
||||||
@ -789,11 +789,16 @@ In Python 2.4, the decorator syntax also works::
|
|||||||
...
|
...
|
||||||
|
|
||||||
A couple of things to note about the ``simple_tag`` helper function:
|
A couple of things to note about the ``simple_tag`` helper function:
|
||||||
* Only the (single) argument is passed into our function.
|
|
||||||
* Checking for the required number of arguments, etc, has already been
|
* Checking for the required number of arguments, etc, has already been
|
||||||
done by the time our function is called, so we don't need to do that.
|
done by the time our function is called, so we don't need to do that.
|
||||||
* The quotes around the argument (if any) have already been stripped away,
|
* The quotes around the argument (if any) have already been stripped away,
|
||||||
so we just receive a plain string.
|
so we just receive a plain string.
|
||||||
|
* If the argument was a template variable, our function is passed the
|
||||||
|
current value of the variable, not the variable itself.
|
||||||
|
|
||||||
|
When your template tag does not need access to the current context, writing a
|
||||||
|
function to work with the input values and using the ``simple_tag`` helper is
|
||||||
|
the easiest way to create a new tag.
|
||||||
|
|
||||||
Inclusion tags
|
Inclusion tags
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
|
@ -15,6 +15,9 @@ r"""
|
|||||||
>>> addslashes('"double quotes" and \'single quotes\'')
|
>>> addslashes('"double quotes" and \'single quotes\'')
|
||||||
'\\"double quotes\\" and \\\'single quotes\\\''
|
'\\"double quotes\\" and \\\'single quotes\\\''
|
||||||
|
|
||||||
|
>>> addslashes(r'\ : backslashes, too')
|
||||||
|
'\\\\ : backslashes, too'
|
||||||
|
|
||||||
>>> capfirst('hello world')
|
>>> capfirst('hello world')
|
||||||
'Hello world'
|
'Hello world'
|
||||||
|
|
||||||
|
@ -187,6 +187,7 @@ class Templates(unittest.TestCase):
|
|||||||
'cycle05': ('{% cycle %}', {}, template.TemplateSyntaxError),
|
'cycle05': ('{% cycle %}', {}, template.TemplateSyntaxError),
|
||||||
'cycle06': ('{% cycle a %}', {}, template.TemplateSyntaxError),
|
'cycle06': ('{% cycle a %}', {}, template.TemplateSyntaxError),
|
||||||
'cycle07': ('{% cycle a,b,c as foo %}{% cycle bar %}', {}, template.TemplateSyntaxError),
|
'cycle07': ('{% cycle a,b,c as foo %}{% cycle bar %}', {}, template.TemplateSyntaxError),
|
||||||
|
'cycle08': ('{% cycle a,b,c as foo %}{% cycle foo %}{{ foo }}{{ foo }}{% cycle foo %}{{ foo }}', {}, 'abbbcc'),
|
||||||
|
|
||||||
### EXCEPTIONS ############################################################
|
### EXCEPTIONS ############################################################
|
||||||
|
|
||||||
@ -304,6 +305,10 @@ class Templates(unittest.TestCase):
|
|||||||
'ifchanged01': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% endfor %}', { 'num': (1,2,3) }, '123'),
|
'ifchanged01': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% endfor %}', { 'num': (1,2,3) }, '123'),
|
||||||
'ifchanged02': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% endfor %}', { 'num': (1,1,3) }, '13'),
|
'ifchanged02': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% endfor %}', { 'num': (1,1,3) }, '13'),
|
||||||
'ifchanged03': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% endfor %}', { 'num': (1,1,1) }, '1'),
|
'ifchanged03': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% endfor %}', { 'num': (1,1,1) }, '1'),
|
||||||
|
'ifchanged04': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% for x in numx %}{% ifchanged %}{{ x }}{% endifchanged %}{% endfor %}{% endfor %}', { 'num': (1, 2, 3), 'numx': (2, 2, 2)}, '122232'),
|
||||||
|
'ifchanged05': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% for x in numx %}{% ifchanged %}{{ x }}{% endifchanged %}{% endfor %}{% endfor %}', { 'num': (1, 1, 1), 'numx': (1, 2, 3)}, '1123123123'),
|
||||||
|
'ifchanged06': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% for x in numx %}{% ifchanged %}{{ x }}{% endifchanged %}{% endfor %}{% endfor %}', { 'num': (1, 1, 1), 'numx': (2, 2, 2)}, '1222'),
|
||||||
|
'ifchanged07': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% for x in numx %}{% ifchanged %}{{ x }}{% endifchanged %}{% for y in numy %}{% ifchanged %}{{ y }}{% endifchanged %}{% endfor %}{% endfor %}{% endfor %}', { 'num': (1, 1, 1), 'numx': (2, 2, 2), 'numy': (3, 3, 3)}, '1233323332333'),
|
||||||
|
|
||||||
### IFEQUAL TAG ###########################################################
|
### IFEQUAL TAG ###########################################################
|
||||||
'ifequal01': ("{% ifequal a b %}yes{% endifequal %}", {"a": 1, "b": 2}, ""),
|
'ifequal01': ("{% ifequal a b %}yes{% endifequal %}", {"a": 1, "b": 2}, ""),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user