Kategoriebaum aus DB einlesen

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MariaDB/MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
tabellar
User
Beiträge: 186
Registriert: Mittwoch 4. September 2002, 15:28

Nur ganz kurz, das tree_model ist vollständig nutzbar, ev. müsste man noch ein Feld parent_id einfügen. Dann ist das ganze flexibler. Der einfache View ist eben statisch gebaut. Nur zwei Ebenen. Für beliebige Tiefe muss man dann eben eine rekursive view_funktion bauen...

Tabellar
tabellar
User
Beiträge: 186
Registriert: Mittwoch 4. September 2002, 15:28

Hab den Baum mal mit einem automatischen Numerierungs View und der Angabe
der Tiefe gebastelt :wink: (Anmerkung: die austeigende Laufzahl
in der Datenliste ist keine Tiefenangabe, sondern ein Hilfsmittel zur Sortierung
in der Datenbank. Nur so ist die Reihenfolge beim Select eindeutig gegeben)

Code: Alles auswählen

#database select
#db_category_tree = cursor.fetchall()
db_category_tree = [
[10,   0, 'Dach',             'container', 1],
[20,   0, 'Heizung',          'container', 2],
[101, 10, 'Firstpfette',      'task',      3],
[102, 10, 'Innenverkleidung', 'task',      4],
[201, 20, 'Aussparungen OG',  'task',      5],
[202, 20, 'Radiatoren EG',    'task',      6],
[301, 202, 'Schlafen',        'task',      7],
[302, 202, 'Wohnen',          'task',      8]
]

#create tree_model
tree = {}
tree[0]= {'parent_id': 0, 'name': 'root', 'childs': []}
for row in db_category_tree:
    tree[row[0]]= {'parent_id': row[1], 'name': row[2], 'type': row[3], 'childs': []}
    tree[row[1]]['childs'].append(row[0])

#create simple_depth_view
def tree_view(tree, n=2):
    def recursion(tree,node,depth, index):
        if depth +1 <= n:
            j = 1
            for child in tree[node]['childs']:
                tree[child]['depth'] = depth +1
                tree[child]['index'] = str(index) + '.' + str(j)
                j += 1
            for child in tree[node]['childs']:
                if tree[child]['depth'] == 1:
                    print str(tree[child]['index'])[2:], tree[child]['name']
                else:
                    print (tree[child]['depth'] - 1) * ' ' + str(tree[child]['index'])[2:], tree[child]['name']
                if tree[child]['childs'] != []:
                   recursion(tree,child,tree[child]['depth'], tree[child]['index'])
    recursion(tree,0,0,0)    
Ergebnis:

Code: Alles auswählen

>>> tree_view(tree,1)
1 Dach
2 Heizung

>>> tree_view(tree,2)
1 Dach
 1.1 Firstpfette
 1.2 Innenverkleidung
2 Heizung
 2.1 Aussparungen OG
 2.2 Radiatoren EG

>>> tree_view(tree,3)
1 Dach
 1.1 Firstpfette
 1.2 Innenverkleidung
2 Heizung
 2.1 Aussparungen OG
 2.2 Radiatoren EG
  2.2.1 Schlafen
  2.2.2 Wohnen
Benutzeravatar
Whitie
User
Beiträge: 216
Registriert: Sonntag 4. Juni 2006, 12:39
Wohnort: Schulzendorf

Hi Leute,
ich hab die letzten Tage mal wieder rumprobiert und habe jetzt eine für mich ideale Lösung gefunden. Inspiriert von einem anderen Thread, indem es um saubere Web-Programmierung geht, habe ich jetzt Django gewählt, um das Projekt auf die Beine zu stellen. Nach einigen Tutorials und durchstöbern der Dokumentation ist folgendes Django-Model rausgekommen:

Code: Alles auswählen

class Category(models.Model):
    parent = models.ForeignKey('self', blank = True, null = True,
                               verbose_name = _('Oberkategorie'),
                               help_text = _('So lassen für neue '
                                             'Oberkategorie.'))
    name = models.CharField(_('Name'), maxlength = 50, unique = True,
                            core = True)
    cat_opening = models.TextField(_('Einführung'), null = True)
    example = models.CharField(_('Beispiele'), maxlength = 3, blank = True,
        help_text = _('Für welche Art Fragen soll zu Beginn der Kategorie '
        'ein Beispiel angezeigt werden ? s = Single Choice, m = Multiple '
        'Choice, f = Freier Text (Einfach die Buchstaben hintereinander '
        'schreiben, z. B. smf für alle und leer für keine)'), default = 'sm')
    active = models.BooleanField(_('Aktiviert'), default = True)
    cat_time = models.IntegerField(_('Zeit'), default = 40,
        help_text = _('Zeit zur Beantwortung aller Fragen in dieser '
        'Kategorie in Minuten.'))
    use_item_time = models.BooleanField(_('Zeit von Fragen benutzen'),
        default = False, help_text = _('Wenn aktiviert, werden '
        'die Fragen einzeln angezeigt und jede Frage kann ein eigenes '
        'Zeitlimit haben.'))
    
    def __str__(self):
        p_list = self._recurse_for_parents(self)
        p_list.append(self.name)
        return self.get_separator().join(p_list)
    
    def _recurse_for_parents(self, cat_obj):
        p_list = []
        if cat_obj.parent_id:
            p = cat_obj.parent
            p_list.append(p.name)
            more = self._recurse_for_parents(p)
            p_list.extend(more)
        if cat_obj == self and p_list:
            p_list.reverse()
        return p_list
                
    def get_separator(self):
        return ' -> '
        
    def _parents_repr(self):
        p_list = self._recurse_for_parents(self)
        return self.get_separator().join(p_list)
    _parents_repr.short_description = _('Oberkategorie(n)')
    
    def _recurse_for_children(self, cat_obj, c_list):
        c_list.append(int(cat_obj.id))
        children = Category.objects.filter(parent = cat_obj.id,
                                           active = True)
        if not children:
            return c_list
        else:
            for child in children:
                c_list = self._recurse_for_children(child, c_list)
            return c_list
    
    def get_children_by_name(self):
        c_list_id = self.get_children_by_id()
        c_list_name = []
        for c_id in c_list_id:
            child = Category.objects.get(pk = c_id)
            c_list_name.append(child.name)
        return ', '.join(c_list_name)
    
    def get_children_by_id(self):
        return self._recurse_for_children(self, list())

    def save(self):
        p_list = self._recurse_for_parents(self)
        if self.name in p_list:
            raise validators.ValidationError(_('Sie können die Kategorie '
                                               'nicht in sich selbst '
                                               'speichern'))
        super(Category, self).save()
    
    class Admin:
        list_display = ['id', '_parents_repr',
                        'name', 'active', 'cat_time']
        list_display_links = ['id', 'name']
        list_filter = ['active']
        search_fields = ['name']
    
    class Meta:
        verbose_name = _('Kategorie')
        verbose_name_plural = _('Kategorien')
        ordering = ['id']
Das Kategoriemodell (entspricht ja meiner Idee) ist dabei aus einem der Codebeispiele von der Django Seite. Auch wenn es nicht jeder ausprobieren kann, da eine Django-Installation nötig ist, wollte ich es Euch nicht vorenthalten.

Gruß, Whitie

Edit: Das Rausfinden der Unterkategorien hatte gefehlt.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Und ich hab hier was neues gemacht: http://www.python-forum.de/topic-9698.html

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