Halb Django, Halb Python (Anfänger) Frage

Django, Flask, Bottle, WSGI, CGI…
Antworten
SnakeBite
User
Beiträge: 46
Registriert: Mittwoch 4. März 2009, 18:26

Hallo meine lieben Mitforenten.

Ich habe vor kurzem angefangen Python und Django zu lernen und bisher läuft es ganz gut. Die Dokus und Foreneinträge mit häufigen Fehlern/Fragen helfen sehr.

Allerdings hab ich manchmal einfach kleine Hänger, weil mir wohl einfach noch die langjährige Programmiererfahrung fehlt. Ich bin gerade an so nem Hängern angekommen und verstehe einfach nicht wo mein Hirn genau hängt.

Erstmal mein Code: (ist aus einem O'Reilly Tutorial)

urls.py:

Code: Alles auswählen

from django.conf.urls import url
from django.contrib import admin

from books.views import list_books

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^$', list_books, name="books"),
]
views.py:

Code: Alles auswählen

from django.shortcuts import render
from .models import Book

def list_books(request):
	"""
	List the books that have reviews
	"""

	books = Book.objects.exclude(date_reviewed__isnull = True)

	context = {"books":books}

	return render(request, "list.html", context)
Auszug aus models.py:

Code: Alles auswählen

class Book(models.Model):
	title = models.CharField(max_length=150)
	authors = models.ManyToManyField("Author", related_name="books")
	review = models.TextField(blank=True, null=True)
	date_reviewed = models.DateTimeField(blank=True, null=True)
	is_favourite = models.BooleanField(default=False, verbose_name="Favorite?")
Auszug aus list.html:

Code: Alles auswählen

		<main>
			<h1>{{ books.count }} books</h1>
		</main>
Was ich nun nicht verstehe, sind zwei Sachen:

1.a. views.py: Warum der "Umweg" über die Variable "context"?
Liefert books = Book.objects.exclude(date_reviewed__isnull = True) nicht eh schon eine List mit allen Büchern?
Ich kann doch in der Django Shell schreiben:

books=Book.objects.exclude(date_reviewed__isnull = True) und dann mit
books[0].title, books[1].title etc durchiterieren

1.b. Und selbst wenn ich es über "context" mache:

Wieso reicht da ein einfaches context = {"books":books} ?

Müsste ich da nicht für jedes einzelne Buch einmal durchiterieren? Also sowas wie for book in books? Das muss ich doch sonst auch immer so machen?
Bzw anders gefragt, worin liegt er Vorteil darin ein Dictionary zu haben das so aussieht:
"books": infos_zu_buch1, Infos_zu_buch2, ...
anstatt eine List zu haben die so aussieht:
infos_zu_buch1, Infos_zu_buch2, ...

Was genau bringt es mir hier ein dictionary zu haben anstatt einer List?
Das ist es doch was der Umweg über context bringt oder?

2. Woher genau kommt die Bezeichnungs "books" in {{ books.count }}?

Wenn das Dictionary "context" übergeben wird, dann müsste es doch context["books"].count heissen???



Das mögen für Euch sicher die peinlichsten Anfängerfehler sein, aber ich hab mir das jetzt schon echt lange überlegt und ich verstehe es einfach nicht.

Vielleicht kann ja jemand von Euch den/die Knoten lösen.

Vielen Dank schonmal!!
Sirius3
User
Beiträge: 18220
Registriert: Sonntag 21. Oktober 2012, 17:20

SnakeBite: context enthält die Variablen, die im Template bekannt sind. Das ist ein Wörterbuch. Das beantwortet auch Deine Frage 2. books kommt aus dem Wörterbuch, da da kein "context" enthalten ist, kennt das Template die Variable context auch nicht.
Benutzeravatar
noisefloor
User
Beiträge: 4151
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,
Was genau bringt es mir hier ein dictionary zu haben anstatt einer List?
Django erwartet hier hat ein Dict. Und hier übergibst du halt eine Dict, was nur einen Schlüssel hat (nämlich "books") und der Wert dieses Schlüssels ist das Ergebnis deiner DB-Abfrage. Grundsätzlich kann das Dict aber auch mehrere Schlüssel haben, z.B. wenn du die Ergebnisse mehrere DB-Abfragen an ein Template durchreichst, z.B.

Code: Alles auswählen

books = Book.objects....
authors = Author.objects...
context = {'book':books, 'authors': authors}
Das ist es doch was der Umweg über context bringt oder?
Nee. Den Zwischenschritt über Context muss du auch nicht machen. Du kannst dass auch so machen:

Code: Alles auswählen

return render(request, "list.html", {"books":books})
Gruß, noisefloor
SnakeBite
User
Beiträge: 46
Registriert: Mittwoch 4. März 2009, 18:26

noisefloor: Ahhhhhh OK jetz hab ichs verstanden. Das ganze hat also nur den Sinn dass Django es nur als Dict annimmt. Dann ist ja Alles klar. Ich dachte das hätte irgendwelche speziellen Python-Programmier-Gründe. Aber dann ist ja Alles klar.

Vielen Dank!!

Dann muss ich ja jedes mal extra nachschauen, was die jeweilige Funktion/Klasse/Instanz/Methode für ein Liste/Tuple/Dict Format annimmt? Oder gibts da bestimmte "ungeschriebene Regeln"? Oder weiß man das einfach mit der Zeit auswendig?

Dann bleibt nur noch eine letzte Frage:

Woher kommt denn das book.count in meiner "list.html"?

Ich habe in "views.py" ja stehen: return render(request, "list.html", context) und ich nehme an so wird context dann an "list.html" übergeben. Aber wo wird denn definiert dass es "books" heisst? Und wenn mit context ein Dict übergeben wurde, dann müsste es doch auch in der "list.html" ein Dict sein und dann müsste es doch entsprechend context["books"].count heissen?!?
BlackJack

@SnakeBite: Du musst wissen was als Argument erwartet wird. Es wird sehr wahrscheinlich nicht nur ein `dict` sein, sondern alles was sich wie eines verhält. Ich wüsste nicht was mit „ungeschriebenen Regeln“ hier gemeint sein könnte‽

Das `count` kommt von dem Objekt das Du unter dem Schlüssel 'book' als Wert in das Wörterbuch gesteckt hast. Und auch da wirst Du wieder in der Dokumentation nachlesen müssen welchen Datentyp das hat und welche Attribute und Methoden es deswegen hat und wie es sich ansonsten verhält. Die Zeile die zu dem Wert führt ist ``books = Book.objects.exclude(date_reviewed__isnull = True)``. Da kannst Du Dich dann dran lang arbeiten. `Book` ist eine Model-Klasse. Und Modelklassen haben ein objects-Attribut, was laut Dokumentation vom Typ `Manager` ist, dessen Methoden in der Dokumentation zu Abfragen beschrieben werden. Die `exclude()`-Methode steht im Abschnitt zur Abfrage mit Filtern. Und wie fast alle Abfragemethoden liefert die Methode einen Wert vom Typ QuerySet. Und die haben dann eine count()-Methode.

Die Schlüssel in dem Wörterbuch werden im Template direkt zur Verfügung gestellt. Zum einen wäre es ja Unsinn jedesmal das ``context[…]`` hinschreiben zu müssen, zum anderen: Warum `context`? Das ist ja bloss ein lokaler Name in *Deiner* Funktion, woher soll der Code im Template diesen Namen kennen? Es werden beim Aufruf nur Werte übergeben, keine Namen.
SnakeBite
User
Beiträge: 46
Registriert: Mittwoch 4. März 2009, 18:26

Hmm...schön langsam komme ich der Sache schon näher.
QuerySet, Manager-Typ, etc hab ich ja noch nie gehört. Da muss ich wohl doch mal noch etwas weiter Python Bücher lesen bevor ich da komplett durchsteige. Ich will ja nich nur irgendwas hinschreiben, sondern auch ganz genau verstehen was, wer, wohin, woher, und wie weit führt.

In Laien-Sprache ausgedrückt kann man dann also sagen:

1. Bei "books = Book.objects..." ist "books" eine non-globale Variable innerhalb der von mir erstellten Funktion und muss deswegen mit return ausdrücklich "nach aussen" gegeben werden und vorher noch in ein Dict umgeändert werden. Django ist dann einfach so programmiert dass es in dem Fall das "books" direkt aus dem übergebenen Dict rauszieht. Sprich hätte context = {"books:books, "authors":authors"} dann würde Django das authors auch automatisch rausziehen und ich könnte dann auch Sachen wie "authors.count()" ausführen?! Ist das so korrekt?

Hört sich für mich irgendwie umständlich an das erst umzuändern anstatt direkt zu übergeben, aber ich gehe davon aus die DJango Jungs haben es mehr drauf als ich. hehe :)

Auf jeden Fall vielen Dank @BlackJack. Ich bin ehrlich gesagt noch etwas überfordert mit dem ganzen Jargon. Aber ich krieg das schon irgendwie raus.
BlackJack

@SnakeBite: Für `QuerySet` und `Manager` musst Du keine (allgemeinen) Python-Bücher lesen sondern die Django-Dokumentation (oder entsprechende Django-Bücher), denn diese Datentypen werden von den Django-Programmierern definiert. Jeder Programmierer kann neue Datentypen definieren und jede Bibliothek kann deshalb eigene Datentypen für den Programmierer zur Verfügung stellen, die dann natürlich nicht in der allgemeinen Python-Dokumentation beschrieben sind, weil die Programmierer von Python und dessen Standardbibliothek nicht alle anderen Bibliotheken von anderen Leuten kennen und dokumentieren können. Selbst *Du* erstellst in Deinen Django-Anwendungen eigene Datentypen und kannst denen Attribute, Methoden, und auch Verhalten im Zusammenspiel mit Python-Syntax geben das neu, speziell für diese Datentypen gilt. Den `Book`-Datentyp hast Du ja durch die Klasse definiert. Das meiste vom Verhalten der Klasse und daraus erstellten Exemplaren wird bereits durch die Basisklasse `Model` definiert, aber Du kannst da noch mehr Verhalten hinzufügen und auch vorhandenes überschreiben oder abändern.

Die Variable wird nicht abgeändert sondern der Wert wird in ein Wörterbuch gesteckt. Und ja, wenn Du dort weitere Schlüssel/Wert-Paare hinein steckst, dann sind diese Werte auch under dem Schlüssel im Template verfügbar. Und genau an der Stelle sehe ich dann auch nicht mehr warum das umständlich sein soll, denn wie sollte man das denn sonst machen? Bei Aufrufen werden wie gesagt nur Werte übergeben. Der aufgerufene Code kann nicht feststellen an welchen Namen der Wert beim Aufrufer gebunden war, also muss man die Namen unter dem der Wert im Template zur Verfügung stehen soll, irgendwie angeben können. Der Wert der übergeben wird, muss ja nicht einmal an einen Namen gebunden worden sein, denn man kann als Wert ja auch direkt einen Ausdruck statt eines Namens übergeben.

Mit dem Begriff „Variable“ solltest Du vielleicht vorsichtiger umgehen. Namen und Werte sind eventuell bessere Begriffe die nicht so ”schwammig” sind. Und Variable stellen sich manchmal verschiedene Leute leicht unterschiedliche Konzepte vor, weil unterschiedliche Programmiersprachen das auch unterschiedlich handhaben.
Benutzeravatar
noisefloor
User
Beiträge: 4151
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,
... sondern auch ganz genau verstehen was, wer, wohin, woher, und wie weit führt.
Grundsätzlich hilft es bei der Verwendung von Framework (wie z.B. Django), wenn man ein grundlegendes Verständnis von objektorientierter Programmierung mit Python hat, also wie man ein Instanz einer Klasse erzeugt, was Methoden und Attribute sind und wie man unter Python so gängiger Weise Daten "rumreicht".

Zu Verstehen, wie Django im Kerne werkelt, ist ziemlich ehrgeizig und IMHO auch nicht nötig, jedenfalls nicht als normaler Anwender. Das wird erst interessant, wenn du mal Erweiterungen für Django programmieren würdest.
da muss ich wohl doch mal noch etwas weiter Python Bücher lesen
Im Falle von Django greife ich immer auf die offizielle Doku zurück - da ist alles erklärt, was man wissen muss. Auch, wenn manchmal ein bisschen verstreut ist. Bei Django-Bücher musst du aufpassen, weil viele Bücher nicht (mehr) aktuell sind und sich ggf. auf alte Version von Djanog beziehen, wo Dinge anders funktionieren als in der aktuelle LTS Version 1.8.x) oder der aktuellen "normalen" Version (1.9.x).

Gruß, noisefloor
SnakeBite
User
Beiträge: 46
Registriert: Mittwoch 4. März 2009, 18:26

Nun ist ja ein guter Monat vergangen und falls es jemanden interessiert, hier mal ein kleines Update:

Inzwischen läuft es eigentlich ganz gut. Meine in diesem Thread gestellte Frage ist mir inzwischen auch absolut klar und ich kann gar nicht nachvollziehen wieso es mir so schwer fiel da selbst drauf zu kommen. :-)
Vielen Dank also nochmal für die Hilfestellungen und die Geduld!

Insgesamt muss ich sagen war nicht das Python-Wissen das Problem, sondern die allgemeine "Programmier-Logik" (unabhängig von Python oder Django). Ich habe mir aber viele Tutorials angesehen und durchgelesen und wenn man es oft genug sieht/hört/liest, versteht man es dann schon irgendwann. Aber für einen Nicht-Developer war das am Anfang einfach ziemlich viel neues Zeug.
Antworten