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.
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!
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..
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With