Can I read polymorphic models from a single database table, with their behaviour depending on a (boolean) field of the model?
In one of my models the behaviour is slightly different if the instance is 'forward' vs. 'backward' or 'left' vs. 'right'. That leads to a lot of if-clauses and code duplication. So I want to have a Forward- and a Backward-variant of the model that encapsulate the different behaviours.
But how can I make the models manager return the instances of the right classes? Do I have to overwrite __init__
of the model?
Maybe it's easier to explain with an example. What I'm doing:
class Foo(models.Model):
forward = models.BooleanField()
other_fields = ...
def do_foobar(bar):
if self.forward:
gap = bar.end_pos - bar.current_pos
self.do_forward_move(max = gap)
if self.pos==bar.end_pos:
and so on ...
else:
gap = bar.current_pos - bar.start_pos
self.do_backward_move(max = gap)
if self.pos==bar.start_pos:
and so on ...
What I want to do:
class Foo(models.Model):
forward = models.BooleanField开发者_运维知识库()
other_fields = ...
def __init__(*args, **kwargs):
""" return ForwardFoo or BackwardFoo
depending on the value of 'forward'"""
How?
def do_foobar(bar):
gap = self.calculate_gap(bar)
self.do_move(max = gap)
if self.end_point_reached():
and so on ...
class ForwardFoo(Foo):
def calculate_gap(bar):
return bar.end_pos - bar.current_pos
and so on ...
for f in Foo.objects.all():
f.do_foobar(bar)
Or is there a totally different way to avoid this kind of code duplication?
Proxy models:
class Foo(models.Model):
# all model attributes here
class ForwardFooManager(models.Manager):
def get_query_set(self, *args, **kwargs):
qs = super(ForwardFooManager, self).get_query_set(*args, **kwargs)
return qs.filter(forward=True)
class ForwardFoo(Foo):
class Meta:
proxy = True
objects = ForwardsFooManager()
# methods for forward model
class BackwardFooManager(models.Manager):
def get_query_set(self, *args, **kwargs):
qs = super(BackwardFooManager, self).get_query_set(*args, **kwargs)
return qs.filter(forward=False)
class BackwardFoo(Foo):
class Meta:
proxy = True
objects = BackwardFooManager()
# methods for backward model
The above creates two proxy models: one for forward, one for backward. Proxy models do not have their own database table; they use the same database table as the model they inherit from. (This also means you cannot add any additional fields to the proxy model, only methods.)
There's also a custom manager for to force each one to only return the subset of items that belong to each. Just add whatever specific methods you need and you're done.
精彩评论