This might sound like a duplicate, but I don't think it is.
I need to do something a bit similar to what the asker did there : django model polymorphism with proxy inheritance
My parent needs to implement a set of methods, let's call them MethodA(), MethodB(). These methods will never be used directly, they will always be called through child models (but no, abstract class is not the way to go for various reasons).
But this is where it becomes trickier :
Each child model inherits from a specific module (moduleA, moduleB), they all implement the same method names but do something different. The calls are made through the parent model, and are redirected to the childs depending on the values of a field
Since I guess it's not very clear, here is some pseudo-code to help you understand
from ModuleA import CustomClassA
from ModuleB import CustomClassB
class ParentModel(models.Model):
TYPE_CHOICES = (
('ChildModelA', 'A'),
('ChildModelB', 'B'),
)
#some fields
type = models.CharField(max_length=1, choices=TYPE_CHOICES)
def __init__(self, *args, **kwargs):
super(ParentModel, self).__init__(*args, **kwargs)
if self.type:
self.__class__ = getattr(sys.modules[__name__], self.type)
def MethodA():
some_method()
def MethodB():
some_other_method()
class ChildModelA(ParentModel, CustomClassA):
class Meta:
proxy = True
class ChildModelB(ParentModel, CustomClassB):
class Meta:
proxy = True
In ModuleA :
class CustomClassA():
def some_method():
#stuff
def some_other_method():
#other stuff
In ModuleB :
class CustomClassB():
def some_method():
#stuff
def some_other_method():
#other stuff
Right now, the problem is that the class change works, but it does not inherit from ChildModelA or B.
Is this even possible? If yes, how can I make it work, and if no, how could I do this elegantly, without too much repetition?
A proxy model must inherit from exactly one non-abstract model class. It seems that both CustomClass and ParentModel are non-abstract. I would suggest to make CustomClass abstract since no attributes are defined.
This is explained in dept here: https://docs.djangoproject.com/en/3.2/topics/db/models/#proxy-models
Related
The gist of the question: if inheriting multiple classes how can I guarantee that if one class is inherited, a compliment Abstract Base Class (abc) is also used by the child object.
I've been messing around with pythons inheritance trying to see what kind of cool stuff I can do and I came up with this pattern, which is kind of interesting.
I've been trying to use this make implementing and testing objects that interface with my cache easier. I've got three modules:
ICachable.py
Cacheable.py
SomeClass.py
ICacheable.py
import abc
class ICacheable(abc.ABC):
#property
#abc.abstractmethod
def CacheItemIns(self):
return self.__CacheItemIns
#CacheItemIns.setter
#abc.abstractmethod
def CacheItemIns(self, value):
self.__CacheItemIns = value
return
#abc.abstractmethod
def Load(self):
"""docstring"""
return
#abc.abstractmethod
def _deserializeCacheItem(self):
"""docstring"""
return
#abc.abstractmethod
def _deserializeNonCacheItem(self):
"""docstring"""
return
Cacheable.py
class Cacheable:
def _getFromCache(self, itemName, cacheType,
cachePath=None):
"""docstring"""
kwargs = {"itemName" : itemName,
"cacheType" : cacheType,
"cachePath" : cachePath}
lstSearchResult = CacheManager.SearchCache(**kwargs)
if lstSearchResult[0]:
self.CacheItemIns = lstSearchResult[1]
self._deserializeCacheItem()
else:
cacheItem = CacheManager.NewItem(**kwargs)
self.CacheItemIns = cacheItem
self._deserializeNonCacheItem()
return
SomeClass.py
import ICacheable
import Cacheable
class SomeClass(Cacheable, ICacheable):
__valueFromCache1:str = ""
__valueFromCache2:str = ""
__CacheItemIns:dict = {}
#property
def CacheItemIns(self):
return self.__CacheItemIns
#CacheItemIns.setter
def CacheItemIns(self, value):
self.__CacheItemIns = value
return
def __init__(self, itemName, cacheType):
#Call Method from Cacheable
self.__valueFromCache1
self.__valueFromCache2
self.__getItemFromCache(itemName, cacheType)
return
def _deserializeCacheItem(self):
"""docstring"""
self.__valueFromCache1 = self.CacheItemIns["val1"]
self.__valueFromCache2 = self.CacheItemIns["val2"]
return
def _deserializeNonCacheItem(self):
"""docstring"""
self.__valueFromCache1 = #some external function
self.__valueFromCache2 = #some external function
return
So this example works, but the scary thing is that there is no gurantee that a class inherriting Cacheable also inherits ICacheable. Which seems like a design flaw, as Cacheable is useless on its own. However the ability to abstract things from my subclass/child class with this is powerful. Is there a way to guarantee Cacheable's dependency on ICacheable?
If you explicitly do not want inheritance, you can register classes as virtual subclasses of an ABC.
#ICacheable.register
class Cacheable:
...
That means every subclass of Cacheable is automatically treated as subclass of ICacheable as well. This is mostly useful if you have an efficient implementation that would be slowed down by having non-functional Abstract Base Classes to traverse, e.g. for super calls.
However, ABCs are not just Interfaces and it is fine to inherit from them. In fact, part of the benefit of ABC is that it enforces subclasses to implement all abstract methods. An intermediate helper class, such as Cacheable, is fine not to implement all methods when it is never instantiated. However, any non-virtual subclass that is instantiated must be concrete.
>>> class FailClass(Cacheable, ICacheable):
... ...
...
>>> FailClass()
TypeError: Can't instantiate abstract class FailClass with abstract methods CacheItemIns, Load, _deserializeCacheItem, _deserializeNonCacheItem
Note that if you
always subclass as class AnyClass(Cacheable, ICacheable):
never instantiate Cacheable
that is functionally equivalent to Cacheable inheriting from ICacheable. The Method Resolution Order (i.e. the inheritance diamond) is the same.
>>> AnyClass.__mro__
(__main__. AnyClass, __main__.Cacheable, __main__.ICacheable, abc.ABC, object)
I have a class based view that I need to inherit from another class if a site setting is set to true. I've done a fair bit of googling on python dynamic inheritance but haven't found anything that I could understand or that seemed appropriate. FYI: my python knowledge is far from extensive so this could be a very un-pythonic thing to do for all I know.
Here is some pseudocode to outline what I mean:
Class MyView(View):
If settings.IMPLEMENTS_ACTIVITY:
Set MyView implements activity
I hope that helps explain what I mean. Maybe someone could point me in the right direction?
class View1(View):
pass
class View2(View1):
def sepcific_view2_activity(self):
pass
if settings.IMPLEMENTS_ACTIVITY:
MyView = View1
else:
MyView = View2
I'd rather make a call to another class method than change inheritance:
Class MyView(View):
def get(request):
If settings.IMPLEMENTS_ACTIVITY:
return CoolView1().get(request)
else:
return CoolView2().get(request)
Class CoolView1(View):
def get(request):
return 1
Class CoolView2(View):
def get(request):
return 2
I have a series of models that look like this:
class Analysis(models.Model):
analysis_type = models.CharField(max_length=255)
def important_method(self):
...do stuff...
class SpecialAnalysis(Analysis):
class Meta:
proxy = True
def important_method(self):
...something different...
This is all pretty standard. However, what I'd like to do is automatically convert an Analysis model to a proxy model based on the value of the analysis_type field. For example, I'd like to be able to write code that looks like this:
>>> analysis = Analysis.objects.create(analysis_type="nothing_special")
>>> analysis.__class__
<class 'my_app.models.Analysis'>
>>> analysis = Analysis.objects.create(analysis_type="special")
>>> analysis.__class__
<class 'my_app.models.SpecialAnalysis'>
>>> analysis = Analysis.objects.get(pk=2)
>>> analysis.__class__
<class 'my_app.models.SpecialAnalysis'>
>>> # calls the ``important_method`` of the correct model
>>> for analysis in Analysis.objects.all():
... analysis.important_method()
Is this even remotely possible? A similar question was asked here, which actually gives some code for the iteration example, but it still leaves me with the question of how to get or create an instance of a proxy class from its parent. I suppose I could just override a bunch of manager methods, but I feel like there must be a more elegant way to do it.
I haven't found a "clean" or "elegant" way to do this. When I ran into this problem I solved it by cheating Python a little bit.
class Check(models.Model):
check_type = models.CharField(max_length=10, editable=False)
type = models.CharField(max_length=10, null=True, choices=TYPES)
method = models.CharField(max_length=25, choices=METHODS)
'More fields.'
def __init__(self, *args, **kwargs):
super(Check, self).__init__(*args, **kwargs)
if self.check_type:
map = {'TypeA': Check_A,
'TypeB': Check_B,
'TypeC': Check_C}
self.__class__ = map.get(self.check_type, Check)
def run(self):
'Do the normal stuff'
pass
class Check_A(Check):
class Meta:
proxy = True
def run(self):
'Do something different'
pass
class Check_B(Check):
class Meta:
proxy = True
def run(self):
'Do something different'
pass
class Check_C(Check):
class Meta:
proxy = True
def run(self):
'Do something different'
pass
It's not really clean but it was the easiest hack to find which solved my problem.
Maybe this is helps you, maybe it doesn't.
I'm also hoping someone else has a more pythonic solution to this problem since I'm counting the days till this method fails and comes back to haunt me..
This is a great approach and I don't particularly see it as a cheat. Here is IMHO some enhancements to the __init__ function so it doesn't has to change when you add more classes.
def __init__(self, *args, **kwargs):
super(Analysis, self).__init__(*args, **kwargs)
if not self.__type and type(self) == Analysis:
raise Exception("We should never create a base Analysis object. Please create a child proxy class instead.")
for _class in Analysis.__subclasses__():
if self.check_type == _class.__name__:
self.__class__ = _class
break
def save(self, **kwargs):
self.check_type = self.__class__.__name__
super(Analysis, self).save(**kwargs)
Hope this helps!
With an instance of Concert I get: unbound method do_stuff() must be called with Concert instance as first argument (got ModelBase instance instead)
models.py:
class Event(models.Model):
def do_stuff(self):
response self.do_specific_stuff(self)
class Concert(Event):
def do_specific_stuff(self):
...
class Party(Event):
def do_specific_stuff(self):
...
views:
def index(request):
x = Concert.objects.get(name='Jack White # Oakland')
output = x.do_stuff()
return HttpResponse(output)
My goal is to loop trough all the events and execute the do_specific_stuff child class method based on what kind of event it is.
In Django, inheritance triggers multi-table inheritance, but you don't get the polymorphism in Python. It's just an instance of the ORM not providing a perfect correspondence between the data schema and the object model.
In other words, when you query Event, you get back a whole bunch of Event objects, regardless of whether some of them are actually Concert or Party objects. You have to manually downcast. If an Event is a Concert, it will have an attribute called concert, which points to the corresponding Concert subclass. Likewise for Party. If it is just a normal Event, it will have neither attribute.
You could use the following property on Event to automatically downcast your object:
#property
def as_child_class(self):
"""Casts this object to its subclass, if possible"""
if hasattr(self, 'concert'):
return self.concert
elif hasattr(self, 'party'):
return self.party
else:
return self
Then you could do something like:
for event in Event.objects.all()
event.as_child_class.do_specific_stuff()
Similar questions have come up before:
Polymorphism in Django
How do I access the child classes of an object in django without knowing the name of the child class?
And this link has some other ideas:
http://jeffelmore.org/2010/11/11/automatic-downcasting-of-inherited-models-in-django/
It seems to me that your Event model is ONLY for inheritance, so you should abstract it:
class Event(models.Model):
class Meta:
absract = True
def do_stuff(self):
response self.do_specific_stuff()
def do_specific_stuff(self):
raise NotImplemented
class Concert(Event):
def do_specific_stuff(self):
...
class Party(Event):
def do_specific_stuff(self):
...
I may be wrong about your usage of Event, but if it was, abstracting your Event model will have it to be like a plain class, I mean no database actions will be taken for such model.
Hope this helps! :)
First of all, see Template method
Secondly, the Event class should be abstract.
class Event:
def __init__:
raise NotImplemented('This class is abstract')
Thirdly, see Single Table Inheritance and Class Table Inheritance patterns.
And django-ORM realisation of the patterns there
Good luck =)
My application uses class inheritance to minimize repetition across my models. My models.py looks kind of like this:
class BaseModel(models.Model):
title = models.CharField(max_length=100)
pub_date = models.DateField()
class Child(BaseModel):
foo = models.CharField(max_length=20)
class SecondChild(BaseModel):
bar = models.CharField(max_length=20)
Now most of the time, my views and templates only deal with instances of Child or SecondChild. Once in a while, however, I have a situation where I have an instance of BaseModel, and need to figure out which class is inheriting from that instance.
Given an instance of BaseModel, let's call it base, Django's ORM offers base.child and base.secondchild. Currently, I have a method that loops through all of them to figure it out. It would look something like this:
class BaseModel(models.Model):
...
def get_absolute_url(self):
url = None
try:
self.child
url = self.child.get_absolute_url()
except Child.DoesNotExist:
pass
if not url:
try:
self.secondchild
url = self.secondchild.get_absolute_url()
except SecondChild.DoesNotExist:
pass
if not url:
url = '/base/%i' % self.id
return url
That is hopelessly ugly, and gets uglier with every additional child class I have. Does anybody have any ideas on a better, more pythonic way to go about this?
Various forms of this question pop up here regularly. This answer demonstrates a generic way to "cast" a parent type to its proper subtype without having to query every subtype table. That way you wouldn't need to define a monster get_absolute_url on the parent which covers all the cases, you'd just convert to the child type and call get_absolute_url normally.
I haven't messed with Django inheitance much, so I suppose you can't override get_absolute_url() in the model classes?
Perhaps the visitor pattern could help if there are lot of functions that need this in many different places.
I haven't tested this, but it might be worth tinkering with:
def get_absolute_url(self):
subclasses = ('child', 'secondchild', )
for subclass in subclasses:
if hasattr(self, subclass):
return getattr(self, subclass).get_absolute_url()
return '/base/%i' % self.id