I'm using django class-based views. Suppose there is a ListView like this:
#method_decorator(ensure_csrf_cookie, name='dispatch')
class SomeView(ListView):
...
If another class-based view inherits SomeView, Does it inherit the "ensure_csrf_cookie" too? Or it has to be defined on every subclasses explicitly?
The "#decorator" syntax is just syntactic sugar that transforms this:
#decorator
class SomeClass(parent):
pass
into this:
class SomeClass(parent):
pass
SomeClass = decorator(SomeClass)
IOW, whatever decorator do is done after the class is created, so as a general rule, you cannot count on it being inherited by child classes of SomeClass - whether "what the decorator did" will actually be inherited (or not) really depends on both "what the decorator did" AND the child class definition.
wrt/ your concrete use case: method_decorator is used to decorate a given method of your class (the dispatch method in your example). If your child class doesn't override this method, then it will be looked up on the parent class. In this case, you will indeed end up using the decorated method. But if you override the decorated method in your subclass, the new method will be used instead of the parent's one, so it will not be automagically decorated and you'll have to apply the decorator again.
FWIW, it's quite easy to test by yourself:
>>> def decorator(func):
... def wrapper(*args, **kw):
... print("before %s(%s, %s)" % (func, args, kw)
... )
... return func(*args, **kw)
... return wrapper
...
>>> from django.utils.decorators import method_decorator
>>> #method_decorator(decorator, name='foo')
... class Bar(object):
... def foo(self):
... print("%s.foo()" % self)
...
>>> b = Bar()
>>> b.foo()
before <function bound_func at 0x7fefab044050>((), {})
<Bar object at 0x7fefab09af10>.foo()
>>> class Quux(Bar): pass
...
>>> q = Quux()
>>> q.foo()
before <function bound_func at 0x7fefab044050>((), {})
<Quux object at 0x7fefab041110>.foo()
>>> class Baaz(Bar):
... def foo(self):
... print("this is Baaz.foo")
...
>>> bz = Baaz()
>>> bz.foo()
this is Baaz.foo
>>>
It has to be defined on every subclasses explicitly. Because method_decorator is just function and do some calculations(it is dependent on how you write you decorator) then it will call your class SomeView. If you inherit the SomeView then you have to use the method_decorator explicitly for that new class. It might help.
Related
I am trying to test DRF endpoints, and trying to add mixings dynamically to a test in order to execute tests again each method allowed in the endpoint (get, post, put, patch, delete)
So, my idea is to make a base test class that will automatically add some mixings to test endpoints if they are allowed. And I can create the actual test that will inherit from this base class.
The code:
from rest_framework.test import APITestCase
class GetTestMixin:
def test_get_all(self):
response = self.client.get(self.uri)
self.assertEqual(response.status_code,status.HTTP_200_OK)
class AutoMixinMeta(type):
def __call__(cls, *args, **kwargs):
allowed_methods = ['get', 'post']
# cls would be the Test class, for example TestExample
# cls.__bases__ is a tuple with the classes inherited in the Test class, for example:
# (<class 'unit_tests.endpoints.base_test.RESTTestCase'>, <class 'rest_framework.test.APITestCase'>)
bases = cls.__bases__
for method in allowed_methods:
bases += (cls.method_mixins[method.lower()],)
# Create a new instance with all the mixins
cls = type(cls.__name__, bases, dict(cls.__dict__))
return type.__call__(cls, *args, **kwargs)
class RESTTestCase(metaclass=AutoMixinMeta):
uri = None
method_mixins = {
'post': PostTestMixin,
'get': GetTestMixin,
}
class TestExample(RESTTestCase, APITestCase):
uri = reverse('somemodel-list')
I was expecting test_get_all to be executed, but it is not.
Mixings are in place. I made a dummy method inside TestExample and put a debugger in place, and checked it, like this:
(Pdb) self.__class__
<class 'TestExample'>
(Pdb) self.__class__.__bases__
(<class 'RESTTestCase'>, <class 'rest_framework.test.APITestCase'>, <class 'GetTestMixin'>)
The problem there is that the code that collects the classes to be tested will never "see" the class as an instance of the Test classes, or as a subclass of: the class inheriting from the test cases only exist when an instance is created.
The only way for this to work is to create the derived classes at import time, and to bind the desired dynamic classes as top-level names on the module.
To do that, you can do away with the metaclass, and just place the statements in the module body, assigning the new class or classes to names using globals(). Or, if you want just the subclasses, rather than at module top level, the code can be placed in the __init_subclass__ method. This methd is called when the class is created, not when it is instantiated, and it should work.
from rest_framework.test import APITestCase
class GetTestMixin:
def test_get_all(self):
response = self.client.get(self.uri)
self.assertEqual(response.status_code,status.HTTP_200_OK)
class RESTTestCase():
uri = None
method_mixins = {
'post': PostTestMixin,
'get': GetTestMixin,
}
def __init_subclass__(cls, *args, **kw):
super.__init_subclass__(*args, **kw)
if "Dynamic" in cls.__name__:
return
allowed_methods = ['get', 'post']
bases = list(cls.__bases__)
for method in allowed_methods:
bases.append(cls.method_mixins[method.lower()])
# Create a new instance with all the mixins
new_cls = type(cls.__name__ + "Dynamic", bases, dict(cls.__dict__))
globals()[new_cls.__name__] = new_cls
class TestExample(RESTTestCase, APITestCase):
uri = reverse('somemodel-list')
# class TestExampleDynamic is created automatically when the `class` statement above resolves
I want to enforce childrens to use a classmethod in python2.7.
I tried this:
import abc
class Base(object):
__metaclass__ = abc.ABCMeta
#abc.abstractproperty
def value(self):
pass
#abc.abstractproperty
#classmethod
def text(cls):
pass
class Imp(Base):
TEXT = "hi im text"
#classmethod
def haba(cls):
print 'HI'
#property
def value(self):
return 'asdasd'
#classmethod
#property
def text(cls):
return 'ho ho p'
print Imp.text
print Imp.TEXT
But I'm getting this output:
<bound method ABCMeta.? of <class 'main.Imp'>>
hi im text
How I can properly enforce childrens to implement classmethod properties?
You can see that Imp.TEXT is working but there is no way to enforce creating this member from base class this way
After re-reading your question a few times I concluded that you want the cl method to behave as if it is a property for the class.
First, Python's implementation of abstract method/property checking is meant to be performed at instantiation time only, not at class declaration. I hope you are aware of that.
Second, Python's descriptor protocol allows for the creation of the equivalent of "class properties", although there is no higher level support for that on the language itself - you can create a class which __get__ method returns your calculated property when the instance argument is None (usually descriptors will return 'self' so that they can be retrieved from the class).
Finally - it is possible that by declaring a custom metaclass being abstract itself, and then declaring it as your class metaclass, abstractproperties will trigger in runtime - let's try that - :
In [1]: import abc
In [2]: class AbsPropertyMeta(abc.ABC, type):
...: #abc.abstractproperty
...: def cl(cls):
...: return "Ho ho ho"
...:
In [3]: class ConcreteExample(metaclass=AbsPropertyMeta):
...: pass
...:
(Note that I will develop the answer using Python 3, which should be what you should be using in any new project or for learning purposes as well)
So, as for the former example, the property in the metaclass does work as a "class property", but Python does not enforce its redefinition in the class body.
So, if you really need this design, you should create a complete custom metaclass for that, and let go of the abc.ABCMeta mechanisms at all:
from functools import partial
def abstractclassproperty(func):
func._abstract_property = True
return func
class clsproperty(object):
def __init__(self, func):
self.func = func
def __get__(self, instance, owner):
return self.func(owner)
class ABCAbstractClsProperty(type):
def __new__(mcls, name, bases, namespace, **kw):
new_cls = super(ABCAbstractClsProperty, mcls).__new__(mcls, name, bases, namespace, **kw)
for attr_name in dir(new_cls): # Dir retrieves attributes from all superclasses
attr = getattr(new_cls, attr_name)
if getattr(attr, "im_func", None): # Python 2 specific normalization.
attr = attr.im_func
if getattr(attr, '_abstract_property', False) and new_cls.__dict__.get(attr_name) is not attr:
raise TypeError("Can't create class {!r}: abstract property {!r} not implemented".format(name, attr_name))
return new_cls
""" # Python 3:
class A(metaclass=ABCAbstractClsProperty):
#abstractclassproperty
def cl(cls):
pass
"""
class A(object):
__metaclass__ = ABCAbstractClsProperty
#abstractclassproperty
def cl(cls):
pass
try:
class B(A):
pass
except TypeError:
print("Check ok")
class C(A):
#clsproperty
def cl(cls):
return "ho ho ho " + cls.__name__
print(C.cl)
Referencece to response_class in Django's code:
django/base.py
class TemplateResponseMixin:
"""A mixin that can be used to render a template."""
response_class = TemplateResponse
def render_to_response(self, context, **response_kwargs):
response_kwargs.setdefault('content_type', self.content_type)
#here
return self.response_class(
#here
request=self.request,
template=self.get_template_names(),
context=context,
using=self.template_engine,
**response_kwargs
)
The class attribute setting response_class = TemplateResponse,
while call it through instance's attribute self.response_class,
I guess it might be super().response_class
How to understand it?
You need to use super() when calling superclass's method. But in case of response_class it's just attribute defined inside TemplateResponseMixin so you can simple accessed it through self.response_class. Since response_class is Class to instancinate you need to add () like this: self.response_class(*args, **kwargs).
You can check this question to get more details about super().
Example:
class A:
def method_a(self):
pass
class B(A):
some_class = SomeClass
def method_a(self):
super().method_a() # This code will find A's method_a and call it
self.some_class() # This will only instancinate some_class attribute of current B's instance
I'm running python 2.7, 3.4 and 3.5. Only 2.7 raises a TypeError with the following code. I'm wondering if I'm doing something wrong, is this a know bug or is it something else?
from abc import ABCMeta, abstractmethod
class Base(object):
__metaclass__ = ABCMeta
#abstractmethod
def bar(self):
pass
class Concrete(Base):
pass
confused = Concrete()
In Python 2.7 I get the following (helpful) error:
Traceback (most recent call last):
File "confused.py", line 16, in <module>
confused = Concrete()
TypeError: Can't instantiate abstract class Concrete with abstract methods bar
But in Python3.x it runs without an error (bad). Thanks.
Declaring an abstract base class changed in python3 to:
import abc
class Base(metaclass=abc.ABCMeta):
#abc.abstractmethod
def bar(self):
pass
class Concrete(Base):
pass
Concrete() # Will raise a TypeError
They behave differently in Python2.x and Python3.x .
Python3.6
# importing abstract base classes module
import abc
class GetterSetter(abc.ABC):
'''
ABSTRACT BASE CLASSES:
- An abstract base class is a kind of 'model' for other classes to be defined.
- It is not designed to construct instances, but can be subclassed by regular classes
- Abstract classes can define interface, or methods that must be implemented by its subclasses.
'''
# Abstract classes are not designed to be instantiated, only to be subclassed
# decorator for abstract class
#abc.abstractmethod
def set_val(self, input):
"""set the value in the instance"""
return
#abc.abstractmethod
def get_val(self):
"""Get and return a value from the instance..."""
return
# Inheriting from the above abstract class
class MyClass(GetterSetter):
# methods overriding in the GetterSetter
def set_val(self, input):
self.val = input
def get_val(self):
return self.val
# Instantiate
x = MyClass()
print(x) # prints the instance <__main__.MyClass object at 0x10218ee48>
x = GetterSetter() #throws error, abstract classes can't be instantiated
Python2.x
import abc
class GetterSetter(object):
# meta class is used to define other classes
__metaclass__ = abc.ABCMeta
# decorator for abstract class
#abc.abstractmethod
def set_val(self, input):
"""set the value in the instance"""
return
#abc.abstractmethod
def get_val(self):
"""Get and return a value from the instance..."""
return
# Inheriting from the above abstract class
class MyClass(GetterSetter):
# methods overriding in the GetterSetter
def set_val(self, input):
self.val = input
def get_val(self):
return self.val
# Instantiate
x = GetterSetter()
print(x)
x = GetterSetter() #throws error, abstract classes can't be instantiated
Check my answer here.
I'm overriding a ModelAdmin method thus:
def response_change(self, request, obj):
# alter redirect location if 'source' is found in GET
response = super(JobOptions, self).response_change(request, obj)
source = request.GET.get('source', None)
if source:
response['location'] = source
return response
Rather than repeat this on every model I'd like to make it a mixin.
If I do:
def RedirectMixin(admin.ModelAdmin)
and then:
def MyModel(admin.ModelAdmin, RedirectMixin)
then I get a MRO error.
However if RedirectMixin doesn't inherit from admin.ModelAdmin then the method doesn't get called.
An additional problem is how to generalise the super() call so it doesn't have the superclass hard-coded in.
Firstly, I presume you mean class rather than def in your examples.
Anyway, the right way to use a Mixin is to use it first in the list of classes to inherit. So:
class RedirectMixin(object):
and
class MyModelAdmin(RedirectMixin, admin.ModelAdmin):
This is because Python looks through all parent classes in order of declaration to find the method, and calls the first one it finds.
As for super, this shouldn't be mentioning the superclass at all - that's the whole point of it. It should reference the current class:
return super(MyModelAdmin, self).__init__(self, *args, **kwargs)
or whatever.
Edit after comments Yes, the mixin should refer to its own class in the super call. Consider the following:
In [1]: class BaseClass(object):
...: def my_function(self):
...: print 'base my_function'
...:
In [2]: class Mixin(object):
...: def my_function(self):
...: print 'mixin my_function'
...: super(Mixin, self).my_function()
...:
In [3]: class MyDerivedClass(Mixin, BaseClass):
...: pass
...:
Now if you instantiate the subclass and call its my_function method, the MRO will happen as you expect, even though Mixin doesn't inherit from BaseClass:
In [4]: m=MyDerivedClass()
In [5]: m.my_function()
mixin my_function
base my_function
The error you mention comes if you fail to make Mixin a descendant of object - if you don't, it's an old-style class, which doesn't support the use of super.