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.conf import settings
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.utils.translation import ugettext_lazy, ugettext as _
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.")
LOGIN_FORM_KEY = 'this_is_the_login_form'
@ -104,4 +110,4 @@ def staff_member_required(view_func):
else:
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.http import HttpResponseRedirect
from django.utils.http import urlquote
@ -51,7 +56,7 @@ class _CheckLogin(object):
self.test_func = test_func
self.login_url = login_url
self.redirect_field_name = redirect_field_name
self.__name__ = view_func.__name__
update_wrapper(self, view_func)
def __get__(self, obj, cls=None):
view_func = self.view_func.__get__(obj, cls)

View File

@ -109,7 +109,8 @@ class BaseHandler(object):
except exceptions.PermissionDenied:
return http.HttpResponseForbidden('<h1>Permission denied</h1>')
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.
# Get the exception info now, in case another exception is thrown later.
exc_info = sys.exc_info()

View File

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

View File

@ -23,26 +23,64 @@ RECURSIVE_RELATIONSHIP_CONSTANT = 'self'
pending_lookups = {}
def add_lookup(rel_cls, field):
name = field.rel.to
module = rel_cls.__module__
key = (module, name)
# Has the model already been loaded?
# If so, resolve the string reference right away
model = get_model(rel_cls._meta.app_label, field.rel.to, False)
def add_lazy_relation(cls, field, relation):
"""
Adds a lookup on ``cls`` when a related field is defined using a string,
i.e.::
class MyModel(Model):
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:
field.rel.to = model
field.do_related_class(model, rel_cls)
field.do_related_class(model, cls)
else:
# Mark the related field for later lookup
pending_lookups.setdefault(key, []).append((rel_cls, field))
key = (app_label, model_name)
value = (cls, field)
pending_lookups.setdefault(key, []).append(value)
def do_pending_lookups(sender):
other_cls = sender
key = (other_cls.__module__, other_cls.__name__)
for rel_cls, field in pending_lookups.setdefault(key, []):
field.rel.to = other_cls
field.do_related_class(other_cls, rel_cls)
"""
Handle any pending relations to the sending model. Sent from class_prepared.
"""
key = (sender._meta.app_label, sender.__name__)
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)
@ -66,9 +104,7 @@ class RelatedField(object):
sup.contribute_to_class(cls, name)
other = self.rel.to
if isinstance(other, basestring):
if other == RECURSIVE_RELATIONSHIP_CONSTANT:
self.rel.to = cls.__name__
add_lookup(cls, self)
add_lazy_relation(cls, self, other)
else:
self.do_related_class(other, cls)

View File

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

View File

@ -2,6 +2,10 @@
import re
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.conf import settings
@ -35,7 +39,7 @@ def stringfilter(func):
for attr in ('is_safe', 'needs_autoescape'):
if hasattr(func, attr):
setattr(_dec, attr, getattr(func, attr))
return _dec
return wraps(func)(_dec)
###################
# STRINGS #

View File

@ -1,6 +1,10 @@
"Functions that help with dynamically creating decorators for views."
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):
"""
@ -53,5 +57,5 @@ def decorator_from_middleware(middleware_class):
if result is not None:
return result
return response
return _wrapped_view
return wraps(view_func)(_wrapped_view)
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 _curried(*moreargs, **morekwargs):
return _curried_func(*(args+moreargs), **dict(kwargs, **morekwargs))
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):
"""
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)
cache[mem_args] = result
return result
return wrapper
return wraps(func)(wrapper)
class Promise(object):
"""
@ -110,7 +222,7 @@ def lazy(func, *resultclasses):
# Creates the proxy object, instead of the actual value.
return __proxy__(args, kw)
return __wrapper__
return wraps(func)(__wrapper__)
def allow_lazy(func, *resultclasses):
"""
@ -126,4 +238,4 @@ def allow_lazy(func, *resultclasses):
else:
return func(*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.
"""
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.cache import patch_cache_control, add_never_cache_headers
from django.middleware.cache import CacheMiddleware
@ -26,7 +31,7 @@ def cache_control(**kwargs):
patch_cache_control(response, **kwargs)
return response
return _cache_controlled
return wraps(viewfunc)(_cache_controlled)
return _cache_controller
@ -39,4 +44,4 @@ def never_cache(view_func):
response = view_func(request, *args, **kwargs)
add_never_cache_headers(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.
"""
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.middleware.http import ConditionalGetMiddleware
from django.http import HttpResponseNotAllowed
@ -24,7 +29,7 @@ def require_http_methods(request_method_list):
if request.method not in request_method_list:
return HttpResponseNotAllowed(request_method_list)
return func(request, *args, **kwargs)
return inner
return wraps(func)(inner)
return decorator
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
def vary_on_headers(*headers):
@ -16,7 +21,7 @@ def vary_on_headers(*headers):
response = func(*args, **kwargs)
patch_vary_headers(response, headers)
return response
return inner_func
return wraps(func)(inner_func)
return decorator
def vary_on_cookie(func):
@ -32,4 +37,4 @@ def vary_on_cookie(func):
response = func(*args, **kwargs)
patch_vary_headers(response, ('Cookie',))
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):
# ...
Note, however, that you can only use strings to refer to models in the same
models.py file -- you cannot use a string to reference a model in a different
application, or to reference a model that has been imported from elsewhere.
Note, however, that this only refers to models in the same models.py file -- you
cannot use a string to reference a model defined in another application or
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
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)
( ${COMP_CWORD} -eq 2 &&
( $( basename ${COMP_WORDS[0]} ) == python?([1-9]\.[0-9]) ) &&
( $( basename ${COMP_WORDS[1]} ) == manage.py) &&
( $( basename -- ${COMP_WORDS[0]} ) == python?([1-9]\.[0-9]) ) &&
( $( basename -- ${COMP_WORDS[1]} ) == manage.py) &&
( -r ${COMP_WORDS[1]} ) )
||
( ${COMP_CWORD} -eq 2 &&
( $( basename ${COMP_WORDS[0]} ) == python?([1-9]\.[0-9]) ) &&
( $( basename ${COMP_WORDS[1]} ) == django-admin.py) &&
( $( basename -- ${COMP_WORDS[0]} ) == python?([1-9]\.[0-9]) ) &&
( $( basename -- ${COMP_WORDS[1]} ) == django-admin.py) &&
( -r ${COMP_WORDS[1]} ) ) ]] ; then
case ${cur} in
@ -149,7 +149,7 @@ unset pythons
if command -v whereis &>/dev/null; then
python_interpreters=$(whereis python | cut -d " " -f 2-)
for python in $python_interpreters; do
pythons="${pythons} $(basename $python)"
pythons="${pythons} $(basename -- $python)"
done
pythons=$(echo $pythons | tr " " "\n" | sort -u | tr "\n" " ")
else

View File

@ -1,18 +1,22 @@
"""
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 *
class Parent(Model):
name = CharField(max_length=100, core=True)
# Use a simple string for forward declarations.
bestchild = ForeignKey("Child", null=True, related_name="favoured_by")
class Child(Model):
name = CharField(max_length=100)
parent = ForeignKey(Parent)
# You can also explicitally specify the related app.
parent = ForeignKey("mutually_referential.Parent")
__test__ = {'API_TESTS':"""
# 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__')