Webserver auf Raspian (Base: Debian) einrichten mit Django

Django, Flask, Bottle, WSGI, CGI…
Antworten
rbaert
User
Beiträge: 20
Registriert: Mittwoch 5. September 2018, 15:37

Hallo zusammen...
Ich habe unter Windows eine Homepage mit Django programmiert. Diese möchte ich nun auf einen Raspberry Pi laufen lassen. Mit dem Web-Interface, wird dann ein LED-Panel gesteuert. Daher soll die Seite nur aus dem lokalen Netz erreicht werden können.
Ich habe nun diverse Tutorials durchgeschaut und muss zugeben, dass ich wohl etwas zu doof dafür bin... :oops: sorry an denjenigen der mir antwortet, bin leider immer noch Anfänger...

Ausgangslage:
- Raspian Stretch (läuft)
- Python 3 (läuft)
- Django 2 (programmiert auf Django 2.1.1, habe es leider noch nicht geschafft diese Version auch auf dem Raspi zu installieren, da läuft aktuell v.
1.11.15 Dies ist aber ein Thema, für einen anderen Tag)
- Webserver Apache2 (läuft, default-Page wird angezeigt)
- mod_wsgi_py3 (installiert)

Fragen:
1. Bei allen Tutorials die ich bisher gefunden habe, muss ei virtueller Host (virtualenv) genutzt werden. Kann ich apache auch ohne konfigurieren?
(Es wird auf diesem Raspi immer nur diese Seite laufen)
2. Was muss ich machen, damit meine Page nur lokal sichtbar ist?
3. Wie verbinde ich nun meine Django-Projekt mit apache? (Sorry, hab bisher leider nicht viel kapiert...)

Ich bin auch zufrieden, mit einer guten Anleitung im Internet. Habe bisher leider nichts gefunden, fürs lokale Netz.

Danke schon mal und sorry für meine Unwissenheit....
Gruss Roland
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@rbaert: Virtueller Host und virtualenv sind zwei verschiedene Dinge. Du brauchst beides nicht zwingend.

Wenn die Seite nur lokal sichtbar sein soll, könntest Du entweder den Webserver nur an ein lokales Interface binden, oder Deiner Anwendung dafür sorgen, das die nur mit Anfragen von IPs aus dem lokalen Netz beantwortet. Beziehungsweise andernfalls mit einer entsprechenden Fehlerseite antwortet.

Das die Leute bei gut dokumentierten Projekten trotzdem immer sagen sie finden nichts… Bei Django in der Dokumentation gibt es natürlich auch Informationen über gängige Möglichkeiten die Anwendung ans Netz zu bringen: https://docs.djangoproject.com/en/1.11/ ... eployment/
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
rbaert
User
Beiträge: 20
Registriert: Mittwoch 5. September 2018, 15:37

Hallo __blackjack__
Danke erstmal für die schnelle Hilfe.
Der Grund, warum ich trotz guter Dokumentationen manchmal nicht weiter komme, sind leider meine schlechten Englisch-Kenntnisse... (ich weis no go... ich arbeite dran).

Ich werde es mit deinem Link versuchen. Danke schon mal...
nezzcarth
User
Beiträge: 1632
Registriert: Samstag 16. April 2011, 12:47

Eine andere Frage wäre auch, ob du den Apache für eine ausschließlich lokale Seite brauchst. Reichen da nicht der eingebaute Entwicklungsserver oder Gunicorn? Falls es Apache sein muss, wäre es auch denkbar, ihn mit 'mod_proxy' davor zu schalten; dieser Weg wird m.W.n. weniger häufig gewählt, geht aber auch.

Das 'virtualenv(ironment)' ist hier hilfreich, um eben eine Python-Umgebung zu erzeugen, die die notwenigen Pakete enthält ohne mit dem, was die Distribution mitbringt in Konflikt zu geraten. So kannst du auch die entsprechende Django Version installieren und verwenden.
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich wuerde auch ohne Apache arbeiten, oder bestenfalls als Proxy oder gar nur Weiterleitung. Fuer ein solches lokales System ist der Unterschied wohl kaum spuerbar von der Geschwindigkeit her, aber dafuer die Komplexitaet offensichtlich eine Herausforderung.
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

Für ein lokales System kann der in Django integrierte Entwicklungsserver völlig ausreichend sein.

@rbaert: welche Last erwartest Du denn?
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

@kbr: er faehrt auf nem Pi... also verschwindend geringe ;)
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

@__deets__: Das war auch eher eine rhetorische Frage an den TE. Bei vielleicht drei Requests pro Minute (oder vielleicht sogar pro Stunde) braucht es keinen apache ... :)
rbaert
User
Beiträge: 20
Registriert: Mittwoch 5. September 2018, 15:37

Hallo zusammen...
Danke für eure Antworten...
Ich erwarte eine sehr, sehr geringe Last. Der User wird vielleicht 1x pro Tag darauf zugreifen, wenn überhaupt. Daher gebe ich euch Recht, dass der Entwicklungs-Server völlig ausreichen würde.
Aber: Ich sehe das ganze als einen Lernprozess. Dieses Projekt, wäre mit Hard-Codierung völlig ausreichend gewesen. Das Projekt hat aber meiner Meinung nach die richtige Grösse um anzufangen. Daher habe ich das Ganze erweiterbar, mit SQLite3 Datenbank und Apache machen wollen. Wenn dann nicht alles gleich klappt, macht das nichts. Es wird zwar in der Firma eingesetzt, ist aber logischerweise fürs Tagesgeschäft nicht wirklich wichtig. Also super Lern-Projekt um Fehler zu machen und daraus zu lernen.

Stand der Dinge:
Apache (läuft)...Nach mehreren Versuchen hat es geklappt. Ich habe meinen ersten Webserver eingerichtet. Super, bin fast schon stolz... :lol:

Aber nun habe ich leider noch ein anderes Problem, dass erst aufgetreten ist, seit ich meine Programmdateien auf dem Raspberri laufen lasse. Die Seite kann aufgerufen werden. Alles funktioniert, ausser eine Page. Da wird 'TemplateDoesNotExist' angezeigt.
Dieses Template liegt aber im gleichen Ordner wie die anderen und hat auch die gleichen Rechte.

Code: Alles auswählen

pi@raspberrypi:~/LEDController/home/templates/sites $ ls -l
total 24
-rw-rw-r-- 1 www-data www-data  970 Sep 11 21:04 base.html
-rw-rw-r-- 1 www-data www-data 1901 Sep 22 09:53 home.html
-rw-rw-r-- 1 www-data www-data 3258 Sep 22 09:45 new_String.html
-rw-rw-r-- 1 www-data www-data  354 Sep 11 21:12 root.html
-rw-rw-r-- 1 www-data www-data 1238 Sep 22 09:26 settings.html
-rw-rw-r-- 1 www-data www-data  764 Sep 20 23:59 temp.html
Es geht hier um die new_String.html. Diese will er einfach nicht finden...Meldung:
[/b]django.template.loaders.app_directories.Loader: /home/pi/LEDController/home/templates/sites/new_string.html (Source does not exist)

In den Settings.py habe ich es bereits mit dem absoluten Pfad versucht. Leider auch kein anderes Ergebnis:

Code: Alles auswählen

TEMPLATES
[{'APP_DIRS': True,
  'BACKEND': 'django.template.backends.django.DjangoTemplates',
  'DIRS': ['/home/pi/LEDController/home/templates/sites'],
  'OPTIONS': {'context_processors': ['django.template.context_processors.debug',
                                     'django.template.context_processors.request',
                                     'django.contrib.auth.context_processors.auth',
                                     'django.contrib.messages.context_processors.messages']}}]
Die Pfadangabe in der URL.py ist bei dieser überladen, da ich einen optionalen Parameter habe:

Code: Alles auswählen

    path('neu/', new_String, name='new_String'),
    path('neu/<int:pk>', new_String, name='new_String'),
Und hier noch die View dazu (oh man, die ist inzwischen etwas gewachsen, sorry :oops: ):

Code: Alles auswählen

def new_String(request, pk=None):
    attribute_list = Attribut.objects.all().filter(visible=True)
    placeholders = Placeholders.objects.all()
    if pk == None:
        active_txt = None
        active_options = None
        active_placeholders = None
        if request.method == 'POST':
            txt_display_text = request.POST.get('display_text')
            m = Display_Text(text=txt_display_text)
            m.save()
            for werte in attribute_list:
                if request.POST.get(werte.name, '') != None:
                    inst = Attribut_Option.objects.get(id=request.POST.get(werte.name))
                    n = m.dtao_set.create(attribut_option = inst)
            m.save()
            for werte in placeholders:
                if request.POST.get('activate_' + werte.titel) != None:
                    datum = request.POST.get('date_' + str(werte.id))
                    datum = time.strftime('%d.%m.%Y', time.strptime(datum, '%Y-%m-%d'))
                    j = Placeholders.objects.get(id = werte.id)
                    k = m.dtph_set.create(placeholders=j, placeholder_value=datum)
                    m.save()
            return redirect(home)
    else:
        active_txt = Display_Text.objects.get(id = pk)
        active_options = DTAO.objects.raw('SELECT id, attribut_option_id from dtao where text_id= %s', [pk])
        active_placeholders = DTPH.objects.raw('SELECT id, placeholders_id from dtph where text_id= %s', [pk])
        if request.method == 'POST':
            txt_display_text = request.POST.get('display_text')
            m = Display_Text.objects.get(id = pk)
            m.text = txt_display_text
            m.save()
            m.dtao_set.all().delete()
            for werte in attribute_list:
                if request.POST.get(werte.name, '') != None:
                    inst = Attribut_Option.objects.get(id=request.POST.get(werte.name))
                    n = m.dtao_set.create(attribut_option = inst)
            m.dtph_set.all().delete()
            m.save()
            for werte in placeholders:
                if request.POST.get('activate_' + werte.titel) != None:
                    datum = request.POST.get('date_' + str(werte.id))
                    datum = time.strftime('%d.%m.%Y', time.strptime(datum, '%Y-%m-%d'))
                    j = Placeholders.objects.get(id = werte.id)
                    k = m.dtph_set.create(placeholders=j, placeholder_value=datum)
                    m.save()
            return redirect(home)
    context = {'attribute': attribute_list,
               'active_txt': active_txt,  
               'active_options': active_options,
               'placeholders': placeholders,
               'active_placeholders': active_placeholders,
               'newString_page': 'active'}
    return render(request, 'sites/new_string.html', context)
Mir gehen leider die Ideen aus, wo der Fehler noch liegen könnte. Hat jemand von euch noch eine Idee?

Danke im voraus
Roland
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Er sucht eine Datei mit kleinem `s`.

Zum Code: Du hast in der Funktion `new_String ` im if und im else-Block bis auf die ersten zwei Zeilen identischen Code. Der sollte nach dem if-else stehen.

Vergleiche mit None wird üblicherweise mit `is` bzw. `is not` gemacht.
Warum benutzt Du raw-SELECT?
rbaert
User
Beiträge: 20
Registriert: Mittwoch 5. September 2018, 15:37

autsch.... :oops: Error 'dummer User'
Danke dir...
Nun zu meiner (zugegeben) Hobby, Programmierung:

Wo meinst du genau? Ist meiner Meinung nach nicht immer gleich, da beim ersten If, alle Werte verarbeitet werden, beim zweiten nur der mit pk==...
Es kann aber durchaus sein, dass das Ganze kompakter sein könnt, also schaue ich vielleicht am falschen Ort...

Die raw habe ich gemacht, weil ich mit meiner Geduld am Ende war. Ich arbeite in meinem Job täglich an T-SQL Skripten. Ich habe mir vorgenommen, das nächste Projekt mit den in Python üblichen connection und SQL-Abfragen zu realisieren. Ich muss zugeben, dass ich mit der Django-Datenbank-Sprache nicht wirklich warm geworden bin. Ich finde vieles davon zu kompliziert (liegt wahrscheinlich auch daran, dass SQL mir logischer erscheint, weil ich das jeden Tag brauche).

Es würde mich aber trotzdem noch interessieren, wie ich es hätte machen können?

Code: Alles auswählen

        <h3>Effekte:</h3>
        <ul class="effects">
            {% for m in attribute %}
            <li>
                <label>{{ m.name }}</label>
                <select name="{{ m.name }}">
                    {% for e in m.attribut_option_set.all %}
                        {% if active_txt == None %}
                            {% if m.default_index == e.counter_index %}
                                <option value="{{ e.id }}" selected="selected">{{ e.optionname }}</option>
                            {% else %}
                                <option value="{{ e.id }}">{{ e.optionname }}</option>
                            {% endif %}
                        {% else %}
                            <option value="{{ e.id }}" 
                            {% for j in active_options %}
                                {% if j.attribut_option_id == e.id %}                                [color=#FF0000]---> Hier[/color]
                                    selected="selected"
                                {% endif %}
                            {% endfor %}
                            >{{ e.optionname }}</option>
                        {% endif %}
                    {% endfor %}
                </select>
            </li>
            {% endfor %}
        </ul>
Ich habe dir die Stelle markiert, die ich nicht in der Django-Template Sprache geschafft habe. Jedem Text werden Optionen mitgegeben (mit n:m Zusatztabelle bestehend aus Fremdschlüssel zur Option und zur Texttabelle).

Zudem hat jedes Attribut (Choice-Field) zugeordnete Optionen. Wenn ich einen neuen String erstelle, sind die Standard-Values ausgewählt (diese befinden sich in der Tabelle der Attribute in sep. Column).

Nun wollte ich erreichen, dass wenn ein Text bearbeitet wird, die Standart-Values von der n:m zwischen-Tabelle(zischen Text und Option) genommen werden und nicht von Attributte. Also eigentilch nichts anderes als: If option.id = text.option.id... Dazu hätte ich aber über drei Tabellen gehen müssen:
Texttabelle --> Fremdschlüsseltabelle --> Optionentabelle. In der Fremdschlüsseltabelle ist eine Instanz von Option...aber wie komme ich von da zur id?

m = Display_Text.objects.get(pk=pk)
n = m.dtao_set.all() ---> diesen Queryset jetzt vergleichen mit zugehörigen attribut, wenn vorhanden Option dazu als Standard-Value eintragen....

Ich hoffe du verstehst etwas von meinem Gequasel. Ich kann es leider nicht besser erklären, ausser ich stelle das gesamte Projekt hier rein.
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@rbaert: Also für meinen Geschmack ist die Funktion zu lang. In den beiden Zweigen werden andere Daten verarbeitet, aber der Code am Ende ist identisch. Den braucht man deshalb nicht zweimal hinschreiben.

Irgendwie scheint mir die Funktion auch zwei verschiedene Dinge zu tun. Ich würde wahrscheinlich POST und GET in unterschiedliche Funktionen packen.

Das hier ist ein Fehler: ``if request.POST.get(werte.name, '') != None``. Diese Bedingung ist *immer* wahr, denn wenn Du '' als Defaultwert angibst, dann kann das ja niemals `None` sein. Die ``for werte in attribute_list:``-Schleife kommt auch zweimal im Code vor. Die sollte in eine eigene Funktion oder vielleicht als Methode auf `Display_Text`.

Insgesamt wäre zu überlegen was von dem Code tatsächlich in der Funktion stehen muss und was nicht besser in sinnvoll benannte Methoden auf die Objekte gehört, damit man die Funktion auch verstehen kann.

Bei den Namen fallen die kryptischen einbuchstabigen Namen auf, sowie kryptische Abkürzungen, und das sich nicht alle an die Konventionen halten. klein_mit_unterstrichen für alles ausser Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase (ohne Unterstriche!)).

Mindestens zwei Namen werden definiert, aber nirgends verwendet.

Aus meiner Erfahrung sind in Python keine SQL-Abfragen üblich. Seit dem ich SQLAlchemy kenne, habe ich mich nur noch ganz selten mit SQL in Zeichenketten abgegeben. Also nur wenn es gar nicht anders ging. Und bei Django muss man IMHO auch einen ziemlich guten Grund haben das ORM nicht zu verwenden.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
rbaert
User
Beiträge: 20
Registriert: Mittwoch 5. September 2018, 15:37

@__blackjack__
hmm... du hast Recht...wie immer.
Danke für deine/eure Rückmeldungen. Ich werde mir deine/eure Kritik zu Herzen nehmen und meine Funktionen nochmal überdenken und überarbeiten.

- Auf die Idee in der Modelklasse noch Methoden hinzuzufügen bin ich gar gekommen. Guter Einwand
- Die Konventionen machen sicher Sinn, da ich ja gerade bei einem meiner letzten Beiträge über diese gestolpert bin. Werde auch das anpassen.
- SQLAcademy kannte ich bisher nicht. Ich habe in meiner "Verarbeitungs-Datei" mit SQL Statements gearbeitet. Auch da werde ich mich hineinlesen. Es ist bei mir wohl, wie bei den meisten Leuten auch: Früher war alles besser und das was ich kenne mag ich. Ich hatte einige Probleme mit dem ORM. Ich habe da wahrscheinlich noch einige Grundsätze nicht ganz verstanden. Ich werde dem nochmal eine Chance geben, versprochen.
- Mit den Namen die definiert wurden, aber nicht verwendet werden, nehme ich an du meinst so Kunststücke wie z.B.:

Code: Alles auswählen

	n = m.dtao_set.create(attribut_option = inst)
Aus irgend einem Grund hatte ich wohl das Bedürfnis, dies 'n' zu nennen... :lol: ...das kommt noch vom Tutorial. Da nannten sie es auch 'n'. Natürlich machten sie dann etwas damit, im Gegensatz zu mir. Gut ist vermerkt, nicht nötig....Danke.

Ich wünsche noch einen schönen Abend...
Benutzeravatar
noisefloor
User
Beiträge: 3843
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,
Ich habe mir vorgenommen, das nächste Projekt mit den in Python üblichen connection und SQL-Abfragen zu realisieren. Ich muss zugeben, dass ich mit der Django-Datenbank-Sprache nicht wirklich warm geworden bin.
Wenn du das Django ORM nicht nutzt, dann brauchst du IMHO auch kein Django benutzen. _Der_ Vorteil von Django ist ja die hohe Integration der Komponenten und das admin-Interface, was OOTB läuft. Letzteres braucht aber das ORM. Wenn du das ORM nicht nutzt, kann du auch z.B. Flask nutzen.
Ich finde vieles davon zu kompliziert (liegt wahrscheinlich auch daran, dass SQL mir logischer erscheint, weil ich das jeden Tag brauche).
Wenn dir das Django ORM zu kompliziert ist, dann brauchst du dir SQLAlchemy IMHO gar nicht anschauen, weil das dann für dich sicher "komplizierter" ist. Aufgrund des größeren Leistungsumfang und der größeren Flexibilität von SQLAlchemy brauchst du da zum Einrichten es mehr Code. Bzw. weniger Code als beim Django ORM geht IMHO fast nicht.

Zur Augangsfrage: IMHO ist Apache + mod_wsgi kein gängiges Gespann mehr für Pyhton Webanwendungen, weil angestaubt und vergleichsweise kompliziert einzurichten. Gängig ist ein WSGI-Applikiationsserver (wie gunicorn) plus ein ggf. vorgeschalteter nginx Webserver als Reverse-Proxy. Dazu findest du auch bergeweise Doku im Web (und der Django Doku).
Das Gespann nutze ich auch zu Hause auf meinem Pi. Nicht, weil es nötig wäre, sondern habe es ebenfalls zum Lernen so eingerichtet.

Gruß, noisefloor
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

noisefloor hat geschrieben: Montag 24. September 2018, 12:12
Ich finde vieles davon zu kompliziert (liegt wahrscheinlich auch daran, dass SQL mir logischer erscheint, weil ich das jeden Tag brauche).
Wenn dir das Django ORM zu kompliziert ist, dann brauchst du dir SQLAlchemy IMHO gar nicht anschauen, weil das dann für dich sicher "komplizierter" ist. Aufgrund des größeren Leistungsumfang und der größeren Flexibilität von SQLAlchemy brauchst du da zum Einrichten es mehr Code. Bzw. weniger Code als beim Django ORM geht IMHO fast nicht.
Naja, SQLAlchemy ist nicht komplizierter als Django weil es genauso weit oder weiter weg von SQL ist, im Gegenteil.

Wenn man SQL kann, sollte SQLAlchemys SQL Expression Language eigentlich ganz nett sein. Wenn man nicht dynamisch Queries generiert und halbwegs weiß was man tut kann man natürlich auch gut ohne ORM auskommen.
Antworten