2011-05-31 5 views
0

Puis-je lire des modèles polymorphes à partir d'une seule table de base de données, leur comportement dépendant d'un champ (booléen) du modèle?Comment récupérer un modèle polymorphe à partir d'une seule table dans django - ou comment implémenter un comportement polymorphe dans django

Dans l'un de mes modèles, le comportement est légèrement différent si l'instance est 'avant' contre 'arrière' ou 'gauche' contre 'droite'. Cela conduit à beaucoup de clauses d'if et de duplication de code. Je veux donc avoir une variante du modèle qui retrace les différents comportements.

Mais comment puis-je demander au gestionnaire de modèles de renvoyer les instances des bonnes classes? Dois-je remplacer __init__ du modèle? Peut-être que c'est plus facile à expliquer avec un exemple. Ce que je fais:

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 ... 

Ce que je veux faire:

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) 

Ou est-il un moyen tout à fait différente pour éviter ce genre de duplication de code?

Répondre

1

modèles Proxy:

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 

Le CRÉE ci-dessus deux modèles de proxy: un pour l'avant, un pour l'arrière. Les modèles de proxy n'ont pas leur propre table de base de données; ils utilisent la même table de base de données que le modèle dont ils héritent. (Cela signifie également que ne peut pas ajouter des champs supplémentaires au modèle de proxy, seules les méthodes.)

Il existe également un gestionnaire personnalisé permettant de forcer chacun à ne renvoyer que le sous-ensemble d'éléments appartenant à chacun. Ajoutez simplement les méthodes spécifiques dont vous avez besoin et vous avez terminé.

+0

Super, merci! Je n'étais pas au courant des modèles de proxy jusqu'à présent. Mais ils ne font toujours pas tout ce que je voulais. Je dois toujours utiliser 'ForwardFooManager' et' BackwardFooManager'. Je ne peux pas simplement appeler 'Foo.objects.all()' et obtenir des instances 'ForwardFoo' et' BackwardFoo' en fonction de la valeur de 'forward'. Donc, je laisse cette question ouverte, peut-être que quelqu'un a une idée différente. – jammon

+0

Ce n'est pas possible basé sur comment l'héritage fonctionne en Python, en général, et Django, en particulier. 'Foo' ne retournera que des objets' Foo'. Voir https://docs.djangoproject.com/fr/dev/topics/db/models/#s-querysets-still-return-the-model-that-was-requested –

+0

J'ai peur que vous ayez raison. Je vais reformuler le problème dans une question différente, plus générale. Peut-être que quelqu'un a une idée fondamentalement différente. Merci quand même d'avoir traité ma question un peu longue. – jammon

Questions connexes