2009-08-11 8 views
4

J'ai une question de conception concernant Django. Je ne suis pas sûr de savoir comment appliquer le principe de couplage lâche des applications à ce problème spécifique:Couplage lâche des applications et héritage du modèle

J'ai une commande-application qui gère les commandes (dans une boutique en ligne). Au sein de cet ordre-app J'ai deux classes:

class Order(models.Model): 
    # some fields 
    def order_payment_complete(self): 
     # do something when payment complete, ie. ship products 
     pass 

class Payment(models.Model): 
    order = models.ForeignKey(Order) 
    # some more fields  
    def save(self): 
     # determine if payment has been updated to status 'PAID' 
     if is_paid: 
      self.order.order_payment_complete() 
     super(Payment, self).save() 

Maintenant, le problème réel: J'ai une application plus spécialisée qui étend sorte de cet ordre. Donc, il ajoute un peu plus de champs à elle, etc. Exemple:

class SpecializedOrder(Order): 
    # some more fields 
    def order_payment_complete(self): 
     # here we do some specific stuff 
     pass 

Maintenant, bien sûr, le comportement prévu serait la suivante: créer un SpecializedOrder, le paiement de cette commande est passée et la méthode order_payment_complete() du SpecializedOrder est appelé. Toutefois, étant donné que le paiement est lié à Order, not SpecializedOrder, la méthode order_payment_complete() de l'ordre de base est appelée.

Je ne connais pas vraiment la meilleure façon de mettre en œuvre une telle conception. Peut-être que je suis complètement éteint - mais je voulais construire cette commande-app afin que je puisse l'utiliser à des fins multiples et je voulais le garder aussi générique que possible.

Ce serait génial si quelqu'un pouvait m'aider ici! Merci, Nino

Répondre

4

Je pense que ce que vous cherchez est le GenericForeignKey du cadre ContentTypes, qui est livré avec Django dans le paquet contrib. Il gère l'enregistrement du type et de l'ID de l'instance de sous-classe et fournit un moyen transparent d'accéder aux sous-classes en tant que propriété de clé étrangère sur le modèle.

Dans votre cas, il ressemblerait à quelque chose comme ceci:

from django.db import models 
from django.contrib.contenttypes.models import ContentType 
from django.contrib.contenttypes import generic 

class Payment(models.Model): 

    order_content_type = models.ForeignKey(ContentType) 
    order_object_id = models.PositiveIntegerField() 
    order = generic.GenericForeignKey('order_content_type', 'order_object_id') 

Vous n'avez pas besoin de faire quelque chose de spécial pour pouvoir utiliser cette clé étrangère ...les génériques manipuler de réglage et l'enregistrement des order_content_type et order_object_id champs de manière transparente:

s = SpecializedOrder() 
p = Payment() 
p.order = s 
p.save() 

Maintenant, lorsque votre Payment méthode de sauvegarde court:

if is_paid: 
    self.order.order_payment_complete() # self.order will be SpecializedOrder 
+0

parfait, c'était exactement ce dont j'avais besoin. :) – Nino

1

La chose que vous voulez s'appelle le polymorphisme dynamique et Django est vraiment mauvais. (Je ressens votre douleur)

La solution la plus simple que je l'ai vu jusqu'à présent quelque chose comme ceci:

1) Créer une classe de base pour tous vos modèles qui ont besoin de ce type de fonctionnalité. Quelque chose comme ceci: (code manifestement volé here)

class RelatedBase(models.Model): 
    childclassname = models.CharField(max_length=20, editable=False) 

    def save(self, *args, **kwargs): 
     if not self.childclassname: 
      self.childclassname = self.__class__.__name__.lower() 
     super(RelatedBase, self).save(*args, **kwargs) 

    @property 
    def rel_obj(self): 
     return getattr(self, self.childclassname) 

    class Meta: 
     abstract = True 

2) Hérite votre commande de cette classe.

3) Chaque fois que vous avez besoin d'un objet Order, utilisez son attribut rel_obj, qui vous renverra l'objet sous-jacent.

Cette solution est loin d'être élégant, mais je n'ai pas encore trouver un meilleur ...

+0

Je suppose que votre solution est juste une solution pour la solution de contenttype ci-dessus par Jarret , droite? – Nino

+0

@Nino Elle est plus générale dans le sens où elle fonctionnera pour n'importe quelle hiérarchie de classes, à chaque fois qu'elle est utilisée, c'est-à-dire que vous pouvez interroger la table de classe parent et accéder aux objets de classe enfant. Mais pour les clés étrangères, oui, c'est juste une solution de contournement. –

Questions connexes