Seite 1 von 1

Django und erstellte Dateien downloaden

Verfasst: Mittwoch 3. September 2008, 13:20
von würmchen
Hallo Leute,
ich hab mit Django ein Webinterface geschrieben was mir mehrere Textdateien schreibt. Jetzt suche ich nach einer brauchbaren Lösung wie ich die erstellten Dateien zur Verfügung stelle.

Das senden der Informationen aus dem Interface zu meinem Pythonscript geschieht über AJAX an einen View.

Mein Problem, das erstellen der Dateien kann schon mal mehrere Minuten dauern. Dh, meine view sollte irgendetwas zurückgeben, damit der AJAX Request nicht abgebrochen wird.
Meine Idee war einen Link zu einem Ordner zurück zu geben. In diesem Ordner will ich die Textdateien speichern, damit der User diese runterladen kann.

Kann mir jemand sagen welchen return ich in dem View machen muss? Wie kann ich das mit der Zeit lösen, also das ein User schon den Link bekommt, obwohl das ganze Script noch nicht fertig ist.

Verfasst: Mittwoch 3. September 2008, 14:19
von jens
Der User bekommt keinen direkten Link zu einer Datei, sondern immer den selben Link zu einer Art "persönlichen Download Ordner" in dem alle schon fertig generierten Dateien aufgelistet werden.

Verfasst: Mittwoch 3. September 2008, 14:22
von würmchen
Das ist genau sowas was ich mir vorstelle. Hast Du eine Idee wie ich sowas realisiere? Bzw ne Quelle?

Verfasst: Freitag 12. September 2008, 09:54
von würmchen
jens hat geschrieben:Der User bekommt keinen direkten Link zu einer Datei, sondern immer den selben Link zu einer Art "persönlichen Download Ordner" in dem alle schon fertig generierten Dateien aufgelistet werden.
Hallo Jens,
ich würde gerne Deine Idee aufgreifen, mir fehlen im Moment ein paar Hinweise wie ich da vorgehen sollte.
Zum einen, was muss ich dem Browser für einen return geben, damit im Hintergrund mein pythoncode weiter arbeiten kann.

Zum anderen dieser quasi persönliche ordner, ich denke den muss ich doch irgendwie in meine urls.py eintrage damit ich da von außen drauf kann, oder?

Ich hab leider echt keine Idee wie ich da vorgehen soll und wäre für einen Hinweis dankbar...

Verfasst: Freitag 12. September 2008, 10:28
von Leonidas
würmchen hat geschrieben:Zum einen, was muss ich dem Browser für einen return geben, damit im Hintergrund mein pythoncode weiter arbeiten kann.
Einen Redirect zur generierten Datei. Oder, und das macht der MVV so, eine Seite mit "Datei anfordern" Link.
würmchen hat geschrieben:Zum anderen dieser quasi persönliche ordner, ich denke den muss ich doch irgendwie in meine urls.py eintrage damit ich da von außen drauf kann, oder?
Wieso? Ich würde das in die statischen Dateien tun, das ist ja Unsinn es von Django ausliefern zu lassen.

Verfasst: Freitag 12. September 2008, 10:28
von jens
Der Persönliche Ordner ist ja quasi nichts anderes, als die Daten welche Datei welcher User laden darf.
Als erstes würde ich mal das Model dazu aufbauen. Dann ein view der die Daten des Models anzeigt und da abhängig davon welcher User gerade eingeloggt ist.
Die urls muß natürlich auf diesen view zeigt...

EDIT: Das Grundproblem bleibt aber: Die sicherheit das nur ein berechtigter Anwender die Dateien laden kann.
Man kann die Dateien nicht im http doc root legen. Somit sind diese nicht per Apache downloadbar. Dann muß die django app selber die Dateien ausliefern, was etwas mehr Preformance kosten.
Eine andere Variante wäre es evtl. eine .htaccess dynamisch zu generieren und die Passwörter von django darein zu stecken. Oder aber den Zugriff auf die IP Adresse zu limitieren (was aber wohl nicht ganz sicher ist)...

Verfasst: Montag 15. September 2008, 08:31
von würmchen
Einen Redirect zur generierten Datei. Oder, und das macht der MVV so, eine Seite mit "Datei anfordern" Link.
[/quote]
Genau so hab ich mir das auch vorgestellt. Mein Problem ist, das ich diese Abfrage mit AJAX sende und da auf eine Antwort gewartet wird. Ich mache das mit Jquery:

Code: Alles auswählen

	$("#get_all_pdb").livequery("click",function(){
		var pdb_keys = ""
		$("td.entry_key input[@type=checkbox][@checked]").each(function() {
			pdb_keys = pdb_keys+" "+$(this).attr("value");
		});
		$.ajax({
			type: "POST",
			timeout: 600000,
			data: "entry_key_string="+pdb_keys+"&database="+$("p input[@type=radio][@checked]").attr("value"),
			url: "createpdb/",
			beforeSend: function(){
				$("#loader").show();
			},
			success: function(data) {
				$('#result').after(data)
			},
			complete: function() {
				$("#loader").hide();
			}
		});
	});
Meine view sieht an der Stelle so aus:

Code: Alles auswählen

def createAllPdb(request):
    from bif.interface.interface import WebInterface
    from bif.interface.writer import CreatePdbFiles
    from bif.interface import output
    interface = WebInterface(request.POST['database'],
            request.session.get('tablename'))
    cursor = interface.getCursor()
    creator = CreatePdbFiles(cursor,'logfile.log')
    creator.createAllPdbFiles(request.POST['entry_key_string'])

    return render_to_response('interface/addinfo.html',
            {'result': 'nix',
                'string':''})
Jetzt fängt bei dem Funktionsaufruf createAllPdbFiles() mein Script an zu arbeiten und das brauch eben seine Zeit. WÄREND das arbeitet würde ich gerne auf eine Seite umleiten die mir diesen Link präsentiert auf dem der User dann nach XX Minuten die fertigen Dateien downloaden kann.
Ich hab aber keine Ahnung wie ich das vorzeitig "melde"...


Leonidas hat geschrieben:Wieso? Ich würde das in die statischen Dateien tun, das ist ja Unsinn es von Django ausliefern zu lassen.
Ich dachte mir jetzt auch das ich das mit richtigen Dateien mache und diese in einem Ordner der eben direkt zugänglich ist bereit stelle.

Wäre für einen Tip nochmal dankbar.

Verfasst: Montag 15. September 2008, 09:36
von Leonidas
Ich würde im View eine Art Token generieren, was an den User zurückgeschickt wird und gleichzeitig an den Dateigenerator in einem anderen Thread. Somit sieht der User den Link und die Dateil kann im Hintergrund erstellt werden.

Letztendlich hat man da dummerweise Race Conditions, d.h. der User kann schneller klicken als das die Datei erstellt wird. :?

Verfasst: Montag 15. September 2008, 09:58
von würmchen
Leonidas hat geschrieben:Ich würde im View eine Art Token generieren, was an den User zurückgeschickt wird und gleichzeitig an den Dateigenerator in einem anderen Thread. Somit sieht der User den Link und die Dateil kann im Hintergrund erstellt werden.

Letztendlich hat man da dummerweise Race Conditions, d.h. der User kann schneller klicken als das die Datei erstellt wird. :?
Bin am überlegen wie ich das mit dem Token machen sollte. Ich denke gerade an sowas wie ein Systemaufruf mit & oder sowas in der Art. Mit Python threading habe ich keine Erfahrung. gibt es Befehle wie ich einen Funktionsaufruf im Hintergrund starten kann?

Mit der Race Condition will ich mir eine view erstellen, die den Inhalt des Ordners anzeigt, und somit die fertigen Dateien, wenn eine bestimmte Datei (xyz.log) vorhanden ist. Diese wollte ich dann einfach erstellen lassen wenn mein Script fertig ist. Ansonsten eben sowas wie "Dateien noch nicht ganz fertig, versuchen Sie es in wenigen Minuten noch einmal" oder sowas.

Verfasst: Montag 15. September 2008, 10:12
von Leonidas
würmchen hat geschrieben:Bin am überlegen wie ich das mit dem Token machen sollte. Ich denke gerade an sowas wie ein Systemaufruf mit & oder sowas in der Art. Mit Python threading habe ich keine Erfahrung. gibt es Befehle wie ich einen Funktionsaufruf im Hintergrund starten kann?
Klar, geht beides. Mit Prozessen nutzt du einfach subprocess und lässt es asynchron laufen und mit Threads gibt es das Thread und threading Modul. Gerade ``thread.start_new_thread()`` ist, obwohl ich es nicht so besonders mag, sehr einfach zu nutzen.

Verfasst: Montag 15. September 2008, 10:30
von würmchen
Damit ich das richtig verstehe, dazu muss ich aber ein ausführbares python Script schreiben, wessen ich mit ARGV oder so Übergabeparameter übergebe. Ich kann nicht in meiner View das erzeugte Objekt und NUR die Funktion in einem extra Thread aufrufen, oder?

Verfasst: Montag 15. September 2008, 10:44
von Leonidas
würmchen hat geschrieben:Damit ich das richtig verstehe, dazu muss ich aber ein ausführbares python Script schreiben, wessen ich mit ARGV oder so Übergabeparameter übergebe.
Genau.
würmchen hat geschrieben:Ich kann nicht in meiner View das erzeugte Objekt und NUR die Funktion in einem extra Thread aufrufen, oder?
Doch. Jetzt mal vorrausgesetzt das ganze ist threadsafe, aber sonst ja.

Verfasst: Montag 15. September 2008, 10:51
von würmchen
Also das ganze sollte Threadsave sein, lesen tue ich Daten aus einer Datenbank, schreiben werde ich dann in einen eigenen Ordner...
Es würde dann ein Thread alle Dateien erstellen.

Hast Du zufällig ein gutes Beispiel für die Arbeit mit Threads? Hatte mal in Java mit Threads programmiert, also Grundkenntnisse sind da. Ich denke ich muss dann ein Threadclasse erzeugen und dieser dann das Objekt übergeben, die Threadklasse ruft dann die Funktion auf, was im Moment die View macht.

Hm, geht es dann direkt weiter in meiner view oder wird dann auch noch auf einen Rückgabewert gewartet oder sowas?

Verfasst: Montag 15. September 2008, 11:28
von Leonidas
würmchen hat geschrieben:Hast Du zufällig ein gutes Beispiel für die Arbeit mit Threads? Hatte mal in Java mit Threads programmiert, also Grundkenntnisse sind da. Ich denke ich muss dann ein Threadclasse erzeugen und dieser dann das Objekt übergeben, die Threadklasse ruft dann die Funktion auf, was im Moment die View macht.
Ich habe dir doch gesagt was du brauchst, jetzt kannst du in der Dokumentation nachschlagen und/oder die Suchfunktion benuten um Beispiele zu finden.
würmchen hat geschrieben:Hm, geht es dann direkt weiter in meiner view oder wird dann auch noch auf einen Rückgabewert gewartet oder sowas?
Auf was für einen Rückgabewert willst du denn warten? Wenn du den Thread synchronisierst, wozu brauchst du ihn dann überhaupt? ;)

Verfasst: Dienstag 18. November 2008, 17:08
von würmchen
Hallo, ich hab erst jetzt wieder Zeit um mich um dieses Problem zu kümmern und wollte jetzt nochmal um Hilfe bitten.

Das mit den Threads funktioniert und ich kann mir einen Link zurück geben lassen.

Ich wollte fragen, ob ich diesen Link gleich in einem neuen Fenster öffnen kann? Sozusagen als popup?

Sollte möglichst beides geschehen, also einmal den Link anzeigen und zum anderen den Link gleich in einem neuen Fenster öffnen.

Danke für eure Hilfe

Verfasst: Samstag 22. November 2008, 10:38
von sma
Suchst du dies?

Code: Alles auswählen

<a href="..." target="_blank">Download</a>
Stefan

Verfasst: Dienstag 25. November 2008, 16:33
von würmchen
Danke für die Antwort,
ich habe es mittlerweile so erledigt, dass ich einen button erzeuge, mit dem ich auf meine Downloadseite komme.
Diese ist dann einfach nur eine Liste mit Dateien, die bereits fertig erstellt wurden.
Dazu hab ich ein Model erzeugt, was userid, einen key, filename und location und so speichert und zusätzlich, ob die datei noch in arbeit ist oder schon fertig geschrieben wurde.

diese seite lasse ich mit javascript alle 20 sekunden neu laden, bis alle dateien erstellt wurden.

danke für eure hilfe