2009-12-11 7 views
5

Ich erstelle eine Django-App, die einige Vererbung in seinem Modell verwendet, hauptsächlich weil ich alles eine UUID und eine Referenz zuweisen muss, damit ich weiß, welche Klasse es war. Hier ist eine vereinfachte Version der Basisklasse:Vererbung und Factory-Funktionen in Python und Django

class BaseElement(models.Model): 
    uuid = models.CharField(max_length=64, editable=False, blank=True, default=lambda:unicode(uuid4())) 
    objmodule = models.CharField(max_length=255, editable=False, blank=False) 
    objclass = models.CharField(max_length=255, editable=False, blank=False) 

class ChildElement(BaseElement): 
    somefield = models.CharField(max_length=255) 

Ich mag würde, um sicherzustellen, dass objmodule, objclass und UUID automatisch eingestellt werden. Ich habe von gelernt, dass es eine schlechte Idee ist, das zu tun, indem ich meinen eigenen Erbauer schreibe, und dass ich besser bin, eine Fabrikfunktion zu schreiben. So, jetzt mein BaseElement und ChildElement sieht so aus:

class BaseElement(models.Model): 
    uuid = models.CharField(max_length=64, editable=False, blank=True, default=lambda:unicode(uuid4())) 
    objmodule = models.CharField(max_length=255, editable=False, blank=False) 
    objclass = models.CharField(max_length=255, editable=False, blank=False) 

    def set_defaults(self): 
     self.objmodule = unicode(self.__class__.__module__) 
     self.objclass = unicode(self.__class__.__name__) 
     self.uuid = unicode(uuid4()) 

class ChildElement(BaseElement): 
    somefield = models.CharField(max_length=255) 

    @staticmethod 
    def create(*args, **kwargs): 
     ce = ChildElement(*args, **kwargs) 
     ce.set_defaults() 
     return ce 

Dies funktioniert. Ich kann ChildElement.create(somefield="foo") anrufen und ich werde ein passendes Objekt mit uuid, objmodule und objclass Felder richtig eingestellt bekommen. Wie auch immer, wenn ich durch gehe und mehr Klassen wie ChildElement2 und ChildElement3 erstelle, finde ich, dass ich die genau gleiche statische Fabrikfunktion einfüge. Das schimpft auf mich, weil die Code-Duplikation schlecht ist.

Mit normalen Methoden würde ich nur die create Factory-Funktion in BaseElement einfügen, aber ich kann das hier nicht tun, weil ich kein Handle zu sich selbst (weil es noch nicht erstellt wurde) zu bekommen Informationen über die Klasse des Objekts, das die Methode aufgerufen hat.

Gibt es eine Möglichkeit, dass ich dieses Werk in die BaseElement Klasse migrieren können, damit ich diesen Code nicht überall vervielfältigen müssen und haben es immer noch so ist es automatisch die Werte von uuid setzt objmodule und objclass?

Antwort

7

Wenn Sie create() eine @classmethod statt @staticmethod machen, haben Sie Zugriff auf das Klassenobjekt haben, die Sie anstelle des Verweises auf sie namentlich verwenden können:

@classmethod 
def create(cls, *args, **kwargs): 
    obj = cls(*args, **kwargs) 
    obj.set_defaults() 
    return obj 

Dies ist jetzt generisch und kann gehen auf der Basisklasse statt jeder Kindklasse.

+1

Genau das habe ich gesucht. Danke, dass du mich an '@ classmethod' erinnert hast. Es ist nicht etwas, was ich sehr oft benutze. – Pridkett

+0

In Python gibt es selten einen guten Anwendungsfall für '@ staticmethod' - das ist der, den Sie wahrscheinlich seltener verwenden sollten, wenn überhaupt. Oft ist etwas, wenn es sich nicht um eine '@ Klassenmethode handelt, als einfache Top-Level-Funktion in einem Modul geeignet. –

2

Ich denke, dass Sie lieber in Ihrem BaseElement speichern überschrieben werden. Nach dem Speichern können Sie diese Felder festlegen. Es wäre etwas wie:

class MyBase(models.Model): 
    uuid = models.CharField(max_length=64, editable=False, blank=True, 
     default=lambda:unicode(uuid4())) 
    objmodule = models.CharField(max_length=255, editable=False, blank=False) 
    objclass = models.CharField(max_length=255, editable=False, blank=False) 

    def save(self): 
     if not self.id: 
      self.objmodule = unicode(self.__class__.__module__) 
      self.objclass = unicode(self.__class__.__name__) 
      self.uuid = unicode(uuid4()) 
     super(self.__class__.__base__, self).save() 

class InheritedFromBase(MyBase): 
    new_field = models.CharField(max_length=100) 

Ich habe damit getestet und es schien zu tun, was Sie suchen. Ich war in der Lage, ein "InheritedFromBase" -Objekt zu erstellen, das die benötigten Felder hatte, ohne viel Code zu duplizieren.

+0

+1: Einfach überschreiben 'save' ist fast immer das Richtige. –

+1

Das ist nah, und kann sich als akzeptabel erweisen, ist aber nicht ganz das, was ich brauche. Im Moment erstelle ich einige Objekte auf dem Server und serialisiere sie dann auf einem Client für die Verwendung in einer AJAXy App. Der Client kann Attribute ändern und dann die Objekte speichern. Mit Ihrem Setup würden sie "Uuid", "Objmodule" und "Objclass" nicht zugewiesen bekommen, bis sie gespeichert sind. Da ich 'uuid' auf dem Client verwende, müsste ich wahrscheinlich dem Server zusätzliche Logik hinzufügen oder dem Client zuweisen. So nah, aber ich würde gerne einen Weg finden, ohne vorher 'save' aufzurufen. Ich kann jedoch damit umgehen, wenn dies der richtige Weg ist. – Pridkett