dynamische html Seite mit Ajax

Django, Flask, Bottle, WSGI, CGI…
erdmulch
User
Beiträge: 230
Registriert: Samstag 17. Juli 2010, 19:50

Hallo zusammen,

hat von euch jemand Erfahrung mit Python, html und Ajax?
ich würde mir gerne Temperaturdaten auf einer Webseite darstellen lassen. da sich die Daten sekündlich ändern würde ich diese gerne aktualisieren lassen und sie so mittels ajax dynamisch anzeigen lassen
Ich bin mal nach folgendem Schema vorgegangen:
http://de.wikibooks.org/wiki/Websiteent ... s_Programm
allerdings weis ich nicht wie man es mit Python macht, muss ich da noch etwas installieren?

danke im voraus
Benutzeravatar
StefanLawl
User
Beiträge: 92
Registriert: Donnerstag 7. Juni 2012, 20:23

Man sagt uns wir sollen der Idee gedenken und nicht des Mannes. Denn ein Mensch kann versagen. Er kann gefangen werden. Er kann getötet und vergessen werden. Aber 400 Jahre später kann eine Idee immer noch die Welt verändern.
-V
Benutzeravatar
noisefloor
User
Beiträge: 3856
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

du musst halt server-seitig was haben, was deinen AJAX-Request des Clients beantwortet. Das sollte sich mit jedem Webframework bewerkstelligen lassen, egal ob "groß" oder "klein". Die konkrete Umsetzung hängt dann vom Framework ab.

@StefanLawl: Die verlinkte Seite sieht ein bisschen alt aus... Da fehlt z.B. Bottle und Flask als auch Pylons / Pyramid.

Gruß, noisefloor
Benutzeravatar
StefanLawl
User
Beiträge: 92
Registriert: Donnerstag 7. Juni 2012, 20:23

Danke für den Hinweis, habe mal das Python Wiki durchsucht:

http://wiki.python.org/moin/WebFrameworks

Sollte ziemlich aktuell sein. Viel Glück :)
Man sagt uns wir sollen der Idee gedenken und nicht des Mannes. Denn ein Mensch kann versagen. Er kann gefangen werden. Er kann getötet und vergessen werden. Aber 400 Jahre später kann eine Idee immer noch die Welt verändern.
-V
erdmulch
User
Beiträge: 230
Registriert: Samstag 17. Juli 2010, 19:50

Hallo zusammen,

ich hab es nun einigermaßen hinbekommen damit Django und Jquery kommunizieren. die Internetseite wird auch aufgerufen und nach einer vorgegebenen Zeit aktualisiert. Allerdings erscheint dann folgende Fehlermeldung:

Code: Alles auswählen

Page not found (404)
Request Method: 	POST
Request URL: 	http://192.168.0.110:9090/home/peter/tmp/djangoprojs/Wetterstation/Wetterstation/articles/views.py

Using the URLconf defined in Wetterstation.urls, Django tried these URL patterns, in this order:

    ^articles/$

The current URL, home/peter/tmp/djangoprojs/Wetterstation/Wetterstation/articles/views.py, didn't match any of these.

You're seeing this error because you have DEBUG = True in your Django settings file. Change that to False, and Django will display a standard 404 page.
der htmlcode sieht wie golgt aus:
<script type="text/javascript">
var xmlHttpObject = false;

if (typeof XMLHttpRequest != 'undefined')
{
xmlHttpObject = new XMLHttpRequest();
}
if (!xmlHttpObject)
{
try
{
xmlHttpObject = new ActiveXObject("Msxml2.XMLHTTP");
}
catch(e)
{
try
{
xmlHttpObject = new ActiveXObject("Microsoft.XMLHTTP");
}
catch(e)
{
xmlHttpObject = null;
}
}
}

function loadContent()
{
xmlHttpObject.open("POST", "/home/peter/tmp/djangoprojs/Wetterstation/Wetterstation/articles/views.py", true);
xmlHttpObject.onreadystatechange = handleContent;
xmlHttpObject.send(null);
return false;




}

function handleContent()
{
if (xmlHttpObject.readyState == 4)
{
document.getElementById('myContent').innerHTML = xmlHttpObject.responseText;
}
}
window.setTimeout("loadContent();", 2000);
</script>

<html>
<body>
<p id="myContent">
<ul>
{% for article in latest_articles_list %}
<li>
<a href="/home/peter/tmp/djangoprojs/Wetterstation/Wetterstation/articles/{{ article.id }}/">
{{article.title}}
</a>
</li>
{%endfor%}
</ul>
</p>
</body>
</html>

komisch finde ich dass er die Seite beim ersten mal aufrufen erkennt. sobald aber jquery versucht die seite neu zu laden erscheint die oben gezeigt Fehlermeldung. Kann mir jemand sagen was ich falsch mache?

muss ich da noch was konfigurieren?

vielen Dank
BlackJack

@erdmulch: Schau mal was Du bei `xmlHttpObject.open()` als Argument übergibst. Das kann natürlich nicht funktionieren, denn das ist ja wohl offensichtlich ein lokaler Pfad und keine URL die Django auflösen kann. Genau das sagt Dir doch auch die Fehlermeldung.

Genau das gleiche Problem gibt es auch bei dem <a>-Tag mit dieser ”URL”.
erdmulch
User
Beiträge: 230
Registriert: Samstag 17. Juli 2010, 19:50

bin leider sehr unerfahren, was mach ich falsch? muss ich eine html seite als Argument liefern?
BlackJack

@erdmulch: Du musst an den Stellen eine URL angeben unter der man die Daten abrufen kann. Das was da steht musst Du in Deinem Browser eingeben können. Du gibst da http://192.168.0.110:9090/home/peter/tm ... s/views.py an, was ganz offensichtlich nicht funktionieren kann.
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

erdmulch hat geschrieben:bin leider sehr unerfahren, was mach ich falsch? muss ich eine html seite als Argument liefern?
Django mappt Aufrufe anhand der Definitionen in urls.py. Dort legst du fest welche Anfrage durch welche Funktion bearbeitet werden soll. Du musst also dort einen entsprechenden regulären Ausdruck definieren der auf das matcht, womit du die Seite aufrufen möchtest.

Teil 3 des Django-Tutorials (V1.4) beschäftigt sich mit dem Aufbau der urls.py.
erdmulch
User
Beiträge: 230
Registriert: Samstag 17. Juli 2010, 19:50

OK, das mit der py Seite habe ich verstanden, das das nicht funktionieren kann.
Nun zur urls.py. Eigentlich soll die Seite nur aktualisiert werden. Die Seite kann ich ja einmal aufrufen. nur dann findet er angeblich die url nicht mehr.

Code: Alles auswählen

from django.conf.urls import patterns, include, url

# Uncomment the next two lines to enable the admin:
# from django.contrib import admin
# admin.autodiscover()

urlpatterns = patterns('',

    (r'^articles/$', 'Wetterstation.articles.views.index'),
)
diese Seite findet er, wenn die Seite allerdings aktualisiert werden soll findet er diese nicht mehr. Was muss ich machen? der Pfad hat sich ja nicht geändert?
BlackJack

@erdmulch: Vergleich doch mal das was Du im Browser eingibst und was zu der Seite führt mit dem was Du dann in der Seite verwendest um auf die gleiche Seite zu kommen. Das ist doch wohl ganz offensichtlich etwas komplett anderes.
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

erdmulch hat geschrieben:diese Seite findet er, wenn die Seite allerdings aktualisiert werden soll findet er diese nicht mehr. Was muss ich machen? der Pfad hat sich ja nicht geändert?
Grundsätzlich ist es egal ob du die Seite direkt aufrufst oder mit Ajax-Methoden. Die Idee bei Ajax ist es allerdings, nur Teile der Seite auszutauschen. Dafür liefert die View dann auch nur HTML-Fragmente oder ein JSON-Objekt zurück das dann wiederum vom clientseitigen JavaScript-Code passend verarbeitet wird.

Ich finde es daher ein wenig erstaunlich, dass du nur ein einziges Mapping in der urls.py stehen hast. Das angegebene Mapping ist doch wohl eher für den kompletten Seitenabruf, aber nicht für den Abruf via Ajax gedacht.

Bei einem meiner Projekte sieht die urls.py wie folgt aus:

Code: Alles auswählen

urlpatterns = patterns('bdh.views',
    url(r'^$', 'index',
        name='bdh.index'),
    [...]
    url(r'^ajx_attr_change/$', 'ajx_attr_change',
        name='bdh.ajx_attr_change'),
    url(r'^ajx_name_change/$', 'ajx_name_change',
        name='bdh.ajx_name_change'),
    url(r'^ajx_skill_change/$', "ajx_skill_change",
        name='bdh.ajx_skill_change'),
    [...]
Der Aufruf via Javascript unter Verwendung von jQuery erfolgt dann beispielsweise so:

Code: Alles auswählen

	[...]
	$.getJSON("{% url bdh.ajx_attr_change %}", data,
		function(data) {
			$.each(data, function(key, value) {
				$("#att_" + key).html(value);
			});
			$("#count_attributes").html("(" + data["attr_total_count"] + ")");
		}
Beachte bitte, dass ich mit passender Namensvergabe für die URLs gearbeitet habe. Eine Änderung in der urls.py schlägt sich dann durch die Verwendung von {% url ... %} automatisch im Template nieder und ich muss mir keine Gedanken darüber machen welche URL ich jetzt exakt anpassen oder ändern muss.

Die in der urls.py gemappte Funktion ajx_attr_change endet dann wie folgt (der komplette Code macht ohne den gesamten Kontext keinen Sinn):

Code: Alles auswählen

    [...]
    derived_atts = c.get_derived_attributes()
    derived_atts['attr_total_count'] = c.count_attribute_points()

    json_data = simplejson.dumps(derived_atts)
    return HttpResponse(json_data, mimetype='application/json')
erdmulch
User
Beiträge: 230
Registriert: Samstag 17. Juli 2010, 19:50

darf ich fragen woher man so was weiß? hast du ein Buch in dem sowas steht? kann man sowas wissen?
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

erdmulch hat geschrieben:darf ich fragen woher man so was weiß? hast du ein Buch in dem sowas steht? kann man sowas wissen?
Na ja, mir schien das zu Anfang auch recht komplex, vor allem, da ich mit JavaScript nie wirklich gearbeitet habe. Gut, dass ein Aufruf via Ajax nichts anderes als ein HTTP-Request ist war schon klar, aber die konkrete Umsetzung war dann doch etwas nebulös.

Ich habe mich dann einfach mal hingesetzt, ein wenig die Suchmaschinen bemüht, jQuery gefunden, die Dokumentation dazu gelesen und experimentiert. Nach etwa 4 Stunden konnte ich dann einigermaßen damit umgehen. Aktuell reicht mein Wissen noch aus für das was ich umsetzen möchte, aber ich werde mich wohl in Zukunft noch etwas tiefer einarbeiten.

Inzwischen habe ich noch ein Buch namens "Django JavaScript Integration: AJAX und jQuery" gekauft und kann es ausdrücklich nicht empfehlen.

Die Sache mit den mit Namen versehenen URLs in der urls.py findet sich übrigens ganz normal in der Django-Dokumentation. Ich habe nirgendwo in meinen Templates mehr fixe Pfade stehen, sondern immer nur Referenzen via {% url ... %}.
erdmulch
User
Beiträge: 230
Registriert: Samstag 17. Juli 2010, 19:50

ich versteh gerade nur Bahnhof ich brauch da mal ein praktisches Beispiel:
und zwar sieht so meine View aus, bei ihr wird eine Datenbankverbindung gemacht und leist die 5 letzten Einträge aus:

Code: Alles auswählen

def index(request):
    "Create a list of the 5 latest articles"
    latest_articles_list = Article.objects.all().order_by('-pub_date')[:5]
    t = loader.get_template('articles.html')
    c = Context({'latest_articles_list': latest_articles_list,})
    return HttpResponse(t.render(c)) 
wie gesagt das ist meine urls.py

Code: Alles auswählen

from django.conf.urls import patterns, include, url

# Uncomment the next two lines to enable the admin:
# from django.contrib import admin
# admin.autodiscover()

urlpatterns = patterns('',

    (r'^articles/$', 'Wetterstation.articles.views.index'),
)
und hier ist meine html seite:

<script type="text/javascript">
var xmlHttpObject = false;

if (typeof XMLHttpRequest != 'undefined')
{
xmlHttpObject = new XMLHttpRequest();
}
if (!xmlHttpObject)
{
try
{
xmlHttpObject = new ActiveXObject("Msxml2.XMLHTTP");
}
catch(e)
{
try
{
xmlHttpObject = new ActiveXObject("Microsoft.XMLHTTP");
}
catch(e)
{
xmlHttpObject = null;
}
}
}

function loadContent()
{
xmlHttpObject.open("GET", "/home/peter/tmp/djangoprojs/Wetterstation/Wetterstation/templates/articles/articles.html", true);
xmlHttpObject.onreadystatechange = handleContent();
xmlHttpObject.send(null);
return false;
}

function handleContent()
{
if (xmlHttpObject.readyState == 4)
{
document.getElementById('myContent').innerHTML = xmlHttpObject.responseText;
}
}

setInterval("loadContent();", 2000);

</script>

<html>
<body>
<p id="myContent">
<ul>
{% for article in latest_articles_list %}
<li>
<a href="/home/peter/tmp/djangoprojs/Wetterstation/Wetterstation/articles/{{ article.id }}/">
{{article.title}}
</a>
</li>
{%endfor%}
</ul>
</p>
</body>
</html>




was muss ich ändern? will nur die Datenbankeinträge aktualisiert haben?
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

erdmulch hat geschrieben:was muss ich ändern? will nur die Datenbankeinträge aktualisiert haben?
Die index-View kannst du dafür nicht verwenden, da die die komplette HTML-Seite schickt. Was du brauchst ist eine zweite View die dir nur das gewünschte HTML-Fragment zurückgibt das in das DOM-Element mit der ID myContent eingefügt werden soll.

Du brauchst also:
  • ein Template das nicht die ganze Seite sondern nur den auszutauschenden Teil umfasst
  • eine View die dieses Template mit Daten füllt und zurückgibt
  • einen Eintrag in der urls.py die auf dieses Template verweist
  • einen Link in der aufrufenden Datei der auf den Eintrag in der urls.py passt und mit Ajax-Mitteln abgeschickt wird
Im Endeffekt unterscheidet sich das also prinzipiell nicht von dem was du machen musst wenn du eine "normale" weitere View hinzufügst.


Du solltest dir aber ernsthaft mal jQuery ansehen. Bei Verwendung dieser JavaScript-Bibliothek kannst du dir eine Menge von der manuellen Rumfummelei zum Erzeugen des HttpRequests und zum Abrufen der Daten sparen.
erdmulch
User
Beiträge: 230
Registriert: Samstag 17. Juli 2010, 19:50

ok, im mom handelt es sich bei meinem template nur um einen Datenbank zugriff. Es stehen also 5 Zeilen auf meinem Browser. Muss ich jetzt das Template mit den Funktionen kopieren? kann ich nicht das bestehen de verwenden? sodass immer das gleich aufgerufen wird? würde es erst einfach machen bevor ich nur den gewissen teil aktualisiere. bei den Anzahl von Daten die empfange spielt es ja keine rolle, da keine aufwendige Grafik etc. verwendet wird
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

erdmulch hat geschrieben:ok, im mom handelt es sich bei meinem template nur um einen Datenbank zugriff.
Ich fürchte wir haben hier ein Verständnisproblem oder alternativ ein Problem mit der präzisen Bezeichnung.

Das Template ist HTML und hat keinen Datenbankzugriff. Der Datenbankzugriff erfolgt in deiner View die die Daten dem Template zur Verfügung stellt.

Jetzt überleg dir mal was passiert, wenn du das geparste Template für die komplette Webseite via Ajax schickst. Du hast da diese Codezeile im Javascript:

Code: Alles auswählen

document.getElementById('myContent').innerHTML = xmlHttpObject.responseText;
Dort würdest du dem Inhalt des DOM-Elementes mit der ID myContent noch einmal die komplette Seite hinzufügen und plötzlich hättest du eine völlig verhauene und ungültige Struktur in deinem HTML-Dokument.

Es ist wirklich nicht schwer das ganze einmal zu testen. Gib doch einfach mal in der von Ajax aufgerufenen View einen festen String statt ein komplett ausgefülltes Template zurück. Damit kannst du dann schon mal überprüfen ob der Mechanismus an sich funktioniert. Danach kannst du dann an die Feinheiten gehen.

Solltest du AJAX POST verwenden, dann könntest du noch in ein Problem mit CSRF (Cross Site Request Forgery) laufen. Lösungsmöglichkeiten dafür stehen in der Django-Dokumentation unter https://docs.djangoproject.com/en/1.4/ref/contrib/csrf/. Auch dort wird übrigens auf jQuery gesetzt.

Ich habe ohnehin den Eindruck als neigtest du dazu, Dinge aufwändiger umzusetzen als es erforderlich ist. Wenn dir passende Werkzeuge zur Verfügung stehen dann nutze sie auch. Schon in deinem Python-Code erledigst du zu viel von Hand.

Code: Alles auswählen

def index(request):
    "Create a list of the 5 latest articles"
    latest_articles_list = Article.objects.all().order_by('-pub_date')[:5]
    t = loader.get_template('articles.html')
    c = Context({'latest_articles_list': latest_articles_list,})
    return HttpResponse(t.render(c)) 
Einfacher wäre es so:

Code: Alles auswählen

from django.shortcuts import render
def index(request):
    "Create a list of the 5 latest articles"
    latest_articles_list = Article.objects.all().order_by('-pub_date')[:5]
    return render(request, 'articles.html', {'latest_articles_list': latest_articles_list,})
erdmulch
User
Beiträge: 230
Registriert: Samstag 17. Juli 2010, 19:50

hallo nochmals,

was ist da Aufwendig? im Prinzip lade ich doch immer nur die Datenbankverbindungseinträge neu.
wie kann man es anders machen?
kann mir da jemand einen tipp geben?
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

erdmulch hat geschrieben:was ist da Aufwendig? im Prinzip lade ich doch immer nur die Datenbankverbindungseinträge neu.
Die was? Datenbankverbindungseinträge? Es wäre wirklich einfacher, wenn du Fachbegriffe so verwendest wie sie vorgesehen sind.
erdmulch hat geschrieben:kann mir da jemand einen tipp geben?
Einen Tipp hätte ich. Lies doch bitte mal etwas genauer und intensiver. Wie dir offensichtlich nicht aufgefallen ist, habe ich die Codestelle, zu der du jetzt ratlos fragst, überhaupt nicht gemeint und sie auch so stehen lassen wie sie stand. Die Änderung ist in den Zeilen danach.
Antworten