Django

Code

Ticket #1015 (closed: fixed)

Opened 3 years ago

Last modified 2 years ago

django.utils.decorators.decorator_from_middleware doesn't work with parameters

Reported by: eugene@lazutkin.com Assigned to: adrian
Milestone: Component: Tools
Version: Keywords: cache_page
Cc: Triage Stage: Accepted
Has patch: 1 Needs documentation: 0
Needs tests: 1 Patch needs improvement: 0

Description

django.utils.decorators.decorator_from_middleware doesn't work for decorators with parameters:

  1. Any parameter of decorator causes a call to _decorator_from_middleware().
  2. Instead of actual wrapping _wrapped_view() is called, which fails because at that point request is actually a view function.

Basically this works:

@cache_page
def my_cool_view(request):
    # cool processing

This doesn't:

@cache_page(60)
def my_cool_view(request):
    # cool processing

Nor this:

@cache_page(cache_timeout=60)
def my_cool_view(request):
    # cool processing

Just try it.

If this is an intended behavior, we need to update documentation for @cache_page. (I didn't check if there is a documentation for @gzip_page and @conditional_page).

It makes sense to document that per-page caching still uses CACHE_MIDDLEWARE_SECONDS and CACHE_MIDDLEWARE_KEY_PREFIX, if no decorator arguments were given. And currently it is impossible to specify them.

Attachments

middleware_decorators.diff (0.6 kB) - added by anonymous on 02/14/07 03:11:30.
Usage is in the comments
1015.diff (4.1 kB) - added by Gary Wilson <gary.wilson@gmail.com> on 04/14/07 01:36:14.

Change History

12/12/05 14:32:52 changed by eugene@lazutkin.com

Duplicate of #1047.

01/18/07 02:31:00 changed by Simon G. <dev@simon.net.nz>

  • stage changed from Unreviewed to Accepted.

02/12/07 09:07:17 changed by e.karpachov@gmail.com

Possible solution, keeping backward compatibility:

--- django/utils/decorators.py  (revision 4490)
+++ django/utils/decorators.py  (working copy)
@@ -30,4 +30,12 @@
                     return result
             return response
         return _wrapped_view
+
+    def _true_24_decorator(*args, **kwargs):
+        def _decorator(f):
+            return _decorator_from_middleware(f, *args, **kwargs)
+        return _decorator
+
+    _decorator_from_middleware.decorator = _true_24_decorator
+
     return _decorator_from_middleware

Usage:

@cache_page.decorator(60)
def my_cool_view(request):
    # cool processing

If it's ok, then feel free to rename the "decorator" attribute to something nice and short enough to type.

02/14/07 03:11:30 changed by anonymous

  • attachment middleware_decorators.diff added.

Usage is in the comments

02/14/07 03:20:55 changed by anonymous

  • needs_better_patch set to 1.
  • has_patch set to 1.
  • component changed from Documentation to Uncategorized.
  • needs_tests set to 1.

04/08/07 12:50:51 changed by Gary Wilson <gary.wilson@gmail.com>

#2567 and #3967 marked as duplicates.

04/08/07 12:51:46 changed by Gary Wilson <gary.wilson@gmail.com>

  • keywords set to cache_page.
  • owner changed from jacob to adrian.
  • component changed from Uncategorized to Tools.

04/14/07 01:35:12 changed by Gary Wilson <gary.wilson@gmail.com>

Decorators that take arguments need to be functions that return a function that returns a decorator. See these docs for info:

From PEP 318:

The current syntax also allows decorator declarations to call a function that returns a decorator:

@decomaker(argA, argB, ...)
def func(arg1, arg2, ...):
    pass

This is equivalent to:

func = decomaker(argA, argB, ...)(func)

The cache_page decorator appears to be the only one using decorator_from_middleware that also takes arguments, but decorator_from_middleware does not handle decorators that take arguments.

Also the cache documentation is incorrect, because instead of:

slashdot_this = cache_page(slashdot_this, 60 * 15)

it should be

slashdot_this = cache_page(60 * 15)(slashdot_this)

04/14/07 01:36:14 changed by Gary Wilson <gary.wilson@gmail.com>

  • attachment 1015.diff added.

04/14/07 01:42:27 changed by Gary Wilson <gary.wilson@gmail.com>

Description of attached patch:

  • Added a decorator_with_arguments_from_middleware function for generating functions that generate decorators from middleware classes whose constructors take arguments.
  • Factored out the inner decorator function from in decorator_from_middleware into a make_decorator_from_middleware function that generates decorators from middleware classes.
  • Changed the cache_page decorator to use decorator_with_arguments_from_middleware since it takes parameters.
  • Added CACHE_MIDDLEWARE_SECONDS to the global settings. You will get an error when using cache_page without this setting since it is using the CacheMiddleware, which uses CACHE_MIDDLEWARE_SECONDS.
  • Documented the fact that cache_page can take more than one argument.

04/14/07 01:45:19 changed by Gary Wilson <gary.wilson@gmail.com>

  • needs_better_patch deleted.

I should note that the patch is backwards incompatible with people using the cache_page decorator as documented:

slashdot_this = cache_page(slashdot_this, 60 * 15)

06/22/07 03:08:31 changed by mtredinnick

Would be nice to avoid the backwards incompatibility if we could. A new name for cache_page would be one way (then cache_page() would work as it does now and the new named function would be the new decorator).

Most of the rest looks great. Will probably give decorator_with_arguments_from_middleware a shorter name to avoid inflicting RSI on the world's population, but that's only cosmetic.

07/05/07 06:08:41 changed by mtredinnick

(In [5618]) Added CACHE_MIDDLEWARE_SECONDS to global settings and documentation (it's used by the cache middleware). Refs #1015.

07/05/07 06:10:27 changed by mtredinnick

  • status changed from new to closed.
  • resolution set to fixed.

(In [5619]) Fixed #1015 -- Fixed decorator_from_middleware to return a real decorator even when arguments are given. This looks a bit ugly, but it's fully backwards compatible and all the extra work is done at import time, so it shouldn't have any real performance impact.


Add/Change #1015 (django.utils.decorators.decorator_from_middleware doesn't work with parameters)




Change Properties
Action