[Django]Templates einer App in ein "main" Template packen

Django, Flask, Bottle, WSGI, CGI…
Antworten
Cyphron
User
Beiträge: 6
Registriert: Sonntag 7. August 2011, 13:48

Hallo ich bin neu bei Django und habe das Tutorial mit der Poll Aplication
auf der Django Projektseite gemacht (siehe: https://docs.djangoproject.com/en/1.3/intro/tutorial01/).

Ich will nun die Ausgaben die durch die Templates der Poll App generiert werden in ein anderes Template „packen“ (z.B. die Startseite).

Die Poll App selbst besteht aus 3 Templates: poll_list (Auflistung aller Umfragen),
poll_detail (Dient zu Abstimmung bei einer Umfrage) und poll_results (Anzeiger der Ergebnisse einer Umfrage).

Über "include" ist es zwar kein Problem die poll_list in die Startseite einzubinden, aber wenn ich nun aus der liste
eine Umfrage wähle, wird selbstverständlich nur noch poll_detail angezeigt.

Was ich aber möchte ist das der Inhalt der Startseite erhalten bleibt während der der Poll App sich ändert (z.B. in einer Sidebar). Mit Frames möchte ich dieses Problem nicht lösen.

Ich habe es damit versucht über extend die 3 Templates von der Startseite abzuleiten was zwar
Funktioniert, aber keine gute Lösung ist (bei mehr als einer App nicht mehr möglich).

Auch versuche ich es zurzeit mit custom tags, aber dies scheint ebenfalls wie bei include nur für ein Template zu funktionieren.

kann mir jemand weiterhelfen wie ich das realisieren kann?
Benutzeravatar
tjuXx
User
Beiträge: 67
Registriert: Freitag 21. September 2007, 09:25
Wohnort: Bremerhaven
Kontaktdaten:

extends ist was du brauchst, warum soll es keine gute Lösung sein??

siehe: https://docs.djangoproject.com/en/1.3/t ... nheritance
Cyphron
User
Beiträge: 6
Registriert: Sonntag 7. August 2011, 13:48

tjuXx hat geschrieben:extends ist was du brauchst, warum soll es keine gute Lösung sein??

siehe: https://docs.djangoproject.com/en/1.3/t ... nheritance

Aber in dem Fall müßte ich dann poll_list als Startseite festlegen welches index.html "extendet" oder sehe ich da etwas falsch?

Denn dann wäre das Problem bei mehr als nur einer App.
Benutzeravatar
tjuXx
User
Beiträge: 67
Registriert: Freitag 21. September 2007, 09:25
Wohnort: Bremerhaven
Kontaktdaten:

Du erstellst dir zunächst ein Basis-Template. In das kannst du dann Blöcke einbauen die du von dem jeweiligen Template der App ersetzen lässt.

Code: Alles auswählen

<html>
<head>
    <title>{% block title %}My amazing site{% endblock %}</title>
</head>
<body>
{% block mainmenu %}
    <a href="#">Home</a><a href="#">Link</a>
{% endblock %}
<h1>Ich stehe auf allen Seiten!</h1>
{% block content %}
    Ich werde durch später ausgetauscht.
{% endblock %}
</body>
</html>
In App-spezifischen Child-Templates kannst du dann die Blöcke ersetzen:

Code: Alles auswählen

{% extends "base.html" %}

{% block title %}App Titel{% endblock %}

{% block content %}
    Ich bin der App-Content
{% endblock %}
Der Inhalt des Basistemplates wird durch gleichnamige im Child-Template Blöcke ersetzt. Wenn ein Block im Child-Template nicht definiert ist wird der Inhalt des Basistemplates gerendert. Blöcke ohne Inhalt werden ignoriert.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Cyphron hat geschrieben:Aber in dem Fall müßte ich dann poll_list als Startseite festlegen welches index.html "extendet" oder sehe ich da etwas falsch?

Denn dann wäre das Problem bei mehr als nur einer App.
Suchst du vielleicht den Umgekehrten Weg? Du hast eine Seite und willst den Poll an Stelle XY rein haben?
Dann könntest du dir einen Template-Tag schreiben. Ist aber nicht mal eben so gemacht.

So ähnlich mache ich das bei PyLucid: http://www.pylucid.org/permalink/375/poll

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Cyphron
User
Beiträge: 6
Registriert: Sonntag 7. August 2011, 13:48

@tjuXx:
Etwa so hatte ich die auch getan Jedoch müsste ich dann auf das child template verweisen. Jedoch habe ich vor weitere Apps zu schreiben und diese in meine Startseite einzubinden.

@jens:

Dies scheint das zu sein wonach ich suche. Ich schau mir grade den Sourcecode an aber ist recht schwer Ihn in seiner Gesamtheit zu verstehen.

Also die css_anchor_div.html ist das Basistemplate für die App bzw. Plugins richtig?
Über „extend“ werden alle templates hiervon abgeleitet bzw. einige.

Das mit den Blöcken habe ich soweit auch verstanden. Der Inhalt dieser wird mit dem Inhalt der Abgeleiteten Templates ersetzt oder es wird der dazwischenliegende Text Ausgegeben (in dem Fall Fehlermeldung).

In der views.py scheinst du das verhalten sowie die Parameter des Tags „lucidTag“ festzulegen für den Fall einer Poll App. „id“ = die ID der Poll (also „poll“ wie bei {% lucidTag poll id=x %})? Außerdem scheint man noch weitere Funktionen der Poll App durch den lucidTag abrufen zu können (z.B. {% lucidTag poll.all_polls %} für all_polls).

lucidTag.py im „defaulttags“ Ordner scheint mir sehr ähnlich zu den custom Tags zu sein mit dem ich mich derzeit auseinandersetze. Worin besteht dabei der prinzipielle unterschied? Benötige man bei dem lucidTag ebenfalls die funktion load?

Jedenfalls komm ich hier zur Zeit nicht weiter.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Cyphron hat geschrieben:Also die css_anchor_div.html ist das Basistemplate für die App bzw. Plugins richtig?
Über „extend“ werden alle templates hiervon abgeleitet bzw. einige.
Das Template ist eigentlich nur dazu da, damit Plugin "Ausgaben" mit einem <div> Tag umschlossen werden.

In PyLucid gibt es verschiedenen Arten, wie Plugin views eingebunden werden können, siehe: http://www.pylucid.org/permalink/134/new-v09-plugin-api

Eine Art ist es über ein {% lucidTag ... %} zu machen:
Der Tag kann im globalen Template rein, z.B.: {% lucidTag main_menu %} für das Haupt-Menü oder {% lucidTag auth %} für den Login/Logout link
Man kann den Tag aber auch in einer CMS Seite rein packen, z.B.: {% lucidTag poll id=x %} oder {% lucidTag generic.youtube id="XL1UNmLDLKc" %}

Mit dem lucidTag kann man zum einen verschiedene views in views.py ansprechen und zum anderen Parameter übergeben:

Code: Alles auswählen

{% lucidTag PluginName %}
{% lucidTag PluginName.view_method %}
{% lucidTag PluginName kwarg1="value1" %}
{% lucidTag PluginName kwarg1="value1" kwarg2="value2" %}
Man kann Plugin views aber noch anders ansprechen: z.B. über get parameter, damit man Dinge machen kann, ohne die Seite zu wechseln. wie z.B. die Sprache wechseln: oder aber login, logout: (Wobei man i.d.R. von den GET Parameter nicht viel sieht, weil es einen Redirect gibt)

Und dann können Plugins auch als "Plugins-Seiten" in den Seitenbaun eingebunden werden. z.B.:
* das Blog-Plugin: http://www.pylucid.org/permalink/141/blog
* Das Lexicon-Plugin: http://www.pylucid.org/permalink/73/pyl ... finitionen


Die Ausgaben der views kommen beim lucidTag nicht über template-Vererbung mit {% extends %} zu stande. Vielmehr ruft der Template Tag lucidTag den view auf und gibt das Ergebnis des views als eigene Rückgabe zurück in die Seite...
Das ganze passiert in lucidTagNode() hier: https://github.com/jedie/PyLucid/blob/m ... ucidTag.py

Template vererbung mit {% extends %} wird allerdings bei echten Plugin Seiten genutzt, siehe: http://www.pylucid.org/permalink/134/ne ... PluginPage
Cyphron hat geschrieben:In der views.py scheinst du das verhalten sowie die Parameter des Tags „lucidTag“ festzulegen für den Fall einer Poll App. „id“ = die ID der Poll (also „poll“ wie bei {% lucidTag poll id=x %})? Außerdem scheint man noch weitere Funktionen der Poll App durch den lucidTag abrufen zu können (z.B. {% lucidTag poll.all_polls %} für all_polls).
Die Parameter werden einfach dem view übergeben. z.B.: {% lucidTag poll id=123 %} ruft im Endeffekt den view: poll.view.lucidTag(request, id) auf, hier: https://github.com/jedie/PyLucid/blob/m ... ws.py#L153

Wobei {% lucidTag poll.all_polls %} dann poll.views.all_polls(request, hide_deactivated=False, not_voteable=False) aufruft: https://github.com/jedie/PyLucid/blob/m ... ws.py#L197
Cyphron hat geschrieben:lucidTag.py im „defaulttags“ Ordner scheint mir sehr ähnlich zu den custom Tags zu sein mit dem ich mich derzeit auseinandersetze. Worin besteht dabei der prinzipielle unterschied? Benötige man bei dem lucidTag ebenfalls die funktion load?
custom Tags? Du meinst https://docs.djangoproject.com/en/dev/h ... late-tags/ ? Oder was externes?
Ein normaler django Template tag ruft eigentlich nichts auf. Ist PyLucid spezifisch ;)

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Cyphron
User
Beiträge: 6
Registriert: Sonntag 7. August 2011, 13:48

Jop dieses Custom Template Tags meine ich. Der lucidTag unter defaulttag bei PyLucid ähneln denen.

Unter verwendung der Custom Template Tags konnte ich zwar das poll_list Template meiner Poll einbinden wenn ich aber einen Poll ausgewähle werde ich ja zur Abstimmungsseite weitergereicht. Ich möchte das dies innerhalb der mainpage passiert ähnlich des Poll Plugins von PyLucid.

Wie könnte man nun solche Template Tags erstellen? Über Google finde ich leider nichts dazu und mit den Custom Template Tags scheint es nicht zu funktionieren falls ich alles richtig gemacht habe.
Benutzeravatar
tjuXx
User
Beiträge: 67
Registriert: Freitag 21. September 2007, 09:25
Wohnort: Bremerhaven
Kontaktdaten:

Nein, du musst nicht auf Child-Templates verweisen.

Template App 1:

Code: Alles auswählen

{% extends "base.html" %}
{% block title %}App1 Titel{% endblock %}
{% block content %}
    Ich bin der Content von App 1.
{% endblock %}
View App 1:

Code: Alles auswählen

from django.shortcuts import render_to_response, get_object_or_404
def detail(request, poll_id):
    p = get_object_or_404(Poll, pk=poll_id)
    return render_to_response('pfad/zu/templateApp1.html', {'poll': p})
Template App 2:

Code: Alles auswählen

{% extends "base.html" %}
{% block title %}App2 Titel{% endblock %}
{% block content %}
    Ich bin der Content von App 2.
{% endblock %}
{% block App2Spezial %}
    Ich werde NUR von App2 gefüllt!
{% endblock %}
View App 2:

Code: Alles auswählen

from django.shortcuts import render_to_response
from polls.models import Poll
def index(request):
    latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
    return render_to_response('pfad/zu/templateApp2.html', {'latest_poll_list': latest_poll_list})
Das Basis-Template könnte dann so aussehen:

Code: Alles auswählen

<html>
<head>
    <title>Meine Seite: {% block title %}{% endblock %}</title>
</head>
<body>
<div class="navigation"><a href="{% url polls.views.index %}">Home</a></div>
<h1>Ich stehe auf allen Seiten!</h1>
{% block content %}
    Ich werde durch später ausgetauscht.
{% endblock %}
{% block App2Spezial %}{% endblock %}
{% block App3Spezial %}{% endblock %}
</body>
</html>
Du definierts in jedem View nur das Child-Template. Das Basistemplate holt sich der Renderer dann von alleine. Im Basistemplate kannst du leer Blöcke angeben die dann nur gerendert werden wenn sie im entsprechenden Child-Template definiert sind.
Cyphron
User
Beiträge: 6
Registriert: Sonntag 7. August 2011, 13:48

Aber wie kann man in diesem Beispiel den Inahlt beider Templates beider Apps in der base.html zeitgleich darstellen? Dazu kommt das z.B. eine App aus meheren Templates besteht die untereinander verlinkt sind:

App1_a:

Code: Alles auswählen

{% extends "templates/index.html" %}
{% block title %}App1 Titel{% endblock %}
{% block content %}
    Ich bin der Content von App 1.
	<a href="{% url app1.views.sub %}">App1 B</a>
{% endblock %}
app1.views.sub ruf dieses Template auf(App1_b):

Code: Alles auswählen

{% extends "templates/index.html" %}
{% block title %}App1 Titel{% endblock %}
{% block content %}
    Ich bin der Content von App 1 <b>B<b>.
{% endblock %}
So habe ich derzeit die beiden verlinkt. Zwar bleibt der Inhalt der Startseite erhalten jedoch wird nicht der Inhalt von App2 angezeigt.
Cyphron
User
Beiträge: 6
Registriert: Sonntag 7. August 2011, 13:48

Ich habe jetzt auch versucht für App1 ein Custom Template Tag zu schreiben. Zwar kann man so den Inhalt beider Apps parallel darstellen, aber man kann sie natürlich nicht mehr von der base.html ableiten. Und wenn auf den Link in App1_a.html geclickt wird ({% url app1.views.sub %}) welcher zu App1_b.html führt ist App2.html selbtverständlich wieder weg.

Ich finde bisher leider keine Andere Lösung als Frames für dieses Problem :(
Benutzeravatar
tjuXx
User
Beiträge: 67
Registriert: Freitag 21. September 2007, 09:25
Wohnort: Bremerhaven
Kontaktdaten:

du kannst dir einen View schreiben der beide Apps läd:

Code: Alles auswählen

#views.py
from django.shortcuts import render_to_response
from app1.models import App1
from app2.models import App2

def myView(request):
    formApp1 = App1()
    formApp2 = App2()
    return render_to_response('pfad/zum/template.html',
                              {'formApp1 ':formApp1 ,
                               'formApp2 ':formApp2 },
                              context_instance=RequestContext(request))
G-Man
User
Beiträge: 2
Registriert: Mittwoch 31. August 2011, 19:41

Hallo zusammen

Ich muss dieses Thema nochmals auffrischen. Seit circa ein bis zwei Wochen beschäftige ich mich nun intensiver mit Django, jedoch habe ich das selbe Problem wie Cyphron anfangs.
Ich möchte keine externen Lösungen (wie PyLucid) verwenden und auch mit der zuletzt von tjuXx vorgeschlagenen Lösung komme ich nicht klar, denn sie funktioniert nicht (vielleicht habe ich etwas falsch gemacht).

Das Problem, nochmals zusammengefasst:
Ich möchte eine App in die Sidebar einbinden. Diese App führt Logik aus und liest Daten aus (liest z. B. Usernamen aus und zeigt Anzahl Posts an). Diese App ist nur nebensächlich, es ist nicht das 'Hauptapp' der aufgerufenen Seite. Es gäbe also Apps für Navigation, Loginbereich, Newsflash, Footer, usw. und solche, die diese Unterapps zusammenfassen und dann die gesamte Seite ausgeben.
Es müssen also zwangsläufig mehrere Views aufgerufen werden.
Diese Unterviews müssen (so wie ich mir das vorstelle) von einer Hauptview aufgerufen werden, welche via urls.py aufgerufen wird.

Ich kann mir nicht vorstellen, dass das so kompliziert sein kann. Bei den meisten Projekten wird ja auf jeder Seite nur der Hauptinhalt geändert, Sidebars, Footer, Navigation, usw.. bleiben gleich (beinhalten jedoch Logik, sind also nicht einfach im base.html abgelegt).

Ich habe mich mal künstlerisch betätigt :wink:
Bild
Wie im Bild dargestellt, wird in der inhaltsspezifischen View nur das ausgegeben, was neu ist. Das Drumherum sollte vorher irgendwo angehängt sein. In einer andere Seite, z. B. example.com/velo/ wären dann die drei Apps links wieder dabei, nur würde statt auto.views.index eben velo.views.index geladen werden.

Wenn ich es wie von tjuXx vorgeschlagen mache, muss ich für jede Seite alle die "Rundeherum"-Teile explizit einbinden. Ich binde sie dann "am Schluss" ein. Ich möchte sie jedoch gerne "so früh wie möglich" einbinden, dass ich am Schluss nur den individuellen Content einbinden muss.
Sonst müsste ich, wollte ich z. B. einen anderen Footer hinzufügen, bei jeder einzelnen View durch, die den Footer ausgeben soll. Das sollte ich jedoch nur an einer Stelle für alle ändern müssen!

Jetzt ist halt die Frage, wie das gemacht wird...

Ich kann mir als mögliche Lösung eine Verkettung vorstellen:
auto.views.index => sidebar.views.userpanel => common.views.header => common.views.footer => common.views.base => Seitenausgabe
Aber ich glaube, das ist bezüglich Variabeln herumschieben/kopieren nicht sonderlich klug. Ebenso gibt es Probleme, weil die Templates mehrmals gerendert würden (auto.views.index würde fünf mal gerendert). Also insgesamt eine unschöne Lösung.


Weiss jemand, wie man das in der Praxis löst?


Vielen Dank schonmals für eure Hilfe!

PS: Ich hoffe nicht, dass ich einen grundlegenden Denkfehler gemacht habe... Ich sehe einfach die Lösung nicht :oops:
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Also normalerweise sind views dazu da, die gesamte Seite für ein urls-pattern aus zu geben. Eine "Verkettung" oder "Ineinanderschachteln" wurde daran nicht gedacht.

Ich denke du hast zwei Möglichkeiten: Entweder du erstellst Template-Tags, die dann die Teile, wie Navigation, Footer ausgeben, oder du machst viel mit Template Vererbung oder einige {% include ... %} Geschichten.

Ich PyLucid habe ich halt ein generisches Template Tag geschrieben, was anhand von Parametern den jeweiligen "view" aufruft. Jeder "view" also Navigation, Footer, könnten aber auch einen eigenen Template Tag haben.

Wenn du dann nicht alle erzeugen Tags explizit mit {% load FooBar %} im Template "laden" willst, kannst du irgendwo sowas wie hier machen: https://github.com/jedie/PyLucid/blob/m ... _init__.py

Eine Lösung wären vielleicht auch die sog. inclusion-tags: https://docs.djangoproject.com/en/1.3/h ... usion-tags

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
G-Man
User
Beiträge: 2
Registriert: Mittwoch 31. August 2011, 19:41

Hi jens

Merci für deine Antwort. Ich habe mich da mal kurz eingelesen. Ich denke, mit den include-Tags kann man schon relativ viel machen, zumindest das Statische. Für alles andere werde ich vermutlich eigene Template Tags erstellen und einbauen, wo ich sie benötige [für alle die es interessiert, es gibt eine gute Anleitung...].

Damit sollte man dann alles machen können, auch den Usernamen anzeigen :D

G-Man
SpiritCrusher
User
Beiträge: 13
Registriert: Montag 11. Januar 2010, 13:59
Wohnort: Köln
Kontaktdaten:

Tag,

wir haben sowas bei uns mit einer Art "Boxen-System" gelöst. Boxen sind in diesem Fall "normale" Python Klassen, die aber eine render Methode haben, die "fertiges" HTML zurückgibt. Die Boxen haben eigene Templates, eigene Logik, usw. Dadurch das die Boxen in den "normalen" views instanziert werden, kann denen allerhand an Paramterern und so mitgegeben werden, und man kann diese Boxen leicht wiederverwenden. Sowas in die Richtung würde ich an deiner Stelle wohl auch machen... Also z.B. ne Navigationsklasse, die das request Object von der view bekommt, z.B. die GET Parameter ausliest, dadurch den aktuellen Menüpunkt highlighten kann und dann ein HTML Snippet rendert und zurückgibt. Das kannste dann überall benutzen. Wobei ne Navigation da ja schon wieder ne Ausnahme darstellt, weil das ja eher was globales ist. Wär dann ja pain das überall manuell einzubinden. Aber naja, das ganze sollte ja nur als Beispiel dienen.

Gruß
Antworten