Django - Daten aus mehreren Modellen auf Detailpage im Template anzeigen

Django, Flask, Bottle, WSGI, CGI…
Antworten
gospat83
User
Beiträge: 36
Registriert: Dienstag 21. September 2021, 14:25

Hallo zusammen,

so langsam komme ich immer besser zurecht mit der Funktionsweise von Django. Die ersten Anforderungen konnte ich bereits erfolgreich umsetzen. Nun habe ich ein Template verantwortlichkeit.html, auf dem ich quasi einen einzelnen Datensatz aus meinem Verantwortlichkeit Model darstelle. An dieser einen Verantwortlichkeit können mehrere Probleme hängen. Diese befinden sich im Problem Model und das Model hat die Verantwortlichkeit als FK enthalten. Ebenfalls hängt ein Status 1:1 an einer Verantwortlichkeit (Model Status). Ich schaffe es den Status zusätzlich im Template über verantwortlichkeit.status.ampel anzuzeigen, aber nicht die Probleme. Könnt ihr mir hier mal auf die Sprünge helfen.

Auszug models.py:

Code: Alles auswählen

class Verantwortlichkeit(models.Model):
    projekt = models.ForeignKey(Projekt, on_delete=models.CASCADE)
    baugruppe = models.ForeignKey(Baugruppe, on_delete=models.CASCADE)
    abteilung = models.ForeignKey(Abteilung, on_delete=models.CASCADE)
    mitarbeiter = models.ForeignKey(Mitarbeiter, on_delete=models.SET_NULL, blank=True, null=True)
    erstellt_am = models.DateTimeField(auto_now_add=True, editable=False)
    geaendert_am = models.DateTimeField(auto_now=True, editable=False)

    class Meta:
        verbose_name_plural = 'Verantwortlichkeiten'
        constraints = [
            models.UniqueConstraint(fields=['projekt', 'baugruppe', 'abteilung', 'mitarbeiter'], name='uq_projekt_baugruppe_abteilung_mitarbeiter')
        ]

    def __str__(self):
        return f'{self.projekt} | {self.baugruppe} | {self.abteilung} | {self.mitarbeiter}'
    
    def get_absolute_url(self):
        return reverse('verantwortlichkeit', kwargs={'pk': self.pk})
    

class Problem(models.Model):
    verantwortlichkeit = models.ForeignKey(Verantwortlichkeit, on_delete=models.CASCADE)
    beschreibung = models.TextField()
    ist_geloest = models.BooleanField(verbose_name='gelöst', default=False)
    erstellt_am = models.DateTimeField(auto_now_add=True, editable=False)
    geaendert_am = models.DateTimeField(auto_now=True, editable=False)

    class Meta:
        verbose_name_plural = 'Probleme'
    
    def __str__(self):
        return f'{self.verantwortlichkeit} | {self.beschreibung[:50]}'
    
        
 class Status(models.Model):
    FARBEN = (
        ('Rot', 'Rot'),
        ('Gelb', 'Gelb'),
        ('Grün', 'Grün'),
    )   
    verantwortlichkeit = models.OneToOneField(Verantwortlichkeit, on_delete=models.CASCADE, unique=True)
    ampel = models.CharField(max_length=4, choices=FARBEN, default='Grün')
    erstellt_am = models.DateTimeField(auto_now_add=True, editable=False)
    geaendert_am = models.DateTimeField(auto_now=True, editable=False)

    class Meta:
        verbose_name_plural = 'Status'

    def __str__(self):
        return self.ampel
Auszug views.py:

Code: Alles auswählen

from django.shortcuts import render
from django.views.generic import DetailView
from django.views.generic.edit import CreateView, UpdateView, DeleteView
from django.urls import reverse_lazy
from .models import Verantwortlichkeit
from .filters import VerantwortlichkeitFilter


def verantwortlichkeiten(request):
    verantwortlichkeiten = Verantwortlichkeit.objects.all()
    verantwortlichkeiten_filter = VerantwortlichkeitFilter(request.GET, queryset=verantwortlichkeiten)
    verantwortlichkeiten = verantwortlichkeiten_filter.qs
    context = {'verantwortlichkeiten': verantwortlichkeiten, 'verantwortlichkeiten_filter': verantwortlichkeiten_filter}
    return render(request, 'verantwortlichkeiten.html', context)


class VerantwortlichkeitDetailView(DetailView):
    model = Verantwortlichkeit
    template_name = 'verantwortlichkeit.html'
Auszug urls.py:

Code: Alles auswählen

from django.urls import path
from .views import verantwortlichkeiten, VerantwortlichkeitDetailView, VerantwortlichkeitCreateView, VerantwortlichkeitUpdateView, VerantwortlichkeitDeleteView


urlpatterns = [
    path('bgr-cockpit/verantwortlichkeiten', verantwortlichkeiten, name="verantwortlichkeiten"),
    path('bgr-cockpit/verantwortlichkeit/<int:pk>/', VerantwortlichkeitDetailView.as_view(), name="verantwortlichkeit"),
    path('bgr-cockpit/verantwortlichkeit/erstellen/', VerantwortlichkeitCreateView.as_view(), name="verantwortlichkeit_erstellen"),
    path('bgr-cockpit/verantwortlichkeit/<int:pk>/bearbeiten/', VerantwortlichkeitUpdateView.as_view(), name="verantwortlichkeit_bearbeiten"),
    path('bgr-cockpit/verantwortlichkeit/<int:pk>/loeschen/', VerantwortlichkeitDeleteView.as_view(), name="verantwortlichkeit_loeschen"),
]
verantwortlichkeit.html:

Code: Alles auswählen

{% extends 'base.html' %}

{% block content %}
<div class="container">
    <table class="table table-light table-striped">
        <thead>
            <tr>
                <th>PROJEKT</th>
                <th>BAUGRUPPE</th>
                <th>ABTEILUNG</th>
                <th>MITARBEITER</th>
                <th>STATUS</th>
                <th>BEARBEITEN</th>
                <th>LÖSCHEN</th>
            </tr>
        </thead>
        <tbody class="table-group-divider">
            <tr>
                <td>{{ verantwortlichkeit.projekt }}</td>
                <td>{{ verantwortlichkeit.baugruppe }}</td>
                <td>{{ verantwortlichkeit.abteilung }}</td>
                <td>{{ verantwortlichkeit.mitarbeiter }}</td>
                <td>{{ verantwortlichkeit.status.ampel }}</td>
                <td><a href="{% url 'verantwortlichkeit_bearbeiten' verantwortlichkeit.pk %}">Verantwortlichkeit bearbeiten</a></td>
                <td><a href="{% url 'verantwortlichkeit_loeschen' verantwortlichkeit.pk %}">Verantwortlichkeit löschen</a></td>
            </tr>
        </tbody>
    </table>
</div>
{% endblock content %}
Die Daten möchte ich gerne in einem div unterhalb der Tabelle darstellen.

Viele Grüße
gospat
Benutzeravatar
__blackjack__
User
Beiträge: 13919
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Aus dem Kopf und ungetestet: Über ``verantwortlichkeit.problem_set.all()`` iterieren‽
“I am Dyslexic of Borg, Your Ass will be Laminated” — unknown
gospat83
User
Beiträge: 36
Registriert: Dienstag 21. September 2021, 14:25

Könntest du mir evlt. einen Codeschnipsel dafür erstellen, ich bekomme es einfach noch nicht hin. Ich habe schon mehrere Vorschläge aus dem Netz ausprobiert, aber keiner funktioniert. Ich bin da leider noch nicht so fit drin und versuche dann im Nachgang zu verstehen, was da gemacht wird. Mit ein wenig Übung steigt das Verständnis dann nach und nach.
Benutzeravatar
__blackjack__
User
Beiträge: 13919
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@gospat83: Na einfach eine Template-``for``-Schleife da drüber.
“I am Dyslexic of Borg, Your Ass will be Laminated” — unknown
gospat83
User
Beiträge: 36
Registriert: Dienstag 21. September 2021, 14:25

Meinst du so?

Code: Alles auswählen

    <div>
        {% for verantwortlichkeit in verantwortlichkeit.problem_set.all %}
            <p>{{ verantwortlichkeit.problem.beschreibung }}</p>
        {% endfor %}
    </div>
Da wird leider trotzdem nichts angezeigt. Mache ich etwas verkehrt?
Benutzeravatar
__blackjack__
User
Beiträge: 13919
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@gospat83: Ziemlich sicher. Einmal nachdenken bitte: Was ist denn `verantwortlichkeit` hier? Also sowohl das `verantwortlichkeit` *vor* dem ``in`` und das *nach* dem ``in``. Und warum sollte das vor dem ``in`` ein Attribut mit dem Namen `problem` haben‽ Was denkst Du denn was `problem_set` ist und was wenn man da `all()` aufruft? Und was in der Dokumentation hat Dich auf diese Idee gebracht? Programmieren ist ja nicht raten, sondern man liest nach was das alles bedeutet.

Zum nichts angezeigt werden: wie sieht denn das HTML an der Stelle aus? Tauchen Fehlermeldungen im Webserverprotokoll auf?
“I am Dyslexic of Borg, Your Ass will be Laminated” — unknown
gospat83
User
Beiträge: 36
Registriert: Dienstag 21. September 2021, 14:25

Also, das erste verantwortlichkeit ist doch einfach nur ein Name für mein Objekt, oder? Das zweite der Name meines Modells. "problem_set" ist doch quasi die alternative zum related name um auf das Modell mit dem FK zu kommen, oder? set_all gibt mir vermutlich alle Probleme zurück. Das mir dem Attribut habe ich dem Stauts nachempfunden, denn dort musste ich es ja in dieser Art schreiben.

Ich bin aber noch ganz am Anfang in meiner "Programmierkarriere". Es ist nett, dass du mir hilfst, aber es würde mir noch mehr helfen, wenn du mir zeigst, wie ich das Problem löse. Ich versuche daraus immer Rückschlüsse zu ziehen um beim nächsten Mal dann nicht wieder diesen Fehler zu machen. Für einen Anfänger ist es leider nicht so einfach das alles immer direkt zu verstehen, auch mit Doku nicht.
Benutzeravatar
__blackjack__
User
Beiträge: 13919
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@gospat83: Das erste ist der Name für Dein Objekt, ja, aber was ist das für ein Objekt, welchen Datentyp hat das? Das zweite ist nicht der Name des Models, der ist `Verantwortlichkeit`, mit einem grossen `V`. Das ist der Name für die konkrete Verantwortlichkeit die da angezeigt werden soll. Und die hat Probleme, als `problem_set`. Also macht es ja so überhaupt gar keinen Sinn das die Laufvariable *auch* die Verantwortlichkeit wäre, denn die Verantwortlichkeit ist ja nur *eine*. Die Schleife sagt, und das kann man ja buchstäblich vorlesen „Für jede Verantwortlichkeit von allen Problemen der Verantwortlichkeit mache folgendes:“. Das macht als Satz ja schon keinen Sinn. `problem_set` enthält die Probleme. Ein einzelnes Problem sollte also weder `verantwortlichkeit` heissen, noch hat ein Problem ein Attribut `problem`. Und eine Verantwortlichkeit hat auch kein Attribut `problem`, denn es gibt ja nicht *das* *eine* Problem für eine Verantwortlichkeit, sondern eine Menge an Problemen, eben in `problem_set`. Das ging bei Status weil es genau *einen* Status gibt. Wobei man dort dann ja aber auch keine Schleife über diesen Wert braucht beziehungsweise nicht schreiben kann.

So etwas kann man übrigens auch ausprobieren wenn ich mich recht erinnere. Man sollte mit Django's `manage.py` auch eine interaktive Python-Shell starten können, in der man sich dann mal eine Verantwortlichkeit von der Datenbank abfragen kann und sich dann ”live” anschauen kann was das `problem_set` ist, und was `all()` (logischerweise) an Objekten liefert, und das das keine Verantwortlichkeiten sind. Und auch das die Verantwortlichkeit selbst kein `problem`-Attribut hat.
“I am Dyslexic of Borg, Your Ass will be Laminated” — unknown
gospat83
User
Beiträge: 36
Registriert: Dienstag 21. September 2021, 14:25

Vielen, vielen Dank! Anhand deiner Ausführungen ist der Groschen gefallen...

Code: Alles auswählen

    <div>
        {% for problem in verantwortlichkeit.problem_set.all %}
            <p>{{ problem.beschreibung }}</p>
        {% endfor %}
    </div>
Das war die Lösung meines Problems.
Antworten