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.
Related
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)
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.
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 have a class that needs auxiliary functions, e.g. one to calculate a checksum that just uses the passed in arguments and not any properties of the class. This function is only called by the class's methods. So I dont need to pass in the 'self' as the first formal of the function.
How should I implement these functions? Can I have non-method functions in a class? Should I define them outside the class (even though they are not used by anything else)? Or is it ok for them to be regular methods?
If you want a function inside a class that doesn't take self as an argument, use the #staticmethod decorator:
class Awesomeness(object):
def method(self, *args):
pass
#staticmethod
def another_method(*args):
pass
However, from a conceptual standpoint I would definitely consider putting it at module scope, especially if it's a checksum function that doesn't use instance or class properties.
Just do a nested function:
class Foo(object):
def bar(self, arg):
def inner(arg):
print 'Yo Adrian imma in inner with {}!'.format(arg)
inner(arg)
Foo().bar('argument')
Or just ignore the self:
class Foo(object):
def prive(_, arg):
print 'In prive with {}!'.format(arg)
def bar(self, arg):
def inner(arg):
print 'Yo Adrian imma in inner with {}!'.format(arg)
inner(arg)
self.prive(arg)
def foo(self,arg):
self.prive(arg)
Foo().bar('argument')
Foo().foo('another argument')
Second example prints:
Yo Adrian imma in inner with argument!
In prive with argument!
In prive with another argument!
tldr define a function outside the class
class myclass():
def __init__(self):
myfunc()
def myfunc():
print('f')
myclass() # prints f
from best to worst:
define outside the class
use staticmethod decorator
define a method, but ignore self
the disadvantage of 2 & 3 is that the "function" is still a method as it can applied to an object the regular way: self.myfunc()
I am trying to use generic CreateView class to handle forms for a set of models inherited from the same base class.
class BaseContent(models.Model):
...
class XContent(BaseContent):
...
class YContent(BaseContent):
...
To keep things DRY, I want to define one CreateView class that will handle all inherited classes from BaseContent.
The url pattern for that view is:
url(r'^content/add/(?P<model_name>\w+)/$', ContentCreateView.as_view(), name='content_add')
Something like this should work:
class ContentCreateView(CreateView):
template_name = 'content_form.html'
def get_model(self, request):
# 'content' is the name of the application; model_name is 'xcontent', 'ycontent', ...
return ContentType.objects.get_by_natural_key('content', self.model_name)
But I am getting this exception:
ContentCreateView is missing a queryset. Define ContentCreateView.model, ContentCreateView.queryset, or override ContentCreateView.get_object().
This suggestion does not seem to hold as I am not willing to set a class attribute like model or queryset to keep the model form generated dynamic. Overriding the get_object does not seem relevant for creating an object.
I tried overriding get_queryset() but this method does not accept the request parameter, nor have access to self.model_name which comes from the url pattern.
Long story short, how can I make a CreateView use a dynamic form based on a parameter passed from the url?
Thanks.
You could set the model attribute from your your urls.py, depending on the url being called:
url(r'^content/add/x/$',
ContentCreateView.as_view(model=XContent), name='x_content_add'),
url(r'^content/add/y/$',
ContentCreateView.as_view(model=YContent), name='y_content_add')
I admit it's not perfect as you are repeating yourself a bit, but therefore you have the advantage of having different names for the same view, depending on the model! Besides that you could also do something similar with overriding form_class...
Had this problem for some time, but found the solution. You need to override the dispatch method, defined in as_view() (django.views.generic.base), something like this:
class ContentCreateView(CreateView):
def dispatch(self, request, *args, **kwargs):
for app in ['foo', 'bar']:
model = models.get_model(app, kwargs['modelname'])
if model:
self.model = model
break
return super(GenericEdit, self).dispatch(request, *args, **kwargs)
...
...