Fixed #30876 -- Moved classproperty() decorator to the django.utils.functional.

This commit is contained in:
André Ericson 2019-10-18 21:00:34 +02:00 committed by Mariusz Felisiak
parent 31174031f1
commit 3120490912
8 changed files with 56 additions and 55 deletions

View File

@ -1,7 +1,7 @@
from django.apps.registry import Apps from django.apps.registry import Apps
from django.db import models from django.db import models
from django.db.utils import DatabaseError from django.db.utils import DatabaseError
from django.utils.decorators import classproperty from django.utils.functional import classproperty
from django.utils.timezone import now from django.utils.timezone import now
from .exceptions import MigrationSchemaMissing from .exceptions import MigrationSchemaMissing

View File

@ -3,7 +3,7 @@ import unittest
from contextlib import contextmanager from contextlib import contextmanager
from django.test import LiveServerTestCase, tag from django.test import LiveServerTestCase, tag
from django.utils.decorators import classproperty from django.utils.functional import classproperty
from django.utils.module_loading import import_string from django.utils.module_loading import import_string
from django.utils.text import capfirst from django.utils.text import capfirst

View File

@ -37,7 +37,7 @@ from django.test.utils import (
CaptureQueriesContext, ContextList, compare_xml, modify_settings, CaptureQueriesContext, ContextList, compare_xml, modify_settings,
override_settings, override_settings,
) )
from django.utils.decorators import classproperty from django.utils.functional import classproperty
from django.views.static import serve from django.views.static import serve
__all__ = ('TestCase', 'TransactionTestCase', __all__ = ('TestCase', 'TransactionTestCase',

View File

@ -150,15 +150,3 @@ def make_middleware_decorator(middleware_class):
return _wrapped_view return _wrapped_view
return _decorator return _decorator
return _make_decorator return _make_decorator
class classproperty:
def __init__(self, method=None):
self.fget = method
def __get__(self, instance, cls=None):
return self.fget(cls)
def getter(self, method):
self.fget = method
return self

View File

@ -49,6 +49,18 @@ class cached_property:
return res return res
class classproperty:
def __init__(self, method=None):
self.fget = method
def __get__(self, instance, cls=None):
return self.fget(cls)
def getter(self, method):
self.fget = method
return self
class Promise: class Promise:
""" """
Base class for the proxy class created in the closure of the lazy function. Base class for the proxy class created in the closure of the lazy function.

View File

@ -263,6 +263,9 @@ Miscellaneous
``ETag`` header to responses with an empty ``ETag`` header to responses with an empty
:attr:`~django.http.HttpResponse.content`. :attr:`~django.http.HttpResponse.content`.
* ``django.utils.decorators.classproperty()`` decorator is moved to
``django.utils.functional.classproperty()``.
.. _deprecated-features-3.1: .. _deprecated-features-3.1:
Features deprecated in 3.1 Features deprecated in 3.1

View File

@ -2,7 +2,7 @@ from django.http import HttpResponse
from django.template import engines from django.template import engines
from django.template.response import TemplateResponse from django.template.response import TemplateResponse
from django.test import RequestFactory, SimpleTestCase from django.test import RequestFactory, SimpleTestCase
from django.utils.decorators import classproperty, decorator_from_middleware from django.utils.decorators import decorator_from_middleware
class ProcessViewMiddleware: class ProcessViewMiddleware:
@ -108,41 +108,3 @@ class DecoratorFromMiddlewareTests(SimpleTestCase):
self.assertTrue(getattr(request, 'process_response_reached', False)) self.assertTrue(getattr(request, 'process_response_reached', False))
# process_response saw the rendered content # process_response saw the rendered content
self.assertEqual(request.process_response_content, b"Hello world") self.assertEqual(request.process_response_content, b"Hello world")
class ClassPropertyTest(SimpleTestCase):
def test_getter(self):
class Foo:
foo_attr = 123
def __init__(self):
self.foo_attr = 456
@classproperty
def foo(cls):
return cls.foo_attr
class Bar:
bar = classproperty()
@bar.getter
def bar(cls):
return 123
self.assertEqual(Foo.foo, 123)
self.assertEqual(Foo().foo, 123)
self.assertEqual(Bar.bar, 123)
self.assertEqual(Bar().bar, 123)
def test_override_getter(self):
class Foo:
@classproperty
def foo(cls):
return 123
@foo.getter
def foo(cls):
return 456
self.assertEqual(Foo.foo, 456)
self.assertEqual(Foo().foo, 456)

View File

@ -1,7 +1,7 @@
from unittest import mock from unittest import mock
from django.test import SimpleTestCase from django.test import SimpleTestCase
from django.utils.functional import cached_property, lazy from django.utils.functional import cached_property, classproperty, lazy
class FunctionalTests(SimpleTestCase): class FunctionalTests(SimpleTestCase):
@ -218,3 +218,39 @@ class FunctionalTests(SimpleTestCase):
with mock.patch.object(__proxy__, '__prepare_class__') as mocked: with mock.patch.object(__proxy__, '__prepare_class__') as mocked:
lazified() lazified()
mocked.assert_not_called() mocked.assert_not_called()
def test_classproperty_getter(self):
class Foo:
foo_attr = 123
def __init__(self):
self.foo_attr = 456
@classproperty
def foo(cls):
return cls.foo_attr
class Bar:
bar = classproperty()
@bar.getter
def bar(cls):
return 123
self.assertEqual(Foo.foo, 123)
self.assertEqual(Foo().foo, 123)
self.assertEqual(Bar.bar, 123)
self.assertEqual(Bar().bar, 123)
def test_classproperty_override_getter(self):
class Foo:
@classproperty
def foo(cls):
return 123
@foo.getter
def foo(cls):
return 456
self.assertEqual(Foo.foo, 456)
self.assertEqual(Foo().foo, 456)