Django acts as tree

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Antworten
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Rails kann ein Modell relativ einfach zu einem Knoten in einem Baum machen, indem man das `acts_as_tree`-Plugin wie folgt benutzt:

Code: Alles auswählen

    class Page
      acts_as_tree
    end
Ich finde diese pseudo-deklarative Form eigentlich gar nicht schlecht, auch wenn man nicht so recht weiß, was da nun eigentlich passiert. Ein Blick in den Quelltext zeigt, dass die Methoden `roots`, `root`, `ancestors` und `siblings` sowie zwei Relationen `parent` und `children` definiert werden. Optional kann man auch noch eine Sortierreihenfolge definieren, aber das soll hier keine Rolle spielen.

Wie sähe die Django-Lösung aus? Ich biete

Code: Alles auswählen

    class Page(models.Model):
        parent = acts_as.Tree(related_name='children')
Ich mache die beiden Namen `parent` und `children` explizit, auch wenn sie eigentlich nicht ohne weiteres änderbar sind. Alternativ ginge auch so etwas:

class Page(models.Model):
acts_as = Tree()

Ich nutze in beiden Fällen aus, dass Django automatisch die Methode `contribute_to_class` für ein Objekt aufruft, das im Kontext eines `Model` einem Attribut zugewiesen wird. Dort kann ich nun alle weiteren Attribute definieren, einen Manager zu weisen und weitere Methoden erzeugen. Funktioniert ausgezeichnet :)

Code: Alles auswählen

    class Tree(object):
        def contribute_to_class(self, model, name):
            TreeManager().contribute_to_class(model, 'objects')
            models.ForeignKey('self', related_name='children', blank=True, null=True)\
                .contribute_to_class(model, 'parent')
            model.ancestors = Tree.ancestors
            ...
        
        @staticmethod
        def ancestors(self):
            ...
Spricht etwas dagegen? Hat jemand noch eine bessere Idee für die Benamsung?

Stefan
bracki
User
Beiträge: 4
Registriert: Montag 27. Oktober 2008, 11:13

Sehr elegante Lösung.

Was IHMO dagegen spricht ist, das Model-Attribute eigentlich immer vom Typ model.Field sind. Evtl. könnte es da zur Verwirrung kommen wieso hier plötzlich ein Nicht-Field als Attribut verwendet wird. Könnte es nicht auch eine Lösung über einen Decorator geben?
Oder vielleicht in die Meta-Klasse eintragen?
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Mich hat es nicht verwirrt ;) Einen Decorator erst kann ich ja nicht an Attribute schreiben, sondern nur Funktionen. Nur, wenn ich Python 2.6 voraussetze, könnte ich auch noch Klassen als Ziel haben. Ich sehe daher nicht, wie ich diesen elegant benutzen könnte. Und Metaklassen zu definieren sieht IMHO nicht nur hässlich aus, sondern käme möglicherweise auch in Konflikt mit der Metaklasse von `django.db.models.Model`. Aber ich lasse mich gerne überraschen, wie es doch elegant gehen könnte.

Stefan
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Was macht das jetzt genau?!
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Was macht was?

Stefan
hinnerk
User
Beiträge: 2
Registriert: Samstag 8. November 2008, 15:58

wie baust Du denn die Hierarchie auf, benutzt Du z.B. MPTT?


Hinnerk
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Das acts as tree. Was macht das und wozu brauch ich das ;-)
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Rails' acts as tree baut auf naive Weise (kein MPTT) eine hierarchische Struktur, indem jedes Modell einen Papa kennt und damit jeder Papa seine Kinderchen. Außerdem kann man (mit Aufwand O(n)) die Vorfahren und die vaterlosen Urväter erfragen.

Stefan
bracki
User
Beiträge: 4
Registriert: Montag 27. Oktober 2008, 11:13

Das hier gibt's ja auch noch: http://code.google.com/p/django-mptt/

Mit einem bisschen Herumspielen habe ich herausgefunden, dass das Subclassen von models.ModelBase keine Probleme bereitet. Man sollte sich nur an die Metaclasses-Doku halten.

Unabhängig davon fände folgendes sonst auch gut:

Code: Alles auswählen

class TreeModel(models.Model):
       parent = models.TreeField
       objects = TreeManager
       
       class Meta:
              abstract = True

class MyTreeModel(TreeModel):
       pass
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Ich finde es nicht so gut, `objects` zu überschreiben, denn was, wenn ich nicht nur Baum-Verhalten will, sondern auch noch Listen-Verhalten (`acts_as_list`), wo ich Objekte nach einer Position anordnen kann oder weitere besondere Eigenschaften ergänzen will, die dann ebenfalls ein eigenes `objects` brauchen würden.

Stefan
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Interessantes Thema.

In PyLucid hab ich auch ein einfaches parent-tree model. In der alten v0.8 Version erstelle ich seperat vom Modell den "internen" Baum: In v0.9 soll das anders werden. Das parent-tree model bleibt, aber es wäre schön, wenn das Modell selber direkt einen Node Baum zurück liefern könnte. Wie, weiß ich aber noch nicht :(

EDIT: neben django-mptt gibt auch auch noch andere Dinge:
http://django-treebeard.googlecode.com/ ... index.html
parent-tree: http://code.google.com/p/django-treemenus/

Auch interessant, wie man ein parent-tree model aufbauen kann:
http://www.eflorenzano.com/blog/post/ex ... heritance/

Gibt sicherlich noch mehr, wenn man mal sucht: http://www.google.de/search?hl=de&q=django+tree+model

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Wie sieht es denn bei euch zu dem Thema aus?

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Antworten