1
0
mirror of https://github.com/django/django.git synced 2025-07-05 02:09:13 +00:00

queryset-refactor: Merged from trunk up to [7168].

git-svn-id: http://code.djangoproject.com/svn/django/branches/queryset-refactor@7173 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Malcolm Tredinnick 2008-02-28 15:48:19 +00:00
parent 7c54780497
commit 2f2908d7b5
18 changed files with 300 additions and 47 deletions

View File

@ -1,3 +1,11 @@
import base64
import md5
import cPickle as pickle
try:
from functools import wraps
except ImportError:
from django.utils.functional import wraps # Python 2.3, 2.4 fallback.
from django import http, template from django import http, template
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import User from django.contrib.auth.models import User
@ -5,8 +13,6 @@ from django.contrib.auth import authenticate, login
from django.shortcuts import render_to_response from django.shortcuts import render_to_response
from django.utils.translation import ugettext_lazy, ugettext as _ from django.utils.translation import ugettext_lazy, ugettext as _
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
import base64, md5
import cPickle as pickle
ERROR_MESSAGE = ugettext_lazy("Please enter a correct username and password. Note that both fields are case-sensitive.") ERROR_MESSAGE = ugettext_lazy("Please enter a correct username and password. Note that both fields are case-sensitive.")
LOGIN_FORM_KEY = 'this_is_the_login_form' LOGIN_FORM_KEY = 'this_is_the_login_form'
@ -104,4 +110,4 @@ def staff_member_required(view_func):
else: else:
return _display_login_form(request, ERROR_MESSAGE) return _display_login_form(request, ERROR_MESSAGE)
return _checklogin return wraps(view_func)(_checklogin)

View File

@ -1,3 +1,8 @@
try:
from functools import wraps, update_wrapper
except ImportError:
from django.utils.functional import wraps, update_wrapper # Python 2.3, 2.4 fallback.
from django.contrib.auth import REDIRECT_FIELD_NAME from django.contrib.auth import REDIRECT_FIELD_NAME
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.utils.http import urlquote from django.utils.http import urlquote
@ -51,7 +56,7 @@ class _CheckLogin(object):
self.test_func = test_func self.test_func = test_func
self.login_url = login_url self.login_url = login_url
self.redirect_field_name = redirect_field_name self.redirect_field_name = redirect_field_name
self.__name__ = view_func.__name__ update_wrapper(self, view_func)
def __get__(self, obj, cls=None): def __get__(self, obj, cls=None):
view_func = self.view_func.__get__(obj, cls) view_func = self.view_func.__get__(obj, cls)

View File

@ -109,7 +109,8 @@ class BaseHandler(object):
except exceptions.PermissionDenied: except exceptions.PermissionDenied:
return http.HttpResponseForbidden('<h1>Permission denied</h1>') return http.HttpResponseForbidden('<h1>Permission denied</h1>')
except SystemExit: except SystemExit:
pass # See http://code.djangoproject.com/ticket/1023 # Allow sys.exit() to actually exit. See tickets #1023 and #4701
raise
except: # Handle everything else, including SuspiciousOperation, etc. except: # Handle everything else, including SuspiciousOperation, etc.
# Get the exception info now, in case another exception is thrown later. # Get the exception info now, in case another exception is thrown later.
exc_info = sys.exc_info() exc_info = sys.exc_info()

View File

@ -29,7 +29,7 @@ class DatabaseOperations(BaseDatabaseOperations):
def field_cast_sql(self, db_type): def field_cast_sql(self, db_type):
if db_type == 'inet': if db_type == 'inet':
return 'CAST(%s AS TEXT)' return 'HOST(%s)'
return '%s' return '%s'
def last_insert_id(self, cursor, table_name, pk_name): def last_insert_id(self, cursor, table_name, pk_name):

View File

@ -23,26 +23,64 @@ RECURSIVE_RELATIONSHIP_CONSTANT = 'self'
pending_lookups = {} pending_lookups = {}
def add_lookup(rel_cls, field): def add_lazy_relation(cls, field, relation):
name = field.rel.to """
module = rel_cls.__module__ Adds a lookup on ``cls`` when a related field is defined using a string,
key = (module, name) i.e.::
# Has the model already been loaded?
# If so, resolve the string reference right away class MyModel(Model):
model = get_model(rel_cls._meta.app_label, field.rel.to, False) fk = ForeignKey("AnotherModel")
This string can be:
* RECURSIVE_RELATIONSHIP_CONSTANT (i.e. "self") to indicate a recursive
relation.
* The name of a model (i.e "AnotherModel") to indicate another model in
the same app.
* An app-label and model name (i.e. "someapp.AnotherModel") to indicate
another model in a different app.
If the other model hasn't yet been loaded -- almost a given if you're using
lazy relationships -- then the relation won't be set up until the
class_prepared signal fires at the end of model initialization.
"""
# Check for recursive relations
if relation == RECURSIVE_RELATIONSHIP_CONSTANT:
app_label = cls._meta.app_label
model_name = cls.__name__
else:
# Look for an "app.Model" relation
try:
app_label, model_name = relation.split(".")
except ValueError:
# If we can't split, assume a model in current app
app_label = cls._meta.app_label
model_name = relation
# Try to look up the related model, and if it's already loaded resolve the
# string right away. If get_model returns None, it means that the related
# model isn't loaded yet, so we need to pend the relation until the class
# is prepared.
model = get_model(app_label, model_name, False)
if model: if model:
field.rel.to = model field.rel.to = model
field.do_related_class(model, rel_cls) field.do_related_class(model, cls)
else: else:
# Mark the related field for later lookup key = (app_label, model_name)
pending_lookups.setdefault(key, []).append((rel_cls, field)) value = (cls, field)
pending_lookups.setdefault(key, []).append(value)
def do_pending_lookups(sender): def do_pending_lookups(sender):
other_cls = sender """
key = (other_cls.__module__, other_cls.__name__) Handle any pending relations to the sending model. Sent from class_prepared.
for rel_cls, field in pending_lookups.setdefault(key, []): """
field.rel.to = other_cls key = (sender._meta.app_label, sender.__name__)
field.do_related_class(other_cls, rel_cls) for cls, field in pending_lookups.pop(key, []):
field.rel.to = sender
field.do_related_class(sender, cls)
dispatcher.connect(do_pending_lookups, signal=signals.class_prepared) dispatcher.connect(do_pending_lookups, signal=signals.class_prepared)
@ -66,9 +104,7 @@ class RelatedField(object):
sup.contribute_to_class(cls, name) sup.contribute_to_class(cls, name)
other = self.rel.to other = self.rel.to
if isinstance(other, basestring): if isinstance(other, basestring):
if other == RECURSIVE_RELATIONSHIP_CONSTANT: add_lazy_relation(cls, self, other)
self.rel.to = cls.__name__
add_lookup(cls, self)
else: else:
self.do_related_class(other, cls) self.do_related_class(other, cls)

View File

@ -171,7 +171,7 @@ class QueryDict(MultiValueDict):
dict.__setitem__(result, key, value) dict.__setitem__(result, key, value)
return result return result
def __deepcopy__(self, memo={}): def __deepcopy__(self, memo):
import copy import copy
result = self.__class__('', mutable=True) result = self.__class__('', mutable=True)
memo[id(self)] = result memo[id(self)] = result
@ -223,7 +223,7 @@ class QueryDict(MultiValueDict):
def copy(self): def copy(self):
"Returns a mutable copy of this object." "Returns a mutable copy of this object."
return self.__deepcopy__() return self.__deepcopy__({})
def urlencode(self): def urlencode(self):
output = [] output = []

View File

@ -2,6 +2,10 @@
import re import re
import random as random_module import random as random_module
try:
from functools import wraps
except ImportError:
from django.utils.functional import wraps # Python 2.3, 2.4 fallback.
from django.template import Variable, Library from django.template import Variable, Library
from django.conf import settings from django.conf import settings
@ -35,7 +39,7 @@ def stringfilter(func):
for attr in ('is_safe', 'needs_autoescape'): for attr in ('is_safe', 'needs_autoescape'):
if hasattr(func, attr): if hasattr(func, attr):
setattr(_dec, attr, getattr(func, attr)) setattr(_dec, attr, getattr(func, attr))
return _dec return wraps(func)(_dec)
################### ###################
# STRINGS # # STRINGS #

View File

@ -1,6 +1,10 @@
"Functions that help with dynamically creating decorators for views." "Functions that help with dynamically creating decorators for views."
import types import types
try:
from functools import wraps
except ImportError:
from django.utils.functional import wraps # Python 2.3, 2.4 fallback.
def decorator_from_middleware(middleware_class): def decorator_from_middleware(middleware_class):
""" """
@ -53,5 +57,5 @@ def decorator_from_middleware(middleware_class):
if result is not None: if result is not None:
return result return result
return response return response
return _wrapped_view return wraps(view_func)(_wrapped_view)
return _decorator_from_middleware return _decorator_from_middleware

View File

@ -1,8 +1,120 @@
# License for code in this file that was taken from Python 2.5.
# PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
# --------------------------------------------
#
# 1. This LICENSE AGREEMENT is between the Python Software Foundation
# ("PSF"), and the Individual or Organization ("Licensee") accessing and
# otherwise using this software ("Python") in source or binary form and
# its associated documentation.
#
# 2. Subject to the terms and conditions of this License Agreement, PSF
# hereby grants Licensee a nonexclusive, royalty-free, world-wide
# license to reproduce, analyze, test, perform and/or display publicly,
# prepare derivative works, distribute, and otherwise use Python
# alone or in any derivative version, provided, however, that PSF's
# License Agreement and PSF's notice of copyright, i.e., "Copyright (c)
# 2001, 2002, 2003, 2004, 2005, 2006, 2007 Python Software Foundation;
# All Rights Reserved" are retained in Python alone or in any derivative
# version prepared by Licensee.
#
# 3. In the event Licensee prepares a derivative work that is based on
# or incorporates Python or any part thereof, and wants to make
# the derivative work available to others as provided herein, then
# Licensee hereby agrees to include in any such work a brief summary of
# the changes made to Python.
#
# 4. PSF is making Python available to Licensee on an "AS IS"
# basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
# IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
# DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
# FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
# INFRINGE ANY THIRD PARTY RIGHTS.
#
# 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
# FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
# A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
# OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
#
# 6. This License Agreement will automatically terminate upon a material
# breach of its terms and conditions.
#
# 7. Nothing in this License Agreement shall be deemed to create any
# relationship of agency, partnership, or joint venture between PSF and
# Licensee. This License Agreement does not grant permission to use PSF
# trademarks or trade name in a trademark sense to endorse or promote
# products or services of Licensee, or any third party.
#
# 8. By copying, installing or otherwise using Python, Licensee
# agrees to be bound by the terms and conditions of this License
# Agreement.
def curry(_curried_func, *args, **kwargs): def curry(_curried_func, *args, **kwargs):
def _curried(*moreargs, **morekwargs): def _curried(*moreargs, **morekwargs):
return _curried_func(*(args+moreargs), **dict(kwargs, **morekwargs)) return _curried_func(*(args+moreargs), **dict(kwargs, **morekwargs))
return _curried return _curried
### Begin from Python 2.5 functools.py ########################################
# Summary of changes made to the Python 2.5 code below:
# * swapped ``partial`` for ``curry`` to maintain backwards-compatibility
# in Django.
# * Wrapped the ``setattr`` call in ``update_wrapper`` with a try-except
# block to make it compatible with Python 2.3, which doesn't allow
# assigning to ``__name__``.
# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007 Python Software Foundation.
# All Rights Reserved.
###############################################################################
# update_wrapper() and wraps() are tools to help write
# wrapper functions that can handle naive introspection
WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__')
WRAPPER_UPDATES = ('__dict__',)
def update_wrapper(wrapper,
wrapped,
assigned = WRAPPER_ASSIGNMENTS,
updated = WRAPPER_UPDATES):
"""Update a wrapper function to look like the wrapped function
wrapper is the function to be updated
wrapped is the original function
assigned is a tuple naming the attributes assigned directly
from the wrapped function to the wrapper function (defaults to
functools.WRAPPER_ASSIGNMENTS)
updated is a tuple naming the attributes off the wrapper that
are updated with the corresponding attribute from the wrapped
function (defaults to functools.WRAPPER_UPDATES)
"""
for attr in assigned:
try:
setattr(wrapper, attr, getattr(wrapped, attr))
except TypeError: # Python 2.3 doesn't allow assigning to __name__.
pass
for attr in updated:
getattr(wrapper, attr).update(getattr(wrapped, attr))
# Return the wrapper so this can be used as a decorator via curry()
return wrapper
def wraps(wrapped,
assigned = WRAPPER_ASSIGNMENTS,
updated = WRAPPER_UPDATES):
"""Decorator factory to apply update_wrapper() to a wrapper function
Returns a decorator that invokes update_wrapper() with the decorated
function as the wrapper argument and the arguments to wraps() as the
remaining arguments. Default arguments are as for update_wrapper().
This is a convenience function to simplify applying curry() to
update_wrapper().
"""
return curry(update_wrapper, wrapped=wrapped,
assigned=assigned, updated=updated)
### End from Python 2.5 functools.py ##########################################
def memoize(func, cache, num_args): def memoize(func, cache, num_args):
""" """
Wrap a function so that results for any argument tuple are stored in Wrap a function so that results for any argument tuple are stored in
@ -18,7 +130,7 @@ def memoize(func, cache, num_args):
result = func(*args) result = func(*args)
cache[mem_args] = result cache[mem_args] = result
return result return result
return wrapper return wraps(func)(wrapper)
class Promise(object): class Promise(object):
""" """
@ -110,7 +222,7 @@ def lazy(func, *resultclasses):
# Creates the proxy object, instead of the actual value. # Creates the proxy object, instead of the actual value.
return __proxy__(args, kw) return __proxy__(args, kw)
return __wrapper__ return wraps(func)(__wrapper__)
def allow_lazy(func, *resultclasses): def allow_lazy(func, *resultclasses):
""" """
@ -126,4 +238,4 @@ def allow_lazy(func, *resultclasses):
else: else:
return func(*args, **kwargs) return func(*args, **kwargs)
return lazy(func, *resultclasses)(*args, **kwargs) return lazy(func, *resultclasses)(*args, **kwargs)
return wrapper return wraps(func)(wrapper)

View File

@ -11,6 +11,11 @@ Additionally, all headers from the response's Vary header will be taken into
account on caching -- just like the middleware does. account on caching -- just like the middleware does.
""" """
try:
from functools import wraps
except ImportError:
from django.utils.functional import wraps # Python 2.3, 2.4 fallback.
from django.utils.decorators import decorator_from_middleware from django.utils.decorators import decorator_from_middleware
from django.utils.cache import patch_cache_control, add_never_cache_headers from django.utils.cache import patch_cache_control, add_never_cache_headers
from django.middleware.cache import CacheMiddleware from django.middleware.cache import CacheMiddleware
@ -26,7 +31,7 @@ def cache_control(**kwargs):
patch_cache_control(response, **kwargs) patch_cache_control(response, **kwargs)
return response return response
return _cache_controlled return wraps(viewfunc)(_cache_controlled)
return _cache_controller return _cache_controller
@ -39,4 +44,4 @@ def never_cache(view_func):
response = view_func(request, *args, **kwargs) response = view_func(request, *args, **kwargs)
add_never_cache_headers(response) add_never_cache_headers(response)
return response return response
return _wrapped_view_func return wraps(view_func)(_wrapped_view_func)

View File

@ -2,6 +2,11 @@
Decorators for views based on HTTP headers. Decorators for views based on HTTP headers.
""" """
try:
from functools import wraps
except ImportError:
from django.utils.functional import wraps # Python 2.3, 2.4 fallback.
from django.utils.decorators import decorator_from_middleware from django.utils.decorators import decorator_from_middleware
from django.middleware.http import ConditionalGetMiddleware from django.middleware.http import ConditionalGetMiddleware
from django.http import HttpResponseNotAllowed from django.http import HttpResponseNotAllowed
@ -24,7 +29,7 @@ def require_http_methods(request_method_list):
if request.method not in request_method_list: if request.method not in request_method_list:
return HttpResponseNotAllowed(request_method_list) return HttpResponseNotAllowed(request_method_list)
return func(request, *args, **kwargs) return func(request, *args, **kwargs)
return inner return wraps(func)(inner)
return decorator return decorator
require_GET = require_http_methods(["GET"]) require_GET = require_http_methods(["GET"])

View File

@ -1,3 +1,8 @@
try:
from functools import wraps
except ImportError:
from django.utils.functional import wraps # Python 2.3, 2.4 fallback.
from django.utils.cache import patch_vary_headers from django.utils.cache import patch_vary_headers
def vary_on_headers(*headers): def vary_on_headers(*headers):
@ -16,7 +21,7 @@ def vary_on_headers(*headers):
response = func(*args, **kwargs) response = func(*args, **kwargs)
patch_vary_headers(response, headers) patch_vary_headers(response, headers)
return response return response
return inner_func return wraps(func)(inner_func)
return decorator return decorator
def vary_on_cookie(func): def vary_on_cookie(func):
@ -32,4 +37,4 @@ def vary_on_cookie(func):
response = func(*args, **kwargs) response = func(*args, **kwargs)
patch_vary_headers(response, ('Cookie',)) patch_vary_headers(response, ('Cookie',))
return response return response
return inner_func return wraps(func)(inner_func)

View File

@ -784,9 +784,17 @@ you can use the name of the model, rather than the model object itself::
class Manufacturer(models.Model): class Manufacturer(models.Model):
# ... # ...
Note, however, that you can only use strings to refer to models in the same Note, however, that this only refers to models in the same models.py file -- you
models.py file -- you cannot use a string to reference a model in a different cannot use a string to reference a model defined in another application or
application, or to reference a model that has been imported from elsewhere. imported from elsewhere.
**New in Django development version:** to refer to models defined in another
application, you must instead explicitially specify the application label. That
is, if the ``Manufacturer`` model above is defined in another application called
``production``, you'd need to use::
class Car(models.Model):
manufacturer = models.ForeignKey('production.Manufacturer')
Behind the scenes, Django appends ``"_id"`` to the field name to create its Behind the scenes, Django appends ``"_id"`` to the field name to create its
database column name. In the above example, the database table for the ``Car`` database column name. In the above example, the database table for the ``Car``

View File

@ -61,13 +61,13 @@ _django_completion()
|| ||
# python manage.py, /some/path/python manage.py (if manage.py exists) # python manage.py, /some/path/python manage.py (if manage.py exists)
( ${COMP_CWORD} -eq 2 && ( ${COMP_CWORD} -eq 2 &&
( $( basename ${COMP_WORDS[0]} ) == python?([1-9]\.[0-9]) ) && ( $( basename -- ${COMP_WORDS[0]} ) == python?([1-9]\.[0-9]) ) &&
( $( basename ${COMP_WORDS[1]} ) == manage.py) && ( $( basename -- ${COMP_WORDS[1]} ) == manage.py) &&
( -r ${COMP_WORDS[1]} ) ) ( -r ${COMP_WORDS[1]} ) )
|| ||
( ${COMP_CWORD} -eq 2 && ( ${COMP_CWORD} -eq 2 &&
( $( basename ${COMP_WORDS[0]} ) == python?([1-9]\.[0-9]) ) && ( $( basename -- ${COMP_WORDS[0]} ) == python?([1-9]\.[0-9]) ) &&
( $( basename ${COMP_WORDS[1]} ) == django-admin.py) && ( $( basename -- ${COMP_WORDS[1]} ) == django-admin.py) &&
( -r ${COMP_WORDS[1]} ) ) ]] ; then ( -r ${COMP_WORDS[1]} ) ) ]] ; then
case ${cur} in case ${cur} in
@ -149,7 +149,7 @@ unset pythons
if command -v whereis &>/dev/null; then if command -v whereis &>/dev/null; then
python_interpreters=$(whereis python | cut -d " " -f 2-) python_interpreters=$(whereis python | cut -d " " -f 2-)
for python in $python_interpreters; do for python in $python_interpreters; do
pythons="${pythons} $(basename $python)" pythons="${pythons} $(basename -- $python)"
done done
pythons=$(echo $pythons | tr " " "\n" | sort -u | tr "\n" " ") pythons=$(echo $pythons | tr " " "\n" | sort -u | tr "\n" " ")
else else

View File

@ -1,18 +1,22 @@
""" """
24. Mutually referential many-to-one relationships 24. Mutually referential many-to-one relationships
To define a many-to-one relationship, use ``ForeignKey()`` . Strings can be used instead of model literals to set up "lazy" relations.
""" """
from django.db.models import * from django.db.models import *
class Parent(Model): class Parent(Model):
name = CharField(max_length=100, core=True) name = CharField(max_length=100, core=True)
# Use a simple string for forward declarations.
bestchild = ForeignKey("Child", null=True, related_name="favoured_by") bestchild = ForeignKey("Child", null=True, related_name="favoured_by")
class Child(Model): class Child(Model):
name = CharField(max_length=100) name = CharField(max_length=100)
parent = ForeignKey(Parent)
# You can also explicitally specify the related app.
parent = ForeignKey("mutually_referential.Parent")
__test__ = {'API_TESTS':""" __test__ = {'API_TESTS':"""
# Create a Parent # Create a Parent

View File

@ -0,0 +1,2 @@
# A models.py so that tests run.

View File

@ -0,0 +1,56 @@
from unittest import TestCase
from sys import version_info
from django.http import HttpResponse
from django.utils.functional import allow_lazy, lazy, memoize
from django.views.decorators.http import require_http_methods, require_GET, require_POST
from django.views.decorators.vary import vary_on_headers, vary_on_cookie
from django.views.decorators.cache import cache_page, never_cache, cache_control
from django.contrib.auth.decorators import login_required, permission_required, user_passes_test
from django.contrib.admin.views.decorators import staff_member_required
def fully_decorated(request):
"""Expected __doc__"""
return HttpResponse('<html><body>dummy</body></html>')
fully_decorated.anything = "Expected __dict__"
# django.views.decorators.http
fully_decorated = require_http_methods(["GET"])(fully_decorated)
fully_decorated = require_GET(fully_decorated)
fully_decorated = require_POST(fully_decorated)
# django.views.decorators.vary
fully_decorated = vary_on_headers('Accept-language')(fully_decorated)
fully_decorated = vary_on_cookie(fully_decorated)
# django.views.decorators.cache
fully_decorated = cache_page(60*15)(fully_decorated)
fully_decorated = cache_control(private=True)(fully_decorated)
fully_decorated = never_cache(fully_decorated)
# django.contrib.auth.decorators
fully_decorated = user_passes_test(lambda u:True)(fully_decorated)
fully_decorated = login_required(fully_decorated)
fully_decorated = permission_required('change_world')(fully_decorated)
# django.contrib.admin.views.decorators
fully_decorated = staff_member_required(fully_decorated)
# django.utils.functional
fully_decorated = memoize(fully_decorated, {}, 1)
fully_decorated = allow_lazy(fully_decorated)
fully_decorated = lazy(fully_decorated)
class DecoratorsTest(TestCase):
def test_attributes(self):
"""
Tests that django decorators set certain attributes of the wrapped
function.
"""
# Only check __name__ on Python 2.4 or later since __name__ can't be
# assigned to in earlier Python versions.
if version_info[0] >= 2 and version_info[1] >= 4:
self.assertEquals(fully_decorated.__name__, 'fully_decorated')
self.assertEquals(fully_decorated.__doc__, 'Expected __doc__')
self.assertEquals(fully_decorated.__dict__['anything'], 'Expected __dict__')