Index: django/views/decorators/http.py
===================================================================
--- django/views/decorators/http.py	(revision 6454)
+++ django/views/decorators/http.py	(working copy)
@@ -3,6 +3,7 @@
 """
 
 from django.utils.decorators import decorator_from_middleware
+from django.utils.functional import wraps
 from django.middleware.http import ConditionalGetMiddleware
 from django.http import HttpResponseNotAllowed
 
@@ -24,7 +25,7 @@
             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"])
Index: django/views/decorators/vary.py
===================================================================
--- django/views/decorators/vary.py	(revision 6454)
+++ django/views/decorators/vary.py	(working copy)
@@ -1,4 +1,5 @@
 from django.utils.cache import patch_vary_headers
+from django.utils.functional import wraps
 
 def vary_on_headers(*headers):
     """
@@ -16,7 +17,7 @@
             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 +33,4 @@
         response = func(*args, **kwargs)
         patch_vary_headers(response, ('Cookie',))
         return response
-    return inner_func
+    return wraps(func)(inner_func)
Index: django/views/decorators/cache.py
===================================================================
--- django/views/decorators/cache.py	(revision 6454)
+++ django/views/decorators/cache.py	(working copy)
@@ -13,6 +13,7 @@
 
 from django.utils.decorators import decorator_from_middleware
 from django.utils.cache import patch_cache_control, add_never_cache_headers
+from django.utils.functional import wraps
 from django.middleware.cache import CacheMiddleware
 
 cache_page = decorator_from_middleware(CacheMiddleware)
@@ -26,7 +27,7 @@
             patch_cache_control(response, **kwargs)
             return response
 
-        return _cache_controlled
+        return wraps(viewfunc)(_cache_controlled)
 
     return _cache_controller
 
@@ -39,4 +40,4 @@
         response = view_func(request, *args, **kwargs)
         add_never_cache_headers(response)
         return response
-    return _wrapped_view_func
+    return wraps(view_func)(_wrapped_view_func)
Index: django/utils/functional.py
===================================================================
--- django/utils/functional.py	(revision 6454)
+++ django/utils/functional.py	(working copy)
@@ -3,6 +3,53 @@
         return _curried_func(*(args+moreargs), **dict(kwargs, **morekwargs))
     return _curried
 
+#Begin from Python 2.5 functools.py =================================
+#N.B. swapping ``partial`` for ``curry``
+# to maintain backwards-compatibility in Django.
+
+# 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:
+        setattr(wrapper, attr, getattr(wrapped, attr))
+    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
Index: django/utils/decorators.py
===================================================================
--- django/utils/decorators.py	(revision 6454)
+++ django/utils/decorators.py	(working copy)
@@ -1,6 +1,7 @@
 "Functions that help with dynamically creating decorators for views."
 
 import types
+from django.utils.functional import wraps
 
 def decorator_from_middleware(middleware_class):
     """
@@ -53,5 +54,5 @@
                 if result is not None:
                     return result
             return response
-        return _wrapped_view
+        return wraps(view_func)(_wrapped_view)
     return _decorator_from_middleware
Index: django/contrib/admin/views/decorators.py
===================================================================
--- django/contrib/admin/views/decorators.py	(revision 6454)
+++ django/contrib/admin/views/decorators.py	(working copy)
@@ -4,6 +4,7 @@
 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.functional import wraps
 import base64, datetime, md5
 import cPickle as pickle
 
@@ -103,4 +104,4 @@
             else:
                 return _display_login_form(request, ERROR_MESSAGE)
 
-    return _checklogin
+    return wraps(view_func)(_checklogin)
\ No newline at end of file
Index: django/contrib/auth/decorators.py
===================================================================
--- django/contrib/auth/decorators.py	(revision 6454)
+++ django/contrib/auth/decorators.py	(working copy)
@@ -1,6 +1,7 @@
 from django.contrib.auth import REDIRECT_FIELD_NAME
 from django.http import HttpResponseRedirect
 from django.utils.http import urlquote
+from django.utils.functional import wraps
 
 def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME):
     """
@@ -19,7 +20,7 @@
         _checklogin.__doc__ = view_func.__doc__
         _checklogin.__dict__ = view_func.__dict__
 
-        return _checklogin
+        return wraps(view_func)(_checklogin)
     return _dec
 
 def login_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME):
Index: tests/regressiontests/decorators/__init__.py
===================================================================
Index: tests/regressiontests/decorators/tests.py
===================================================================
--- tests/regressiontests/decorators/tests.py	(revision 0)
+++ tests/regressiontests/decorators/tests.py	(revision 0)
@@ -0,0 +1,36 @@
+"""
+>>> fully_decorated.__module__
+'regressiontests.decorators.tests'
+>>> fully_decorated.__name__
+'fully_decorated'
+>>> fully_decorated.__doc__
+'Expected __doc__'
+>>> fully_decorated.__dict__['anything']
+'Expected __dict__'
+"""
+from django.views.decorators.http import require_http_methods
+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 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__"
+
+fully_decorated = require_http_methods(["GET"])(fully_decorated)
+
+fully_decorated = vary_on_headers('Accept-language')(fully_decorated)
+fully_decorated = vary_on_cookie(fully_decorated)
+
+fully_decorated = cache_page(60*15)(fully_decorated)
+fully_decorated = cache_control(private=True)(fully_decorated)
+fully_decorated = never_cache(fully_decorated)
+
+fully_decorated = user_passes_test(lambda u:True)(fully_decorated)
+fully_decorated = staff_member_required(fully_decorated)
+
+if __name__ == "__main__":
+    import doctest
+    doctest.testmod()
Index: tests/regressiontests/decorators/models.py
===================================================================
Index: tests/regressiontests/views/views.py
===================================================================
--- tests/regressiontests/views/views.py	(revision 6454)
+++ tests/regressiontests/views/views.py	(working copy)
@@ -4,4 +4,4 @@
 def index_page(request):
     """Dummy index page"""
     return HttpResponse('<html><body>Dummy page</body></html>')
-
+    
